在c#中到达数组的末尾或开头时如何环绕索引?

     2023-04-13     201

关键词:

【中文标题】在c#中到达数组的末尾或开头时如何环绕索引?【英文标题】:How to wrap around the index when reaching the end or beginning of an array in c#? 【发布时间】:2021-10-30 01:39:32 【问题描述】:

我目前正在为 FPS 编写武器脚本,我想用鼠标滚轮切换武器。我创建了一个包含武器的数组,每次我用鼠标滚轮向上滚动时,武器的索引都会增加一。我的问题是,当我使用最后一个武器时,我收到 IndexOutOfBounds 错误消息。如果它位于数组的末尾,我尝试将武器索引重置为 0,但由于某种原因不起作用。我也尝试过使用 while 循环而不是 if 语句来做到这一点,但效果不佳。代码如下:

public class WeaponManager : MonoBehaviour


    [SerializeField]
    private WeaponHandler[] weapons;

    private int current_weapon_index;

    void Start()
    
        current_weapon_index = 0;
        weapons[current_weapon_index].gameObject.SetActive(true);
    

    
    void Update()
    
        if (Input.GetKeyDown(KeyCode.Alpha1))
        
            TurnOnSelectedWeapon(0);
        

        if (Input.GetKeyDown(KeyCode.Alpha2))
        
            TurnOnSelectedWeapon(1);
        

        if (Input.GetKeyDown(KeyCode.Alpha3))
        
            TurnOnSelectedWeapon(2);
        

        if (Input.GetKeyDown(KeyCode.Alpha4))
        
            TurnOnSelectedWeapon(3);
        

        if (Input.GetKeyDown(KeyCode.Alpha5))
        
            TurnOnSelectedWeapon(4);
        

        if (Input.GetKeyDown(KeyCode.Alpha6))
        
            TurnOnSelectedWeapon(5);
        

        if(Input.mouseScrollDelta.y > 0)
        
            SwitchToNextWeapon();
        

        if (Input.mouseScrollDelta.y < 0)
        
            SwitchToPreviousWeapon();
        
    

    void TurnOnSelectedWeapon(int weaponIndex)
    
        weapons[current_weapon_index].gameObject.SetActive(false);

        weapons[weaponIndex].gameObject.SetActive(true);

        current_weapon_index = weaponIndex;
    

    void SwitchToNextWeapon()
    
        weapons[current_weapon_index].gameObject.SetActive(false);

        current_weapon_index++;
       
        weapons[current_weapon_index].gameObject.SetActive(true);

        if (current_weapon_index >= weapons.Length)
        
            current_weapon_index = 0;
        
    

    void SwitchToPreviousWeapon()
    
        weapons[current_weapon_index].gameObject.SetActive(false);

        current_weapon_index--;

        weapons[current_weapon_index].gameObject.SetActive(true);
    

【问题讨论】:

current_weapon_index = current_weapon_index % weapons.Length - 类似这样。 @GuruStron 可以很好地增加,但是当你减少到 0 以下时会发生什么? 【参考方案1】:
void SwitchToNextWeapon()

    weapons[current_weapon_index].gameObject.SetActive(false);
    var temp = current_weapon_index + 1;
    current_weapon_index = temp >= weapons.Count() ? 0 : temp;
    
    weapons[current_weapon_index].gameObject.SetActive(true);


void SwitchToPreviousWeapon()

    weapons[current_weapon_index].gameObject.SetActive(false);
    var temp = current_weapon_index - 1;
    current_weapon_index = temp < 0 ? weapons.Count() - 1 : temp;

    weapons[current_weapon_index].gameObject.SetActive(true);

在增加或减少当前武器索引之前添加一个检查。如果达到最大值,则恢复为 0,如果达到最小值 (0),则将索引设置为最大值。

【讨论】:

由于weapons 是一个数组,我会说最好使用Length 成功了!谢谢!你能解释一下为什么它不像我那样工作吗?我只是想知道,这样我就可以避免类似的错误。当然,您不必这样做。 @L1NTHALO 很高兴它有帮助!在您的代码中,您试图在检查其有效性之前使用新的增加和减少的索引来访问该项目。【参考方案2】:

实现一个为您处理此问题的类将非常简单,只需维护一个内部的List&lt;&gt; 项目。添加 Current 属性以读取当前选定的项目,以及从鼠标滚轮处理程序调用的 MoveNext/MovePrevious 方法

public class ContinuousList<T>

    private List<T> internalList = new List<T>();
    private int currentIndex = 0;

    public void Add(T item) => internalList.Add(item);

    public T Current  get => internalList[currentIndex]; 

    public void MoveNext()
    
       currentIndex++;
       if(currentIndex >= internalList.Count) currentIndex = 0;
    

    public void MovePrevious()
    
       currentIndex--;
       if(currentIndex <= 0) currentIndex = internalList.Count - 1;
    

假设你可能有一些具有基类Weapon的武器:

var weaponList = new ContinuousList<Weapon>();
weaponList.Add(new Sword()); 
weaponList.Add(new Axe());
var currentWeapon = weaponList.Current; // gets Sword
weaponList.MoveNext();
var currentWeapon = weaponList.Current; // gets Axe
weaponList.MoveNext();
var currentWeapon = weaponList.Current; // Back to Sword

现场示例:https://dotnetfiddle.net/Ji7rkt

请注意,在此 ContinuousList 上实现 IEnumerable&lt;T&gt; 非常容易,因此它可以用于任何枚举和 LINQ 方法。我不想让一个简单的例子复杂化,而是在这里查看实际操作:https://dotnetfiddle.net/NtdfDi

【讨论】:

作为一个小改进,您可以考虑实现IEnumerable&lt;T&gt;IEnumerator&lt;T&gt;,这使得类可以与foreach 构造和其他一些地方一起使用,就像普通列表一样。 @Alejandro 确实如此。我推迟了这样做,以免代码复杂化。【参考方案3】:

使用模 (%) 运算符可以轻松处理循环值。

int mod = 5;

for (int i = 0; i < 10; i++)

    Console.WriteLine(i % mod);

您会看到输出循环从 0 到 mod-1:0,1,2,3,4,0,1,2,...

这涵盖了加一的情况:

int index = 4;
int mod = myArray.Length; // assume 5 items in the array

// Increment and cycle
index = ++index % mod;

您会看到索引现在是 0,因为您位于列表的末尾,因此下一项应该位于列表的开头。


但是,递减周期性值存在一些问题。由于我不明白的原因,C# 选择允许负模值,即:

-1 % 5 = -1

... 而不是 4,这是您所期望的。编辑:在 cmets 中争辩说 4 不是每个人都期望的。根据我第一次解决这个问题时的经验,我在网上发现了很多关于负模结果存在的困惑/烦恼,但我不能反驳这是我的观察偏差。

我过去已经解决过这个问题,解决这个问题的最简单方法是:

取模 添加模数 再次取模

本质上,如果第一步的结果为负数(例如-1),我们只需将模数相加,从而将值推至零以上。但是,如果第一步已经是积极的结果,那么我们现在将值设置得太高了。因此,通过再次取模,我们能够抵消可能过高的值。这涵盖了这两种情况。

Here is a dotnetfiddle to prove that it works.

换句话说:

public int Increment(int current, int mod)

    return ((++current % mod) + mod) % mod;


public int Decrement(int current, int mod)

    return ((--current % mod) + mod) % mod;

为了 DRY,你可以重新塑造它,这样你就只使用这个复杂的公式一次

public int Cycle(int current, int mod)

    return ((current % mod) + mod) % mod;

...但是您必须先手动输入/减少该值。您喜欢哪个版本取决于您。

【讨论】:

“这是您所期望的” - 这可能是 所期望的,但它已经按照我所期望的方式运行;期望可能有点主观。 @MarcGravell:我希望大多数人期望% x 输出介于0x-1 之间的整数值,而不是介于-x+1x-1 之间的整数值。我的期望是基于我第一次解决这个负模数问题时在网上发现的大量困惑和烦恼。但是,也许如果您从负面结果有意义的特定用例开始,它可能会有所不同并且更直观。我无法证明这一点,因为我是从将它们用于简单的周期性值开始的。 这基本上不是我也做过的here? @derHugo:就负模量的具体计算方式而言,是的,公式相同。但答案不仅仅是它的代码 sn-p,我想就底层数学提供更深入的解释。【参考方案4】:

This answer 用于 swift 但基本上以相同的方式应用于c#

因此,一般来说,您可以通过两次取模将任何给定索引包装到数组长度。

这适用于更一般的情况,不仅适用于上下移动一个步骤:

public static class ArrayUtils

    public static void Forward<T>(ref int currentIndex, T[] array, int amount)
    
        currentIndex = WrapIndex(currentIndex + amount, array);
    
    
    public static void Backward<T>(ref int currentIndex, T[] array, int amount)
    
        currentIndex = WrapIndex(currentIndex - amount, array);
    
    
    public static int WrapIndex<T>(int newIndex, T[] array)
    
        var length = array.Length;
        return ((newIndex % length) + length) % length;
    

见Fiddle

您现在可以将其用于任何数组。

【讨论】:

Python/Numpy - 在数组末尾环绕切片

】Python/Numpy-在数组末尾环绕切片【英文标题】:Python/Numpy-WrapSliceAroundEndofArray【发布时间】:2011-08-0617:36:26【问题描述】:我有两个一维数组,一个具有一些感兴趣的值(a),另一个提供该数组的索引(b)。我知道b中的值总是增加... 查看详情

到达数组末尾所需的最少跳转 - 获取索引位置

】到达数组末尾所需的最少跳转-获取索引位置【英文标题】:minimumofjumpsrequiredtoreachendofarray-getindexpositions【发布时间】:2014-02-2100:01:41【问题描述】:问题是获取minimumjumps和数组中的相应索引,这些索引会导致array的结尾以较少... 查看详情

如何在 Access 中快速滚动并到达组合框的末尾

】如何在Access中快速滚动并到达组合框的末尾【英文标题】:HowcanIscrollandreachtoendofComboboxfastinAccess【发布时间】:2016-09-0717:06:09【问题描述】:如何像第二次一样快速滚动并到达组合框的末尾?打开表单后第一次滚动7000条记录... 查看详情

如何在字符串中间添加符号字符但不在C#中字符串的开头或结尾

...2015-08-2615:09:26【问题描述】:我有一个动态大小的字符串数组。例如:string[]UserName_arr=newstring[usercount+1];/ 查看详情

如何在 Vim 中的单个命令调用中从光标位置开始并环绕文件末尾进行全局搜索和替换?

...如何在Vim中的单个命令调用中从光标位置开始并环绕文件末尾进行全局搜索和替换?【英文标题】:Howtosearchandreplaceglobally,startingfromthecursorpositionandwrappingaroundtheendoffile,inasinglecommandinvocationinVim?【发布时间】:2011-11-2717:28:06【问... 查看详情

如何添加到数组 C# 的末尾?

】如何添加到数组C#的末尾?【英文标题】:HowtoaddtoendofarrayC#?【发布时间】:2010-12-2215:48:19【问题描述】:如何在引用类的ArrayList末尾处的Windows窗体上的文本框和按钮中添加新的item?privateproduct[]value=newproduct[4];value[1]=newproduct("... 查看详情

“BOM”字符在文件开头或文件末尾的位置是啥? [复制]

】“BOM”字符在文件开头或文件末尾的位置是啥?[复制]【英文标题】:Whatisthelocationof\'BOM\'characteratbeginningoffileoratendoffile?[duplicate]“BOM”字符在文件开头或文件末尾的位置是什么?[复制]【发布时间】:2014-07-2502:55:33【问题描述... 查看详情

使用 jquery 的 next() 函数获取列表中的下一个元素时,如何在到达列表末尾后获取第一个元素?

...使用jquery的next()函数获取列表中的下一个元素时,如何在到达列表末尾后获取第一个元素?【英文标题】:Whenusingjquery\'snext()functiontogetthenextelementinalist,howdoIgetthefirstelementafterreachingtheendofthelist?【发布时间】:2012-07-1001:09:50【问... 查看详情

如何在 O(n) 时间内找到到达数组末尾的最小跳转次数

】如何在O(n)时间内找到到达数组末尾的最小跳转次数【英文标题】:HowtofindminimumnumberofjumpstoreachtheendofthearrayinO(n)time【发布时间】:2015-03-0715:27:03【问题描述】:问题给定一个整数数组,其中每个元素表示可以从该元素向前执行... 查看详情

在 C# 中存储和索引常量

...中发现了C#。我们需要的第一件事就是将常量存储在一堆数组中,这样任何人都可以轻松访问这些值,因此我们可以在定义新角色或武器类型时快速添加或修改值。现在在C++中,我学会了这样做:在一些constants.h文件中 查看详情

Python列表/数组:禁用切片中的负索引环绕

】Python列表/数组:禁用切片中的负索引环绕【英文标题】:Pythonlists/arrays:disablenegativeindexingwrap-aroundinslices【发布时间】:2012-11-0419:49:04【问题描述】:虽然我发现负数环绕(即A[-2]索引倒数第二个元素)在许多情况下非常有用... 查看详情

如何转换和复制数组(或数组的维度),以便每个元素每次都位于不同的索引中

】如何转换和复制数组(或数组的维度),以便每个元素每次都位于不同的索引中【英文标题】:Howtotransform&duplicateanarray(ordimensionofarray)sothateachelementisinadifferentindexeachtime【发布时间】:2021-04-1807:44:08【问题描述】:(在C#中... 查看详情

当精灵到达路径的末尾时,如何从 SpriteKit 重复动作中获取回调?

】当精灵到达路径的末尾时,如何从SpriteKit重复动作中获取回调?【英文标题】:HowdoIgetacallbackfromaSpriteKitrepeatingactionwhenthespritereachestheendofapath?【发布时间】:2013-10-2922:45:55【问题描述】:我以这种方式创建了我的SKAction:unicorn... 查看详情

Java ArrayList如何在开头添加元素

...ayList队列中,但是当我调用函数添加元素时,我希望它在数组的开头添加元素(因此它具有最低索引),如果该数组有10个元素,添加一个新元素会删除最旧的元素(索引最高的元素)。有人有什么建议吗?【问题讨论】: 查看详情

如何在 SwiftUI 中查找滚动视图内容是不是已到达内容的末尾

】如何在SwiftUI中查找滚动视图内容是不是已到达内容的末尾【英文标题】:HowtofindifscrollviewcontenthasreacheditsendofthecontentinSwiftUI如何在SwiftUI中查找滚动视图内容是否已到达内容的末尾【发布时间】:2020-03-0516:43:04【问题描述】:... 查看详情

c#数组之操作方法

向大家介绍C#数组操作,可能好多人还不了解C#数组操作,没有关系,看完本文你肯定有不少收获,希望本文能教会你更多东西。   数组是相同类型的对象的集合。由于数组几乎可以为任意长度,因此可以使用数组存储数... 查看详情

将类添加到列表 C# 时,索引超出了数组的范围

】将类添加到列表C#时,索引超出了数组的范围【英文标题】:IndexwasoutsidetheboundsofthearraywhenaddingclasstolistC#【发布时间】:2015-04-0310:35:37【问题描述】:异常:索引超出了数组的范围。首先,我对这个异常很熟悉,并且我之前已... 查看详情

我是不是在查找数组中最小值的索引时引用或实例错误?

】我是不是在查找数组中最小值的索引时引用或实例错误?【英文标题】:AmIreferencingorinstancingwrongforfindingtheindexofthesmallestvalueinanarray?我是否在查找数组中最小值的索引时引用或实例错误?【发布时间】:2021-07-1200:12:01【问题描... 查看详情