- Home /
How to make enemy chase for longer distances than it's initial "notice" range
I have an enemy, that begins chasing the player when they get within a certain distance (in this case 5) however if the player exceeds the 5 distance while being chased the enemy will stop chasing. I want to make it so that, when the player enters the 5 distance, the enemy begins chasing them, but the enemy doesn't let up until the player is, say, 20 away, far greater than its initial notice range, but I'm unsure how to accomplish this. (There's more code than this but I just picked out the relevant portion).
public class WastrelChase : MonoBehaviour
{
public Transform player;
private Animator anim;
private Rigidbody myRigidbody;
private Vector3 moveDirection;
public CapsuleCollider cc;
public BoxCollider pc;
public Slider healthbar;
public Collider Attack;
public GameObject hurt;
public GameObject lunge;
public GameObject growl;
public GameObject shriek;
void OnTriggerEnter(Collider attack)
{
if (attack.gameObject.tag == "PlayerAttack") {
healthbar.value -= 15;
anim.SetBool("IsInjured", true);
}
}
// Use this for initialization
void Start()
{
anim = GetComponent<Animator>();
myRigidbody = GetComponent<Rigidbody>();
cc.enabled = false;
pc.enabled = false;
transform.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY | RigidbodyConstraints.FreezeRotationZ;
}
// Update is called once per frame
void Update()
{
moveDirection = Vector3.zero;
if (Vector3.Distance(player.position, this.transform.position) < 5)
{
Vector3 direction = player.position - this.transform.position;
direction.y = 0;
this.transform.rotation = Quaternion.Slerp(this.transform.rotation,
Quaternion.LookRotation(direction), 0.1f);
anim.SetBool("IsIdle", false);
if (direction.magnitude > .8)
{
moveDirection = direction.normalized;
anim.SetBool("IsWalking", true);
anim.SetBool("IsStriking", false);
anim.SetBool("IsInjured", false);
}
else
{
anim.SetBool("IsStriking", true);
anim.SetBool("IsWalking", false);
}
}
else
{
anim.SetBool("IsIdle", true);
anim.SetBool("IsWalking", false);
anim.SetBool("IsStriking", false);
}
if (healthbar.value <= 0){
anim.SetBool("IsVulnerable", true);
}
else
{
anim.SetBool("IsVulnerable", false);
}
}
Answer by MennoB · Jul 14, 2017 at 05:26 PM
I would create an "isFollowing" bool; and then use it as below. I also took the liberty to create separate functions for the different behaviours in Update(), this is what I would do but your preference may differ.
public class WastrelChase : MonoBehaviour { public Transform player; private Animator anim; private Rigidbody myRigidbody; private Vector3 moveDirection; public CapsuleCollider cc; public BoxCollider pc; public Slider healthbar; public Collider Attack; public GameObject hurt; public GameObject lunge; public GameObject growl; public GameObject shriek; private float triggerDist = 5; private float maxDist = 20; private bool isFollowing = false; void OnTriggerEnter(Collider attack) { if (attack.gameObject.tag == "PlayerAttack") { healthbar.value -= 15; anim.SetBool("IsInjured", true); } } // Use this for initialization void Start() { anim = GetComponent(); myRigidbody = GetComponent(); cc.enabled = false; pc.enabled = false; transform.GetComponent().constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY | RigidbodyConstraints.FreezeRotationZ; } // Update is called once per frame void Update() { moveDirection = Vector3.zero; if (!isFollowing) { NotFollowing (); if (Vector3.Distance (player.position, this.transform.position) < triggerDist) { isFollowing = true; } } else { Following (); if (Vector3.Distance (player.position, this.transform.position) > maxDist) { isFollowing = false; } } CheckHealth (); } private void NotFollowing() { anim.SetBool("IsIdle", true); anim.SetBool("IsWalking", false); anim.SetBool("IsStriking", false); } private void Following() { Vector3 direction = player.position - this.transform.position; direction.y = 0; this.transform.rotation = Quaternion.Slerp(this.transform.rotation, Quaternion.LookRotation(direction), 0.1f); anim.SetBool("IsIdle", false); if (direction.magnitude > .8) { moveDirection = direction.normalized; anim.SetBool("IsWalking", true); anim.SetBool("IsStriking", false); anim.SetBool("IsInjured", false); } else { anim.SetBool("IsStriking", true); anim.SetBool("IsWalking", false); } } private void CheckHealth() { if (healthbar.value <= 0){ anim.SetBool("IsVulnerable", true); } else { anim.SetBool("IsVulnerable", false); } } }
Answer by Bieere · Jul 14, 2017 at 01:20 PM
So i would recommend looking into a finite state machine to achieve the simple behaviour that you're looking for, where the idle, chase, and seek are all separate behaviours.
With that you can trigger a chase state, which will directly follow the player for that minimum view threshold, and once out of that state, move to a Seek state, which would move towards a direction for X amount of time, until either that enemy has either noticed the player, and has reentered a chase state, or has lost the player, and will revert to its idle state.
Unity does have an example of this, but there loads of resources online.
The GamerToGameDeveloper S3 tutorial on youtube covers a system like Bieere described.