using UnityEngine; using System.Collections.Generic; public class EnemyAI : MonoBehaviour { public enum EnemyState { Patrol, Chase, Returning } public EnemyState currentState = EnemyState.Patrol; [Header("Movement Settings")] public float patrolSpeed = 3f; public float chaseSpeed = 6f; public float detectionRadius = 12f; public float maxChaseDistance = 30f; // The "Leash" length private Vector3 spawnPosition; public List patrolPoints = new List(); private int currentPointIndex = 0; private Transform player; private LayerMask terrainLayer; void Start() { player = GameObject.FindGameObjectWithTag("Player").transform; // Looking for the "Terrain" layer specifically as per your Spawner terrainLayer = LayerMask.GetMask("Terrain"); spawnPosition = transform.position; // Remember where it was born // Generate 3 patrol points around spawn for (int i = 0; i < 3; i++) { Vector3 randomPos = spawnPosition + new Vector3(Random.Range(-15f, 15f), 10f, Random.Range(-15f, 15f)); if (Physics.Raycast(randomPos, Vector3.down, out RaycastHit hit, 20f, terrainLayer)) { patrolPoints.Add(hit.point + Vector3.up * 1f); } } } void Update() { float distToPlayer = Vector3.Distance(transform.position, player.position); float distFromHome = Vector3.Distance(transform.position, spawnPosition); // State Logic if (currentState == EnemyState.Chase && distFromHome > maxChaseDistance) { currentState = EnemyState.Returning; } else if (distToPlayer < detectionRadius && currentState != EnemyState.Returning) { currentState = EnemyState.Chase; } else if (currentState == EnemyState.Returning && distFromHome < 2f) { currentState = EnemyState.Patrol; } ExecuteState(); } void ExecuteState() { switch (currentState) { case EnemyState.Patrol: Patrol(); break; case EnemyState.Chase: MoveTowards(player.position, chaseSpeed); break; case EnemyState.Returning: MoveTowards(spawnPosition, patrolSpeed); break; } } void Patrol() { if (patrolPoints.Count == 0) return; Vector3 target = patrolPoints[currentPointIndex]; MoveTowards(target, patrolSpeed); if (Vector3.Distance(transform.position, target) < 1.5f) currentPointIndex = (currentPointIndex + 1) % patrolPoints.Count; } void MoveTowards(Vector3 target, float speed) { Vector3 dir = (target - transform.position).normalized; dir.y = 0; // 1. Check for obstacles directly in front // We fire a ray from the "chest" of the enemy forward RaycastHit wallHit; if (Physics.Raycast(transform.position + Vector3.up * 1f, dir, out wallHit, 1.5f, terrainLayer)) { // If the hill is too steep, we try to "step up" or slide along it dir += wallHit.normal * 0.5f; } // 2. Apply movement transform.position += dir * speed * Time.deltaTime; // 3. Ground Hugging (Smooth height adjustment) // We use a longer ray and a SmoothDamp or Lerp to prevent snapping if (Physics.Raycast(transform.position + Vector3.up * 10f, Vector3.down, out RaycastHit groundHit, 20f, terrainLayer)) { float targetHeight = groundHit.point.y + 0.5f; // Smoothly transition the Y position so they don't "teleport" up hills float newY = Mathf.MoveTowards(transform.position.y, targetHeight, speed * Time.deltaTime); transform.position = new Vector3(transform.position.x, newY, transform.position.z); } // 4. Rotation (Make them face where they are going) if (dir != Vector3.zero) { Quaternion targetRotation = Quaternion.LookRotation(dir); transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f); } } }