using UnityEngine; using UnityEngine.AI; [RequireComponent(typeof(Animator))] public class EnemyAnimationController : MonoBehaviour { [Header("State Names")] public string idleState = "monster_idle_1"; public string walkState = "monster_Walking"; public string runState = "monster_Run"; public string walkBackState = "monster_Walk_Back"; public string[] attackStates = { "monster_attack_1", "monster_attack_2", "monster_attack_3" }; public string[] hitStates = { "monster_hit_1", "monster_hit_2" }; public string angerState = "monster_anger"; public string deathState = "monster_Death"; [Header("Locomotion")] public int layerIndex = 0; public float walkSpeed = 0.15f; public float runSpeed = 2.0f; public float crossFade = 0.08f; [Header("Stability")] public float walkToIdleDown = 0.08f; public float runToWalkDown = 1.7f; public float speedSmoothTime = 0.1f; public float minActionLock = 0.15f; Animator anim; NavMeshAgent agent; CharacterController cc; Rigidbody rb; string currentState = ""; float smoothedSpeed, speedVel; bool inAction; float actionUnlockTime; string actionState; void Awake() { anim = GetComponent(); agent =transform.parent.GetComponent(); cc = transform.parent.GetComponent(); rb = transform.parent.GetComponent(); } void Update() { if (inAction) TryAutoUnlockFromAction(); UpdateLocomotion(); } void UpdateLocomotion() { if (inAction) return; float rawSpeed = GetHorizSpeed(); smoothedSpeed = Mathf.SmoothDamp(smoothedSpeed, rawSpeed, ref speedVel, speedSmoothTime); string target = string.IsNullOrEmpty(currentState) ? idleState : currentState; if (currentState == idleState || string.IsNullOrEmpty(currentState)) { if (smoothedSpeed > walkSpeed) target = walkState; else target = idleState; } else if (currentState == walkState) { if (smoothedSpeed > runSpeed) target = runState; else if (smoothedSpeed < walkToIdleDown) target = idleState; } else if (currentState == runState) { if (smoothedSpeed < runToWalkDown) target = walkState; } else { if (smoothedSpeed <= walkToIdleDown) target = idleState; } if (target != currentState && !anim.IsInTransition(layerIndex)) { anim.CrossFade(target, crossFade, layerIndex); currentState = target; } } float GetHorizSpeed() { if (agent) return agent.velocity.magnitude; if (cc) return cc.velocity.magnitude; if (rb) return new Vector3(rb.velocity.x, 0f, rb.velocity.z).magnitude; return 0f; } void BeginAction(string stateName) { if (!anim.IsInTransition(layerIndex)) { anim.CrossFade(stateName, crossFade, layerIndex); currentState = stateName; inAction = true; actionState = stateName; actionUnlockTime = Time.time + minActionLock; } } void TryAutoUnlockFromAction() { if (Time.time < actionUnlockTime) return; var info = anim.GetCurrentAnimatorStateInfo(layerIndex); if (!info.IsName(actionState)) { inAction = false; currentState = ""; return; } if (!info.loop && info.normalizedTime >= 0.99f && !anim.IsInTransition(layerIndex)) { inAction = false; currentState = ""; } } public void PlayAttack(int index = -1) { if (attackStates == null || attackStates.Length == 0) return; if (index < 0) index = Random.Range(0, attackStates.Length); index = Mathf.Clamp(index, 0, attackStates.Length - 1); BeginAction(attackStates[index]); } public void PlayHit() { if (hitStates == null || hitStates.Length == 0) return; BeginAction(hitStates[Random.Range(0, hitStates.Length)]); } public void PlayAnger() { if (!string.IsNullOrEmpty(angerState)) BeginAction(angerState); } public void PlayWalkBack(bool enable) { if (enable && !string.IsNullOrEmpty(walkBackState)) { BeginAction(walkBackState); } } public void PlayDeath() { if (!string.IsNullOrEmpty(deathState)) BeginAction(deathState); } public void OnActionFinished() { inAction = false; currentState = ""; } }