181 lines
4.6 KiB
C#
181 lines
4.6 KiB
C#
// EnemyBase.cs
|
|
|
|
using System.Collections;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
|
|
[RequireComponent(typeof(NavMeshAgent))]
|
|
[RequireComponent(typeof(HealthManager))]
|
|
public abstract class EnemyBase : MonoBehaviour , IFreezable
|
|
{
|
|
public EnemyConfig config;
|
|
[Header("Targeting")]
|
|
public Transform target; // assign player at runtime or via tag lookup
|
|
public string playerTag = "Player"; // optional: auto-find
|
|
|
|
protected NavMeshAgent agent;
|
|
protected HealthManager health;
|
|
protected float lastAttackTime = -999f;
|
|
protected bool hasAggro;
|
|
public bool isFrozen { get; set; }
|
|
Coroutine _freezeCo;
|
|
float _origSpeed, _origAccel, _origAngSpeed;
|
|
protected virtual void Awake()
|
|
{
|
|
agent = GetComponent<NavMeshAgent>();
|
|
health = GetComponent<HealthManager>();
|
|
}
|
|
|
|
protected virtual void Start()
|
|
{
|
|
if (!target && !string.IsNullOrEmpty(playerTag))
|
|
{
|
|
var go = GameObject.FindGameObjectWithTag(playerTag);
|
|
if (go) target = go.transform;
|
|
}
|
|
ApplyConfig();
|
|
_origSpeed = agent.speed;
|
|
_origAccel = agent.acceleration;
|
|
_origAngSpeed = agent.angularSpeed;
|
|
}
|
|
|
|
protected virtual void Update()
|
|
{
|
|
if (!target || health.IsDead()) { Idle(); return; }
|
|
|
|
float dist = Vector3.Distance(transform.position, target.position);
|
|
if (!hasAggro)
|
|
{
|
|
if (dist <= config.aggroRange) hasAggro = true;
|
|
else { Idle(); return; }
|
|
}
|
|
else // has aggro
|
|
{
|
|
if (dist >= config.loseAggroRange)
|
|
{
|
|
hasAggro = false;
|
|
Idle();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// move / attack
|
|
if (dist > config.attackRange)
|
|
{
|
|
Chase();
|
|
}
|
|
else
|
|
{
|
|
FaceTarget();
|
|
TryAttack();
|
|
}
|
|
}
|
|
|
|
protected virtual void Idle()
|
|
{
|
|
if (agent && agent.enabled)
|
|
{
|
|
agent.isStopped = true;
|
|
agent.ResetPath();
|
|
}
|
|
}
|
|
|
|
protected virtual void Chase()
|
|
{
|
|
if (!agent || !agent.enabled) return;
|
|
agent.isStopped = false;
|
|
agent.stoppingDistance = config.stoppingDistance;
|
|
agent.SetDestination(target.position);
|
|
}
|
|
|
|
protected void FaceTarget()
|
|
{
|
|
Vector3 to = target.position - transform.position;
|
|
to.y = 0f;
|
|
if (to.sqrMagnitude > 0.001f)
|
|
{
|
|
var look = Quaternion.LookRotation(to);
|
|
transform.rotation = Quaternion.RotateTowards(transform.rotation, look, config.angularSpeed * Time.deltaTime);
|
|
}
|
|
}
|
|
|
|
protected void ApplyConfig()
|
|
{
|
|
if (!agent || config == null) return;
|
|
agent.speed = config.moveSpeed;
|
|
agent.angularSpeed = config.angularSpeed;
|
|
agent.acceleration = config.accel;
|
|
agent.stoppingDistance = config.stoppingDistance;
|
|
}
|
|
|
|
// ====== Attacking (implemented in child classes) ======
|
|
protected abstract void TryAttack();
|
|
|
|
// ====== IDamageable (forward to HealthManager) ======
|
|
public void ApplyDamage(float amount) => health.TakeDamage(amount);
|
|
public bool IsDead() => health.IsDead();
|
|
|
|
// Handy debug
|
|
[ContextMenu("Debug/Take 10")]
|
|
void DebugHit() => ApplyDamage(10);
|
|
|
|
|
|
public void Freeze(float duration)
|
|
{
|
|
if (_freezeCo != null) StopCoroutine(_freezeCo);
|
|
_freezeCo = StartCoroutine(FreezeRoutine(duration));
|
|
}
|
|
|
|
IEnumerator FreezeRoutine(float duration)
|
|
{
|
|
// Enter freeze
|
|
if (!isFrozen) EnterFreeze();
|
|
|
|
// Wait for duration
|
|
float t = duration;
|
|
while (t > 0f)
|
|
{
|
|
t -= Time.deltaTime;
|
|
yield return null;
|
|
}
|
|
|
|
// Exit
|
|
ExitFreeze();
|
|
_freezeCo = null;
|
|
}
|
|
|
|
void EnterFreeze()
|
|
{
|
|
isFrozen = true;
|
|
|
|
// Stop movement immediately
|
|
if (agent)
|
|
{
|
|
agent.isStopped = true; // halts pathfinding updates
|
|
agent.ResetPath(); // clear current path
|
|
agent.velocity = Vector3.zero;
|
|
|
|
// Optional: zero out movement-related params to avoid jitter
|
|
agent.speed = 0f;
|
|
agent.acceleration = 0f;
|
|
agent.angularSpeed = 0f;
|
|
}
|
|
|
|
}
|
|
|
|
void ExitFreeze()
|
|
{
|
|
isFrozen = false;
|
|
|
|
if (agent)
|
|
{
|
|
// Restore movement settings
|
|
agent.speed = _origSpeed;
|
|
agent.acceleration = _origAccel;
|
|
agent.angularSpeed = _origAngSpeed;
|
|
agent.isStopped = false;
|
|
}
|
|
|
|
}
|
|
}
|