- Home /
Problem with AI script: is there any way to add delay to a function called from Update method?
Hello,
I have a problem with my simple AI script. From what I see the problem is generated by using Invoke method to create a delay inside function the is called from the Update method.
In the script I wanted the AI agent to "wander around": travel to random destination, then when the destination is reached wait for a couple of seconds, then choose new destination and repeat the process.
Unfortunately the waiting part is not working correctly. Everything works for the first time, but in next iteration the script switches _isWandering variable also executes SetWanderPoint multiple times. Agent is frantically changing destination, but after a while everything starts to work properly again (agent reached destinations) only to repeat in next iteration.
I am quite new to C#, but I am quite proficient in Python, so I wonder why does it happen? Does Update method creates something like multiple instances of Wander method, which then overlap? Is there any advised way to have a proper delay inside a functions that are called from Update method?
Heres the script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class YBotController : MonoBehaviour
{
private float _alertRadius = 4f;
private NavMeshAgent _agent;
[SerializeField]
private float _maxWanderDistance;
[SerializeField]
private bool _isWandering;
[SerializeField]
private Vector3 _currentWanderingPoint;
[SerializeField]
private float _minWorthWanderingDistance = 1.0f;
// Start is called before the first frame update
void Start()
{
_agent = GetComponent<NavMeshAgent>();
_isWandering = false;
}
// Update is called once per frame
void Update()
{
Wander();
}
private void Wander()
{
if (_isWandering)
{
_agent.SetDestination(_currentWanderingPoint);
float distanceToWanderingPoint = Vector3.Distance(this.transform.position, _currentWanderingPoint);
if(distanceToWanderingPoint < 1.0f)
{
// this is the part that generates the problem
Invoke("ConcludeWandering", 10.0f);
//_isWandering = false; // this alternative is working fine
}
} else
{
_currentWanderingPoint = SetWanderPoint(this.transform.position);
_isWandering = true;
}
}
private Vector3 SetWanderPoint(Vector3 position)
{
float randomValueX = Random.Range(-_maxWanderDistance, _maxWanderDistance);
float randomValueZ = Random.Range(-_maxWanderDistance, _maxWanderDistance);
Vector3 newWanderingPoint = new Vector3(position.x + randomValueX, position.y, position.z + randomValueZ);
float distanceToNewWanderingPoint = Vector3.Distance(position, newWanderingPoint);
// if agent would only travel for a short distance, lets recursively find a new WanderPoint
if (distanceToNewWanderingPoint < _minWorthWanderingDistance)
{
newWanderingPoint = SetWanderPoint(position);
}
return newWanderingPoint;
}
private void ConcludeWandering()
{
_isWandering = false;
}
I would suggest to use IEnumerator(Coroutine) instead of doing this in Update... in IEnumerator you also have: yield return new WaitForSeconds(timeToWait) and I think that is what you are looking for.
Answer by logicandchaos · Feb 10, 2021 at 02:26 PM
It's because you are calling Invoke() every update so you are creating many stacks to the call ConcludeWandering() this might be easier to code with a timer updated in update.