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 [Header("Nuclear Diagnostic (Siêu chẩn đoán)")] public string forcePlayState = ""; // Gõ tên State vào đây để ép nó chạy (vd: Idle) public bool showLayerWeights = false; 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ạy chẩn đoán hạt nhân StartCoroutine(NuclearDiagnosticRoutine()); InitializeParameters(); } private IEnumerator NuclearDiagnosticRoutine() { while (true) { yield return new WaitForSeconds(1f); if (animator == null) yield break; // 1. Kiểm tra Enabled if (!animator.enabled) Debug.LogError($"[NUCLEAR ALERT] Component Animator đang bị TẮT (enabled = false)!"); // 2. Kiểm tra Rig Type if (animator.isHuman) Debug.Log($"[Rig Info] Rig là Humanoid. Hãy chắc chắn các Animation cũng là Humanoid."); else Debug.LogWarning($"[Rig Info] Rig là Generic/Legacy. Nếu bạn dùng Animation Humanoid nó sẽ không chạy."); // 3. Kiểm tra Layer Weight if (showLayerWeights) { for (int i = 0; i < animator.layerCount; i++) { Debug.Log($"Layer {i} ({animator.GetLayerName(i)}): Weight = {animator.GetLayerWeight(i)}"); } } // 4. Ép chạy State nếu có yêu cầu if (!string.IsNullOrEmpty(forcePlayState)) { Debug.Log($"[Nuclear Force] ÉP CHẠY STATE: {forcePlayState}"); animator.Play(forcePlayState); forcePlayState = ""; // Reset sau khi chạy } // 5. Kiểm tra vị trí xương (Nếu T-pose thì thường xương không đổi vị trí) Vector3 handPos = animator.GetBoneTransform(HumanBodyBones.RightHand) ? animator.GetBoneTransform(HumanBodyBones.RightHand).position : Vector3.zero; yield return new WaitForSeconds(0.1f); if (handPos != Vector3.zero && Vector3.Distance(handPos, animator.GetBoneTransform(HumanBodyBones.RightHand).position) < 0.001f && animator.speed > 0) { // Nếu xương không nhúc nhích dù tốc độ > 0 Debug.LogWarning($"[NUCLEAR ALERT] CẢNH BÁO: Xương nhân vật không nhúc nhích! Có thể do Rig lỗi hoặc bị script khác khóa xương."); } if (!useSimulation) yield break; } } 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 }