| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- 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<Vector3> patrolPoints = new List<Vector3>();
- 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);
- }
- }
- }
|