C# — Цикл выполняет код сразу для всех элементов массива, вместо того, чтобы выполнять итерацию по одному за раз

#c# #loops #iteration

Вопрос:

Я делаю небольшую игру в бокс в Unity. Я разработал функцию (attackOverdrive), которая принимает множество аргументов, а затем заставляет вражеского боксера атаковать на основе данных, введенных в функцию. У меня есть еще один класс, который задает набор переменных, а затем я считываю эти значения, присваиваю их локальным переменным, а затем запускаю свою функцию. Дело в том, что код в моем цикле выполняется на всех членах массива вместо того, чтобы повторять их по одному за раз, в результате чего враг одновременно бросает много атак, которые должны были быть брошены в последовательности. Предполагается, что punchDelayTimer будет считать до определенного предела, затем отправит сообщение для создания экземпляра атаки, затем повторит цикл и сделает это снова.

Я схожу с ума, перепробовал все различные типы итераторов и ничего не добился. Я новичок в C#, понятия не имею, что я здесь сделал не так. Функция, которая задает переменные в другом скрипте, находится в нижней части блока кода.

 using UnityEngine;
using System.Text.RegularExpressions;

public class overdrivePiano : MonoBehaviour
{
    private GameObject enemyBoxer;
    private pianoRoll theRoll;
    private string theCoordMod;
    private string[] thePatterns;
    private int howMany;
    private float thePunchDelay, thePatternDelay, theCoordModAmount, punchDelayTimer, patternDelayTimer;
    private Vector2[] theCoords;

    void Start()
    {
        enemyBoxer= GameObject.Find("enemyBoxer");
        theRoll = gameObject.GetComponent<pianoRoll>();
        punchDelayTimer = 0;
    }

    private void readRoll()
    {
        thePatterns = theRoll.patterns;
        thePunchDelay = theRoll.punchDelay;
        thePatternDelay = theRoll.patternDelay;
        theCoords = theRoll.coords;
        theCoordMod = theRoll.coordMod;
        theCoordModAmount = theRoll.modAmount;
    }

    public void onSwitch()
    {
        theRoll.SendMessage("firstVerse");
        readRoll();
        attackOverdrive(thePatterns, thePunchDelay, thePatternDelay, theCoords);
    }

    public void attackOverdrive(string[] patterns, float punchDelay, float patternDelay, Vector2[] coords, string coordMod = "none", float modAmount = 0)
    {
        for(int i = 0; i < patterns.Length; i  )
        {
            if (patterns[i] == "triangle")
            {
                int j = 0;
                Vector2[] triangleVectors = new Vector2[] {new Vector2(coords[i].x, coords[i].y   0.75f), new Vector2(coords[i].x - 0.75f, coords[i].y - 0.75f), new Vector2(coords[i].x   0.75f, coords[i].y - 0.75f)};
                do
                {
                    if (punchDelayTimer <= punchDelay)
                    {
                        punchDelayTimer  = Time.deltaTime; //Debug statement here for i and j both print 0
                    }
                    else
                    {
                        enemyBoxer.SendMessage("createAttack", triangleVectors[j]);
                        punchDelayTimer = 0;  //Debug statement here outputs 9 statements for the value of i: 0,0,0,1,1,1,2,2,2
                        j  = 1; //Debug statement here outputs 9 statements for the value of j: 0,1,2,0,1,2,0,1,2
                    }
                } while (j < 3);
            }
            else if (patterns[i] == "square")
            {
                
            }
            else if (patterns[i] == "circle")
            {
                
            }
            else if ("verticalLine".CompareTo(patterns[i]) == -1)
            {
               
                var result = Regex.Match(patterns[i], @"d $", RegexOptions.RightToLeft);
                if (result.Success) 
                { 
                    //Debug.Log(result.Value);
                } 
            }
        }
    }
}


private void firstVerse()
    {
        patterns = new string[] {"triangle", "triangle", "triangle", "singleRandom", "singleRandom", "verticalLine5"};
        coords = new Vector2[] {new Vector2(-1.3f, 2.5f), new Vector2(0f, -2.5f), new Vector2(1.3f, 2.5f), new Vector2(0,0), new Vector2(0,0), new Vector2(0, 4f)};
        punchDelay = 0.5f;
        patternDelay = 0.5f;
    }
 

Комментарии:

1. Вы пробовали добавить некоторые записи в журнал? Какие значения вы видите для punchDelayTimer, punhcDelay и Time.deltaTime в каждом цикле?

2. Значение punchDelay равно 0,5, и таймер правильно отсчитывает до него. Как только он превысит 0,5, весь код в предложении else будет выполняться на всех членах массива, а не только на шаблонах[i].

3. Кроме того, любой оператор журнала, который я помещаю в предложение else, будет выполняться 9 раз (по 3 для каждого из 3 треугольников) — все с отметками времени одновременно.

4. Где в вашем цикле вы выполняете свою «задержку». Т. е. как вы ждете? Единственное, что я вижу, — это увеличение значения punchDelayTimer.

5. Предложение if — punchDelayTimer = Time.deltaTime; — увеличивает таймер с течением времени. Как только он достигнет предела, установленного punchDelay, он должен запустить предложение else, которое затем сбрасывает punchDelayTimer обратно на 0, чтобы он мог работать снова. Он делает все эти вещи, но 9 раз одновременно, а не по одному за раз.

Ответ №1:

Оказывается, проблема заключалась в таймере с плавающей точкой. Он правильно перебирал массив, но все это происходило в одном и том же кадре. Я смог решить проблему, изменив тип возврата attackOverdrive на IEnumerator, а затем вызвав его как сопрограмму из OnSwitch. Я удалил оператор if внутри треугольной части цикла и заменил таймер с плавающей точкой классом WaitForSeconds Unity. Вот функции, которые изменились, если кому — то интересно:

 public void onSwitch()
    {
        theRoll.SendMessage("firstVerse");
        readRoll();
        StartCoroutine(attackOverdrive(thePatterns, thePunchDelay, thePatternDelay, theCoords));
    }

    public IEnumerator attackOverdrive(string[] patterns, float punchDelay, float patternDelay, Vector2[] coords, string coordMod = "none", float modAmount = 0)
    {
        for(int i = 0; i < patterns.Length; i  )
        {
            if (patterns[i] == "triangle")
            {
                int j = 0;
                Vector2[] triangleVectors = new Vector2[] {new Vector2(coords[i].x, coords[i].y   0.5f), new Vector2(coords[i].x - 0.5f, coords[i].y - 0.5f), new Vector2(coords[i].x   0.5f, coords[i].y - 0.5f)};
                do
                {
                    enemyBoxer.SendMessage("createAttack", triangleVectors[j]);
                    yield return new WaitForSeconds(punchDelay);
                    punchDelayTimer = 0f;
                    j  = 1;
                } while (j < 3);
            }
            else if (patterns[i] == "square")
            {
                
            }
            else if (patterns[i] == "circle")
            {
                
            }
            else if ("verticalLine".CompareTo(patterns[i]) == -1)
            {
               
                var result = Regex.Match(patterns[i], @"d $", RegexOptions.RightToLeft);
                if (result.Success) 
                { 
                    Debug.Log(result.Value);
                } 
            }
            else if ("single".CompareTo(patterns[i]) == -1)
            {
               
                var result = Regex.Match(patterns[i], @"d $", RegexOptions.RightToLeft);
                if (result.Success) 
                { 
                    Debug.Log(result.Value);
                } 
            }
        }
    }
 

Спасибо rage_co на форумах Unity, который показал мне это исправление =]