I wanted to add a hunter / prey feature to my project. As a jumping off point I modified the AIController script to create another script, as follows below, to simulate random wandering of game animals. I wanted to also include a method that causes the agent to “flee” from an enemy agent.
Pertinent Information -
*Using Unity 2022.2. Same version throughout.
*Have imported assets that are either open licensed or my own crafting. Insofar
no conflicts have been observed.
*Asset load is a little bloated (culling pending) so editor is starting to bog down a bit.
I haven’t noticed any performance hits from my scene once it is fully compiled and
running but Unity callbacks in the editor and Microsoft VS are going all molasses on
me .
*All involved agents have colliders, are tagged correctly, and have Health scripts. All
prefabs function as intended if assigned the scripts exactly as demonstrated in the
course.
Expected Result -
- Agent picks a random point as a destination and travels to that point.
- Agent will nullify its current path and re-path (Flee) if an enemy is within the alert
range. - Agent returns to its random wander behavior when no enemy is detected.
Actual Result -
- Agent picks a random point as a destination and travels to that point.
- Agent will nullify its current path and re-path only if the enemy is tagged
“Player”. Any other tags (including Unity’s other default tags) will return null. - Agent returns to its random wander cycle when no enemy is detected.
Several names of functions and variables have been changed to fit with my other, off the course scripts. It should read clearly enough though.
namespace UnitSystem
{
public class WanderingGrazerAI : MonoBehaviour
{
NavMeshAgent agent;
Health health;
Animator animator;
GameObject player;
GameObject predator;
[SerializeField] float walkSpeed = 1.3f;
[SerializeField] float runSpeed = 7;
[SerializeField] float wanderRange;
[SerializeField] float alertRange = 10;
/* The next field sets a position that the agent will return to before moving
again in a different, “random” direction.
Instead of a specified return position you can set this transform to the
transform of the agent to keep the agent moving without boudaries */
[SerializeField] Transform anchor;
[SerializeField] float grazeTime = 3.26f;void Awake() { agent = GetComponent<NavMeshAgent>(); animator = GetComponent<Animator>(); player = GameObject.FindWithTag("Player"); predator = GameObject.FindWithTag("Enemy"); } void Start() { GameObject[] enemyArray = GameObject.FindGameObjectsWithTag("Enemy"); Debug.Log("Length: " + enemyArray.Length); for (int e = 0; e < enemyArray.Length; e++) { Debug.Log(enemyArray[e].name, enemyArray[e]); } health = GetComponent<Health>(); } void FixedUpdate() { agent.enabled = !health.IsDead(); if (agent.remainingDistance <= agent.stoppingDistance) { Vector3 point; if (RandomPoint(anchor.position, wanderRange, out point)) { agent.SetDestination(point); } } if (InDanger(player)) { Flee(player); } if (InDanger(predator)) { Flee(predator); } AnimateMovement(); } bool RandomPoint(Vector3 center, float range, out Vector3 result) { Vector3 randomPoint = center + Random.insideUnitSphere * range; NavMeshHit hit; if (NavMesh.SamplePosition(randomPoint, out hit, 1.0f, NavMesh.AllAreas)) { result = hit.position; return true; } result = Vector3.zero; return false; } void AnimateMovement() { Vector3 localVelocity = transform.InverseTransformDirection(agent.velocity); float velocity = localVelocity.z; animator.SetFloat("Velocity", velocity); } void Flee(GameObject enemy) { Vector3 newRandomPoint = 15f * ((transform.position + Random.insideUnitSphere) * alertRange); if (InDanger(enemy)) { Vector3 approachedFromDirection = (transform.position - player.transform.position).normalized; agent.speed = runSpeed; Vector3 runPosition = approachedFromDirection * -25; agent.SetDestination(runPosition); animator.SetTrigger("Flee"); } else if (!InDanger(enemy)) { agent.speed = walkSpeed; agent.SetDestination(newRandomPoint); animator.ResetTrigger("Flee"); } } bool InDanger(GameObject enemy) { float distance = Vector3.Distance(enemy.transform.position, transform.position); return distance < alertRange; } void OnDrawGizmos() { Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(transform.position, alertRange); } }
}
My heart tells me I am shooting somewhere in the same room as the solution. I think I have just been square pegging it. If a more competent person has the time, I would welcome their insight.
Thanks in advance.
EDIT The debug log in the Start method does indeed accurately return an array of enemy units.