- Home /
How to make enemy check if it doesn`t see player for certain times it changes state
Hello guys. So I'm making an finite state machine for my enemy behaviors. I'm detecting if player is in sight of enemy by raycasting using this block of code:
public bool TargetInSight()
{
if (TargetInRange())
{
Vector3 dir = player.transform.position - transform.position;
float angle = Vector3.Angle(dir, transform.forward);
RaycastHit hit;
if (angle < enemyFov / 2)
{
for (int i = 0; i < angle; i++)
{
if (Physics.Raycast(new Vector3(transform.position.x, transform.position.y + 1, transform.position.z),
transform.forward + new Vector3(i - angle / 2, 0, 0), out hit, enemyViewDistance, NotEnemy))
{
if (hit.collider.tag == "Player")
{
Debug.Log("true");
return true;
}
}
}
}
}
return false;
}
TargetInRange is just a simple Vector3.Distance.
when I detect the player I change the state to attack, so the problem is in attack state I can not use the same function bc it returns false some frames and it causes the enemy to switch to the next state for a short period of time
its like attack, attack, then move ahead, attack, move move attack etc. What I wanna do is to check if player is not detected for some time like 2f, only then change the state. But I can not do it. I tried checking with Time.time but couldn't get it to work.
any help is appreciated. Thanks
Answer by highpockets · Feb 13, 2021 at 01:15 PM
You could make a coroutine.
IEnumertor WaitForValidAttack()
{
float timer = 0.0f
waitingForValidAttack = true;
while( timer < 2.0f )
{
timer += Time.deltaTime;
yield return null;
}
if( TargetInSight() )
{
//Attack
}
waitingForValidAttack = false;
}
private waitingForValidAttack = false;
public bool TargetInSight()
{
if (TargetInRange())
{
Vector3 dir = player.transform.position - transform.position;
float angle = Vector3.Angle(dir, transform.forward);
RaycastHit hit;
if (angle < enemyFov / 2)
{
for (int i = 0; i < angle; i++)
{
if (Physics.Raycast(new Vector3(transform.position.x, transform.position.y + 1, transform.position.z),
transform.forward + new Vector3(i - angle / 2, 0, 0), out hit, enemyViewDistance, NotEnemy))
{
if (hit.collider.tag == "Player")
{
Debug.Log("true");
if(!waitingForValidAttack)
StartCoroutine( WaitForValidAttack() );
return true;
}
}
}
}
}
return false;
}
So while waitingForValidAttack is true, you don’t need to call TargetInSight from anywhere else until the coroutine is finished and waitingForValidAttack gets set back to false.
BTW you could be even more accurate and test of the target stays in sight every frame in the while loop and maybe it becomes invalid if the target leaves because right now the code just double checks after 2 seconds have passed. Come to think of it, there is no need for the while loop if you want to just yield return new WaitForSeconds(2.0f);
, but the while loop is handy if you want to run some logic each frame specific to the functionality
Your answer
Follow this Question
Related Questions
How to make a Raycast enemy? 0 Answers
enemy raycast to detect player 1 Answer
2d raycast enemy detection problem. 0 Answers
spherecast wont work 2 Answers
2D Enemy AI range problem 2 Answers