using UnityEngine; using UnityEngine.AI; using Invector; using System.Collections; /// /// AnimatorAI: Đồng bộ hóa trạng thái của EnemyAI với Animator. /// Tích hợp Simulation Mode để giả lập animation khi chưa có logic di chuyển hoàn chỉnh. /// public class AnimatorAI : MonoBehaviour { protected Animator animator; protected EnemyAI enemyAI; protected NavMeshAgent agent; protected Rigidbody rb; [Header("Debug Settings")] public bool debugMode = true; public Color debugColor = Color.cyan; [Header("Simulation Mode (Giả lập)")] public bool useSimulation = false; // Tích chọn để dùng thông số giả lập bên dưới public bool autoCycleSpeed = false; // Tự động chạy/đi bộ/đứng im theo vòng lặp [Range(0, 1)] public float simVerticalVelocity = 0f; public bool simIsSprinting = false; public bool simIsAiming = false; public int simMoveSetID = 0; [Header("Movement Settings")] public float sprintThreshold = 0.8f; public float dampTime = 0.1f; #region Animator Parameters (Invector Style) protected vAnimatorParameter isDead; protected vAnimatorParameter isGrounded; protected vAnimatorParameter isCrouching; protected vAnimatorParameter isStrafing; protected vAnimatorParameter isSliding; protected vAnimatorParameter isSprinting; protected vAnimatorParameter isAiming; protected vAnimatorParameter canAim; protected vAnimatorParameter flipAnimation; protected vAnimatorParameter flipEquip; protected vAnimatorParameter groundDistance; protected vAnimatorParameter groundAngle; protected vAnimatorParameter verticalVelocity; protected vAnimatorParameter moveSet_ID; protected vAnimatorParameter upperBody_ID; protected vAnimatorParameter idleRandom; protected vAnimatorParameter idleRandomTrigger; protected vAnimatorParameter randomAttack; protected vAnimatorParameter weakAttack; protected vAnimatorParameter strongAttack; protected vAnimatorParameter isBlocking; protected vAnimatorParameter attackID; protected vAnimatorParameter defenseID; protected vAnimatorParameter recoilID; protected vAnimatorParameter reactionID; protected vAnimatorParameter triggerRecoil; protected vAnimatorParameter triggerReaction; protected vAnimatorParameter hitDirection; protected vAnimatorParameter resetState; protected vAnimatorParameter reload; protected vAnimatorParameter cancelReload; protected vAnimatorParameter reloadID; protected vAnimatorParameter shoot; protected vAnimatorParameter shot_ID; protected vAnimatorParameter powerCharger; #endregion protected virtual void Start() { animator = GetComponentInChildren(); enemyAI = GetComponent(); agent = GetComponent(); rb = GetComponent(); if (animator == null) { Debug.LogError($"[AnimatorAI] KHÔNG tìm thấy Animator trên {gameObject.name} hoặc các con của nó!"); return; } // CHẨN ĐOÁN SÂU: if (debugMode) StartCoroutine(DeepDiagnosticRoutine()); InitializeParameters(); } private IEnumerator DeepDiagnosticRoutine() { while (true) { yield return new WaitForSeconds(2f); // Kiểm tra mỗi 2 giây if (animator == null) yield break; // 1. Kiểm tra Avatar if (animator.avatar == null) Debug.LogError($"[T-POSE ALERT] {animator.gameObject.name} KHÔNG CÓ AVATAR! Đây là lý do bị T-Pose. Hãy kéo Avatar vào component Animator."); // 2. Kiểm tra Speed if (animator.speed <= 0) Debug.LogWarning($"[T-POSE ALERT] Tốc độ Animator đang bằng {animator.speed}. Nhân vật sẽ không cử động."); // 3. Kiểm tra Animation Clips var stateInfo = animator.GetCurrentAnimatorStateInfo(0); var clipInfo = animator.GetCurrentAnimatorClipInfo(0); if (clipInfo.Length == 0) { Debug.LogError($"[T-POSE ALERT] State hiện tại ({stateInfo.fullPathHash}) KHÔNG CÓ Clip animation nào! Hãy kéo file .anim vào ô Motion của State trong Animator Controller."); } // 4. Kiểm tra Culling if (animator.cullingMode == AnimatorCullingMode.CullCompletely) { // Đôi khi Unity tự tắt animation nếu camera không nhìn thấy Debug.Log($"[Info] Culling Mode đang là CullCompletely. Nếu nhân vật ở xa có thể sẽ dừng animation."); } if (!useSimulation) yield break; // Nếu không dùng simulation thì chỉ check 1 lần rồi thôi } } protected virtual void InitializeParameters() { if (animator == null) return; // Khởi tạo và kiểm tra từng tham số quan trọng isDead = ValidateAndInit("isDead"); isGrounded = ValidateAndInit("IsGrounded"); isCrouching = ValidateAndInit("IsCrouching"); isStrafing = ValidateAndInit("IsStrafing"); isSliding = ValidateAndInit("IsSliding"); isSprinting = ValidateAndInit("IsSprinting"); isAiming = ValidateAndInit("IsAiming"); canAim = ValidateAndInit("CanAim"); flipAnimation = ValidateAndInit("FlipAnimation"); flipEquip = ValidateAndInit("FlipEquip"); groundDistance = ValidateAndInit("GroundDistance"); groundAngle = ValidateAndInit("GroundAngle"); verticalVelocity = ValidateAndInit("VerticalVelocity"); moveSet_ID = ValidateAndInit("MoveSet_ID"); upperBody_ID = ValidateAndInit("UpperBody_ID"); idleRandom = ValidateAndInit("IdleRandom"); idleRandomTrigger = ValidateAndInit("IdleRandomTrigger"); randomAttack = ValidateAndInit("RandomAttack"); weakAttack = ValidateAndInit("WeakAttack"); strongAttack = ValidateAndInit("StrongAttack"); isBlocking = ValidateAndInit("IsBlocking"); attackID = ValidateAndInit("AttackID"); defenseID = ValidateAndInit("DefenseID"); recoilID = ValidateAndInit("RecoilID"); reactionID = ValidateAndInit("ReactionID"); triggerRecoil = ValidateAndInit("TriggerRecoil"); triggerReaction = ValidateAndInit("TriggerReaction"); hitDirection = ValidateAndInit("HitDirection"); resetState = ValidateAndInit("ResetState"); reload = ValidateAndInit("Reload"); cancelReload = ValidateAndInit("CancelReload"); reloadID = ValidateAndInit("ReloadID"); shoot = ValidateAndInit("Shoot"); shot_ID = ValidateAndInit("Shot_ID"); powerCharger = ValidateAndInit("PowerCharger"); } private vAnimatorParameter ValidateAndInit(string paramName) { vAnimatorParameter p = new vAnimatorParameter(animator, paramName); if (!p.isValid && debugMode) { // Chỉ cảnh báo những biến cốt lõi nếu thiếu if (paramName == "VerticalVelocity" || paramName == "IsGrounded" || paramName == "IsAiming") Debug.LogWarning($"[AnimatorAI] Cảnh báo: Controller thiếu biến quan trọng: {paramName}"); } return p; } protected virtual void Update() { if (animator == null) return; if (useSimulation) { RunSimulation(); } else { if (enemyAI == null || agent == null) return; UpdateMovementParameters(); UpdateCombatParameters(); } } protected virtual void RunSimulation() { // 1. Giả lập tốc độ di chuyển if (autoCycleSpeed) { // Tạo vòng lặp tốc độ từ 0 đến 1 dùng hàm Sin simVerticalVelocity = Mathf.Abs(Mathf.Sin(Time.time * 0.5f)); simIsSprinting = simVerticalVelocity > sprintThreshold; } SetFloat(verticalVelocity, simVerticalVelocity, "SIM: VerticalVelocity"); SetBool(isSprinting, simIsSprinting, "SIM: IsSprinting"); SetBool(isGrounded, true, "SIM: IsGrounded"); // Luôn giả lập trên mặt đất // 2. Giả lập chiến đấu SetBool(isAiming, simIsAiming, "SIM: IsAiming"); SetInt(moveSet_ID, simMoveSetID, "SIM: MoveSet_ID"); SetBool(canAim, simIsAiming, "SIM: CanAim"); } protected virtual void UpdateMovementParameters() { bool grounded = agent.isOnNavMesh || agent.enabled; SetBool(isGrounded, grounded, "IsGrounded"); float speed = agent.velocity.magnitude / enemyAI.moveSpeed; SetFloat(verticalVelocity, speed, "VerticalVelocity"); bool sprinting = agent.velocity.magnitude > (enemyAI.moveSpeed * sprintThreshold); SetBool(isSprinting, sprinting, "IsSprinting"); bool isDodging = !agent.enabled && !rb.isKinematic; SetBool(flipAnimation, isDodging, "FlipAnimation (Dodge)"); } protected virtual void UpdateCombatParameters() { bool aiming = enemyAI.playerHasArtifact && agent.isStopped; SetBool(isAiming, aiming, "IsAiming"); int moveID = enemyAI.playerHasArtifact ? 1 : 0; SetInt(moveSet_ID, moveID, "MoveSet_ID"); SetBool(canAim, enemyAI.playerHasArtifact, "CanAim"); } #region Optimized Setters with Debug protected void SetBool(vAnimatorParameter param, bool value, string name) { if (param.isValid) { bool current = animator.GetBool(param); if (current != value) { animator.SetBool(param, value); if (debugMode) Debug.Log($"[AnimDebug] {gameObject.name}: {name} -> {value}"); } } } protected void SetFloat(vAnimatorParameter param, float value, string name) { if (param.isValid) { animator.SetFloat(param, value, dampTime, Time.deltaTime); } } protected void SetInt(vAnimatorParameter param, int value, string name) { if (param.isValid) { int current = animator.GetInteger(param); if (current != value) { animator.SetInteger(param, value); if (debugMode) Debug.Log($"[AnimDebug] {gameObject.name}: {name} -> {value}"); } } } #endregion #region Helper Methods (Triggers) public virtual void SetAnimatorTrigger(vAnimatorParameter trigger, string name = "Trigger") { if (trigger.isValid) { if (debugMode) Debug.Log($"[AnimDebug] {gameObject.name}: Kích hoạt {name}"); StartCoroutine(SetTriggerRoutine(trigger)); } } private IEnumerator SetTriggerRoutine(int targetHash) { animator.SetTrigger(targetHash); yield return new WaitForSeconds(0.1f); animator.ResetTrigger(targetHash); } #endregion }