using UnityEngine;
namespace IndianOceanAssets.Engine2_5D
{
///
/// 敌人AI —— 检测/追击/脱离持续/摇铃聆听响应。
///
/// 状态流程:
/// - Idle:站岗。主角进入 detectionRange → Chasing
/// - Chasing:追击主角。主角离开 detectionRange → ChasingPersist
/// - ChasingPersist:脱离后继续追击 chasePersistTime 秒。
/// 主角重新进入范围 → 回到 Chasing;超时 → Idle(停在当前位置,不回出生点)
/// - BellResponding:主角在 listenRange 内按E摇铃时触发,
/// 朝主角方向移动 bellMoveTime 秒(距离 = 速度 × 时间),然后停下 → Idle
///
/// 挂载位置:敌人预制体上
/// 需要:场景中有 EchoSystem(用于摇铃事件)、玩家物体带 "Player" 标签
/// (或手动把玩家拖到 playerTarget 字段)
///
public class EnemyAI : MonoBehaviour
{
[Header("目标")]
[Tooltip("玩家目标(留空则自动查找 Player 标签)")]
[SerializeField] private Transform playerTarget;
[Header("检测与追击")]
[Tooltip("发现主角的范围")]
[SerializeField] private float detectionRange = 8f;
[Tooltip("追击速度")]
[SerializeField] private float chaseSpeed = 3f;
[Tooltip("靠近主角到此距离时停下")]
[SerializeField] private float stopDistance = 1.2f;
[Tooltip("主角脱离检测范围后,继续追击的时间(秒)")]
[SerializeField] private float chasePersistTime = 3f;
[Header("聆听(摇铃响应)")]
[Tooltip("聆听范围:主角在此范围内按E摇铃时,敌人会朝主角方向移动")]
[SerializeField] private float listenRange = 15f;
[Tooltip("摇铃响应移动速度")]
[SerializeField] private float bellMoveSpeed = 4f;
[Tooltip("摇铃响应移动时间(距离 = 速度 × 时间)")]
[SerializeField] private float bellMoveTime = 1.5f;
private enum State { Idle, Chasing, ChasingPersist, BellResponding }
private State _state = State.Idle;
private float _stateTimer;
private Vector3 _bellDirection;
private void Start()
{
if (playerTarget == null)
{
var player = GameObject.FindWithTag("Player");
if (player != null)
playerTarget = player.transform;
}
}
private void OnEnable()
{
EchoSystem.OnEchoReleased += OnBell;
}
private void OnDisable()
{
EchoSystem.OnEchoReleased -= OnBell;
}
private void Update()
{
switch (_state)
{
case State.Idle: UpdateIdle(); break;
case State.Chasing: UpdateChasing(); break;
case State.ChasingPersist: UpdateChasingPersist(); break;
case State.BellResponding: UpdateBellResponding(); break;
}
}
// ===== Idle:站岗,等主角进入检测范围 =====
private void UpdateIdle()
{
if (playerTarget == null) return;
if (XZDistance(transform.position, playerTarget.position) <= detectionRange)
{
_state = State.Chasing;
}
}
// ===== Chasing:追击主角 =====
private void UpdateChasing()
{
if (playerTarget == null) return;
float dist = XZDistance(transform.position, playerTarget.position);
// 主角脱离检测范围 → 继续追击一段时间
if (dist > detectionRange)
{
_state = State.ChasingPersist;
_stateTimer = chasePersistTime;
return;
}
// 靠近到停止距离时停下(但仍处于追击状态)
if (dist > stopDistance)
MoveToward(playerTarget.position, chaseSpeed);
}
// ===== ChasingPersist:脱离后继续追击,倒计时 =====
private void UpdateChasingPersist()
{
if (playerTarget == null) return;
_stateTimer -= Time.deltaTime;
// 超时 → 停在当前位置,回到 Idle(不回出生点)
if (_stateTimer <= 0f)
{
_state = State.Idle;
return;
}
float dist = XZDistance(transform.position, playerTarget.position);
// 主角重新进入检测范围 → 回到正常追击
if (dist <= detectionRange)
{
_state = State.Chasing;
return;
}
if (dist > stopDistance)
MoveToward(playerTarget.position, chaseSpeed);
}
// ===== BellResponding:摇铃响应,朝主角方向移动固定时间 =====
private void UpdateBellResponding()
{
_stateTimer -= Time.deltaTime;
if (_stateTimer <= 0f)
{
_state = State.Idle;
return;
}
// 沿摇铃时刻的方向移动(距离 = 速度 × 时间)
Vector3 move = _bellDirection * bellMoveSpeed * Time.deltaTime;
transform.position += new Vector3(move.x, 0f, move.z);
}
// ===== 摇铃事件回调(由 EchoSystem.OnEchoReleased 触发)=====
private void OnBell(Vector3 bellPos)
{
if (playerTarget == null) return;
// 只有在聆听范围内才响应
if (XZDistance(transform.position, bellPos) > listenRange) return;
// 计算朝主角方向(摇铃时刻的位置)
_bellDirection = new Vector3(
bellPos.x - transform.position.x,
0f,
bellPos.z - transform.position.z
);
if (_bellDirection.sqrMagnitude > 0.001f)
_bellDirection.Normalize();
else
_bellDirection = transform.forward; // 重叠时用当前朝向
// 摇铃响应可打断任何当前状态
_state = State.BellResponding;
_stateTimer = bellMoveTime;
}
// ===== 工具方法 =====
private void MoveToward(Vector3 target, float speed)
{
Vector3 dir = new Vector3(
target.x - transform.position.x,
0f,
target.z - transform.position.z
);
float dist = dir.magnitude;
if (dist < 0.01f) return;
dir /= dist;
Vector3 move = dir * speed * Time.deltaTime;
if (move.magnitude > dist) move = dir * dist; // 不越过目标
transform.position += new Vector3(move.x, 0f, move.z);
}
private float XZDistance(Vector3 a, Vector3 b)
{
float dx = a.x - b.x;
float dz = a.z - b.z;
return Mathf.Sqrt(dx * dx + dz * dz);
}
// ===== 编辑器可视化 =====
private void OnDrawGizmosSelected()
{
// 检测范围(黄色)
Gizmos.color = new Color(1f, 0.85f, 0f, 0.6f);
Gizmos.DrawWireSphere(transform.position, detectionRange);
// 聆听范围(青色)
Gizmos.color = new Color(0f, 0.85f, 1f, 0.6f);
Gizmos.DrawWireSphere(transform.position, listenRange);
// 停止距离(红色)
Gizmos.color = new Color(1f, 0.3f, 0.3f, 0.6f);
Gizmos.DrawWireSphere(transform.position, stopDistance);
}
/// 当前状态(调试用)
public string CurrentState => _state.ToString();
}
}