diff --git a/Assets/Prefabs/NPC/xNPC.prefab b/Assets/Prefabs/NPC/xNPC.prefab
index bfd33362..247b00d8 100644
--- a/Assets/Prefabs/NPC/xNPC.prefab
+++ b/Assets/Prefabs/NPC/xNPC.prefab
@@ -453,6 +453,8 @@ GameObject:
- component: {fileID: 5770331367975928816}
- component: {fileID: 4112854993683970537}
- component: {fileID: 3240800621832008763}
+ - component: {fileID: 8239948856752686218}
+ - component: {fileID: 3321677048699702084}
m_Layer: 0
m_Name: xNPC
m_TagString: Untagged
@@ -491,23 +493,15 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::EnemyAI
player: {fileID: 0}
- viewAngle: 90
- viewRadius: 20
- targetLayerMask:
- serializedVersion: 2
- m_Bits: 0
- obstacleLayerMask:
- serializedVersion: 2
- m_Bits: 0
- patrolPoints: []
moveSpeed: 3
- chaseSpeed: 5
+ rotateSpeed: 50
+ patrolPoints: []
+ patrolWaitTime: 2
playerHasArtifact: 0
laserPrefab: {fileID: 3965388737199864462, guid: fbec2b501d70daa4c9cb481ba53fc0b8, type: 3}
firePoint: {fileID: 5863061020199015852}
minShootDelay: 1
maxShootDelay: 3
- rotateSpeed: 50
dodgeForce: 8
dodgeDuration: 0.5
dodgeCooldown: 3
@@ -575,6 +569,42 @@ MonoBehaviour:
textDisplay: {fileID: 4454520411962799200}
canvasGroup: {fileID: 1620994605374968907}
bubbleRect: {fileID: 6775114823217050358}
+--- !u!114 &8239948856752686218
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7522161431095319480}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 210b37cfe4a84a34a91d0a9e58856a60, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::FieldOfView
+ viewAngle: 90
+ viewRadius: 20
+ obstacleLayerMask:
+ serializedVersion: 2
+ m_Bits: 128
+ targetLayerMask:
+ serializedVersion: 2
+ m_Bits: 256
+ canSeePlayer: 0
+ lastKnownPlayerPosition: {x: 0, y: 0, z: 0}
+--- !u!114 &3321677048699702084
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7522161431095319480}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 35bba55c2a743d042ab1fff35e29db50, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::AnimatorAI
+ sprintThreshold: 0.8
+ dampTime: 0.1
--- !u!1001 &7561534673732472622
PrefabInstance:
m_ObjectHideFlags: 0
diff --git a/Assets/Scripts/AI NPC/AnimatorAI.cs b/Assets/Scripts/AI NPC/AnimatorAI.cs
index 7d96529a..695cfb0c 100644
--- a/Assets/Scripts/AI NPC/AnimatorAI.cs
+++ b/Assets/Scripts/AI NPC/AnimatorAI.cs
@@ -1,16 +1,251 @@
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
{
- // Start is called once before the first execution of Update after the MonoBehaviour is created
- void Start()
+ 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()
{
-
+ // Sử dụng GetComponentInChildren để tìm Animator ở các đối tượng con (khung Mesh)
+ 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ó!");
+ }
+ else if (debugMode)
+ {
+ Debug.Log($"[AnimatorAI] Đã tìm thấy Animator trên đối tượng: {animator.gameObject.name}");
+ }
+
+ InitializeParameters();
}
- // Update is called once per frame
- void Update()
+ protected virtual void InitializeParameters()
{
-
+ if (animator == null) return;
+
+ isDead = new vAnimatorParameter(animator, "isDead");
+ isGrounded = new vAnimatorParameter(animator, "IsGrounded");
+ isCrouching = new vAnimatorParameter(animator, "IsCrouching");
+ isStrafing = new vAnimatorParameter(animator, "IsStrafing");
+ isSliding = new vAnimatorParameter(animator, "IsSliding");
+ isSprinting = new vAnimatorParameter(animator, "IsSprinting");
+ isAiming = new vAnimatorParameter(animator, "IsAiming");
+ canAim = new vAnimatorParameter(animator, "CanAim");
+ flipAnimation = new vAnimatorParameter(animator, "FlipAnimation");
+ flipEquip = new vAnimatorParameter(animator, "FlipEquip");
+ groundDistance = new vAnimatorParameter(animator, "GroundDistance");
+ groundAngle = new vAnimatorParameter(animator, "GroundAngle");
+ verticalVelocity = new vAnimatorParameter(animator, "VerticalVelocity");
+ moveSet_ID = new vAnimatorParameter(animator, "MoveSet_ID");
+ upperBody_ID = new vAnimatorParameter(animator, "UpperBody_ID");
+ idleRandom = new vAnimatorParameter(animator, "IdleRandom");
+ idleRandomTrigger = new vAnimatorParameter(animator, "IdleRandomTrigger");
+ randomAttack = new vAnimatorParameter(animator, "RandomAttack");
+ weakAttack = new vAnimatorParameter(animator, "WeakAttack");
+ strongAttack = new vAnimatorParameter(animator, "StrongAttack");
+ isBlocking = new vAnimatorParameter(animator, "IsBlocking");
+ attackID = new vAnimatorParameter(animator, "AttackID");
+ defenseID = new vAnimatorParameter(animator, "DefenseID");
+ recoilID = new vAnimatorParameter(animator, "RecoilID");
+ reactionID = new vAnimatorParameter(animator, "ReactionID");
+ triggerRecoil = new vAnimatorParameter(animator, "TriggerRecoil");
+ triggerReaction = new vAnimatorParameter(animator, "TriggerReaction");
+ hitDirection = new vAnimatorParameter(animator, "HitDirection");
+ resetState = new vAnimatorParameter(animator, "ResetState");
+ reload = new vAnimatorParameter(animator, "Reload");
+ cancelReload = new vAnimatorParameter(animator, "CancelReload");
+ reloadID = new vAnimatorParameter(animator, "ReloadID");
+ shoot = new vAnimatorParameter(animator, "Shoot");
+ shot_ID = new vAnimatorParameter(animator, "Shot_ID");
+ powerCharger = new vAnimatorParameter(animator, "PowerCharger");
}
+
+ 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
}