Part of these game systems:
beginner AI & Pathfinding

Enemy Chase AI

Enemy AI that detects the player within radius, chases with line-of-sight, and returns to patrol when player escapes.

Unity 2022.3+ · 2.5 KB · EnemyChaseAI.cs

How to Use

1

Attach EnemyChaseAI to your enemy

2

Tag your player as 'Player'

3

Set detection radius and field of view

4

Configure chase speed and give-up distance

5

Hook OnPlayerDetected and OnAttackRange events

6

Gizmos show detection/attack ranges in Scene view

7

Pair with WaypointPatrol for idle behavior

Source Code

EnemyChaseAI.cs
C#
using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// Enemy AI with detection radius, line-of-sight check,
/// and chase behavior. Returns to origin when player escapes.
/// </summary>
public class EnemyChaseAI : MonoBehaviour
{
    [Header("Detection")]
    [SerializeField] private float detectionRadius = 10f;
    [SerializeField] private float fieldOfView = 120f;
    [SerializeField] private bool requireLineOfSight = true;
    [SerializeField] private LayerMask obstacleLayers;

    [Header("Chase")]
    [SerializeField] private float chaseSpeed = 5f;
    [SerializeField] private float rotationSpeed = 8f;
    [SerializeField] private float giveUpDistance = 15f;
    [SerializeField] private float attackRange = 1.5f;

    [Header("Target")]
    [SerializeField] private string playerTag = "Player";

    [Header("Events")]
    public UnityEvent OnPlayerDetected;
    public UnityEvent OnPlayerLost;
    public UnityEvent OnAttackRange;

    private Transform player;
    private Vector3 originPosition;
    private bool isChasing;
    private bool isReturning;

    public bool IsChasing => isChasing;

    private void Start()
    {
        originPosition = transform.position;
        GameObject playerObj = GameObject.FindGameObjectWithTag(playerTag);
        if (playerObj != null)
            player = playerObj.transform;
    }

    private void Update()
    {
        if (player == null) return;

        float distToPlayer = Vector3.Distance(transform.position, player.position);

        if (isChasing)
        {
            if (distToPlayer > giveUpDistance)
            {
                StopChasing();
                return;
            }

            if (distToPlayer <= attackRange)
            {
                OnAttackRange?.Invoke();
                return;
            }

            ChasePlayer();
        }
        else if (isReturning)
        {
            ReturnToOrigin();
        }
        else
        {
            if (CanDetectPlayer(distToPlayer))
            {
                isChasing = true;
                OnPlayerDetected?.Invoke();
            }
        }
    }

    private bool CanDetectPlayer(float distance)
    {
        if (distance > detectionRadius) return false;

        // FOV check
        Vector3 dirToPlayer = (player.position - transform.position).normalized;
        float angle = Vector3.Angle(transform.forward, dirToPlayer);
        if (angle > fieldOfView * 0.5f) return false;

        // Line of sight check
        if (requireLineOfSight)
        {
            if (Physics.Raycast(transform.position + Vector3.up, dirToPlayer, distance, obstacleLayers))
                return false;
        }

        return true;
    }

    private void ChasePlayer()
    {
        Vector3 direction = (player.position - transform.position);
        direction.y = 0f;

        if (direction.magnitude > 0.1f)
        {
            Vector3 moveDir = direction.normalized;
            transform.position += moveDir * chaseSpeed * Time.deltaTime;

            Quaternion targetRot = Quaternion.LookRotation(moveDir);
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, rotationSpeed * Time.deltaTime);
        }
    }

    private void StopChasing()
    {
        isChasing = false;
        isReturning = true;
        OnPlayerLost?.Invoke();
    }

    private void ReturnToOrigin()
    {
        Vector3 direction = (originPosition - transform.position);
        direction.y = 0f;

        if (direction.magnitude <= 0.3f)
        {
            isReturning = false;
            return;
        }

        Vector3 moveDir = direction.normalized;
        transform.position += moveDir * (chaseSpeed * 0.6f) * Time.deltaTime;

        Quaternion targetRot = Quaternion.LookRotation(moveDir);
        transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, rotationSpeed * Time.deltaTime);
    }

#if UNITY_EDITOR
    private void OnDrawGizmosSelected()
    {
        Gizmos.color = new Color(1f, 0.3f, 0.3f, 0.3f);
        Gizmos.DrawWireSphere(transform.position, detectionRadius);

        Gizmos.color = new Color(1f, 0.6f, 0f, 0.3f);
        Gizmos.DrawWireSphere(transform.position, giveUpDistance);

        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, attackRange);

        // FOV lines
        Gizmos.color = Color.yellow;
        Vector3 fovLeft = Quaternion.Euler(0, -fieldOfView * 0.5f, 0) * transform.forward * detectionRadius;
        Vector3 fovRight = Quaternion.Euler(0, fieldOfView * 0.5f, 0) * transform.forward * detectionRadius;
        Gizmos.DrawLine(transform.position, transform.position + fovLeft);
        Gizmos.DrawLine(transform.position, transform.position + fovRight);
    }
#endif
}