How to get a for loop to run once per coroutine?
I am trying to make an AI (my first one without tutorials) and I am having a bug that completely ruins the AI. I have an array of points that I want to cycle through but instead of it adding one then going to the next point it goes straight from 0 for a while to 1,2,3 really fast, confusing the AI. It seems like it is running the complete for loop every time it passes through when I only want the for loop to do it once per coroutine Here is the script:
// Start is called before the first frame update
void Start()
{
noise = transform.GetChild(0).gameObject;
noiseAnim = GetComponent<Animator>();
chosenNo = 0;
chosen = patrol[chosenNo];
}
// Update is called once per frame
void Update()
{
StartCoroutine(Move());
Debug.Log(chosenNo.ToString());
}
IEnumerator Move()
{
if (noise.GetComponent<Soundwave>().sense == true)
{
lastKnownPoint = noise.GetComponent<Soundwave>().point;
transform.position = Vector2.MoveTowards(transform.position, lastKnownPoint.position, speed * Time.deltaTime);
yield return null;
} else if (noise.GetComponent<Soundwave>().sense == false)
{
if (transform.position == chosen.position)
{
for (; chosenNo < patrol.Length; chosenNo++)
{
noiseAnim.SetTrigger("Screech");
yield return new WaitForSeconds(waitTime);
chosen = patrol[chosenNo];
}
if (chosenNo >= patrol.Length || chosen == null)
{
chosenNo = 0;
}
}
if (transform.position != chosen.position)
{
transform.position = Vector2.MoveTowards(transform.position, chosen.position, speed * Time.deltaTime);
}
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Player")
{
noise.GetComponent<Soundwave>().sense = true;
noise.GetComponent<Soundwave>().player = collision.gameObject;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
StartCoroutine(Move());
}
Any help would be great thanks :)
Answer by streeetwalker · Sep 21, 2020 at 08:28 AM
It looks like the only way your issue could happen is if you change the value of waitTime, however corourtine execution is dependent on the frame rate - if your fps is varying due to the cpu load your game is pulling, WaitForSeconds will be innacurate.
What is your waitTime, and can you verify it is not changing?
Here is an alternate, sample coroutine loop in pseudoCode that might solve your problem:
waitTime = 1000f; // wait for one second in milliseconds
// do the loop
int itemIndex = 0;
float currentTime = 0;
while( itemIndex < itemList.Count ){
if( currentTime >= waitTime ) {
process itemList[ itemIndex ];
currentTime = 0;
itemIndex++;
}
yield return null; // or perhaps new WaitForFixedUpdate();
currentTime += Time.deltaTime;
}
I think this will allow you a finer grained response time that WaitForSeconds will if the problem is due to the frame rate dropping or speeding up. To that end, you might want to use WaitForFixedUpdate. While you are guaranteed a certain number of FixedUpdate frames, like all timing issues in Unity, the time between them is not.
I have just verified that it doesn't change by printing it to the console and it stays constant throughout the bug occurring
What is the waitTime? How big or small that is can make a difference when it comes to WaitForSeconds inherent inaccuracies.
So the next step is to verify how much time is actually elapsing between your yield statements. You'll probably need to set up a "startTime" variable to get the current time (Time.time) before your loop starts, and then inside the loop report the difference between the current time and your start time.
rather than WaitForSeconds, it might be better to set up a loop to wait only for one frame, accumulate deltaTime, then do your work if the total delta time exceeds your waitTime in milliseconds.
I don't think it has anything to do with the waitforseconds but I will try what u said if you can give me an example of code that can do that. I think it is instead with the fact that the for loop is running for the full length each time the coroutine is mentioned instead of once.