2026-06-05 15:59:33 +07:00
using UnityEngine ;
2026-06-05 16:26:04 +07:00
using UnityEngine.AI ;
using Invector ;
using System.Collections ;
2026-06-05 15:59:33 +07:00
2026-06-05 16:26:04 +07:00
/// <summary>
/// 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.
/// </summary>
2026-06-05 15:59:33 +07:00
public class AnimatorAI : MonoBehaviour
{
2026-06-05 16:26:04 +07:00
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 < Animator > ( ) ;
enemyAI = GetComponent < EnemyAI > ( ) ;
agent = GetComponent < NavMeshAgent > ( ) ;
rb = GetComponent < Rigidbody > ( ) ;
if ( animator = = null )
{
Debug . LogError ( $"<color=red>[AnimatorAI]</color> KHÔNG tìm thấy Animator trên {gameObject.name} hoặc các con của nó!" ) ;
2026-06-05 16:38:25 +07:00
return ;
2026-06-05 16:26:04 +07:00
}
2026-06-05 16:38:25 +07:00
// CHẨN ĐOÁN SÂU:
if ( debugMode ) StartCoroutine ( DeepDiagnosticRoutine ( ) ) ;
2026-06-05 16:26:04 +07:00
InitializeParameters ( ) ;
}
2026-06-05 16:38:25 +07:00
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 ( $"<color=red>[T-POSE ALERT]</color> {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 ( $"<color=yellow>[T-POSE ALERT]</color> 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 ( $"<color=red>[T-POSE ALERT]</color> 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 ( $"<color=white>[Info]</color> 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
}
}
2026-06-05 16:26:04 +07:00
protected virtual void InitializeParameters ( )
{
if ( animator = = null ) return ;
2026-06-05 16:38:25 +07:00
// 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 ( $"<color=yellow>[AnimatorAI]</color> Cảnh báo: Controller thiếu biến quan trọng: <b>{paramName}</b>" ) ;
}
return p ;
2026-06-05 16:26:04 +07:00
}
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 ( )
2026-06-05 15:59:33 +07:00
{
2026-06-05 16:26:04 +07:00
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 ( $"<color=cyan>[AnimDebug]</color> {gameObject.name}: <b>{name}</b> -> {value}" ) ;
}
}
2026-06-05 15:59:33 +07:00
}
2026-06-05 16:26:04 +07:00
protected void SetFloat ( vAnimatorParameter param , float value , string name )
2026-06-05 15:59:33 +07:00
{
2026-06-05 16:26:04 +07:00
if ( param . isValid )
{
animator . SetFloat ( param , value , dampTime , Time . deltaTime ) ;
}
2026-06-05 15:59:33 +07:00
}
2026-06-05 16:26:04 +07:00
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 ( $"<color=orange>[AnimDebug]</color> {gameObject.name}: <b>{name}</b> -> {value}" ) ;
}
}
}
#endregion
#region Helper Methods ( Triggers )
public virtual void SetAnimatorTrigger ( vAnimatorParameter trigger , string name = "Trigger" )
{
if ( trigger . isValid )
{
if ( debugMode ) Debug . Log ( $"<color=yellow>[AnimDebug]</color> {gameObject.name}: Kích hoạt <b>{name}</b>" ) ;
StartCoroutine ( SetTriggerRoutine ( trigger ) ) ;
}
}
private IEnumerator SetTriggerRoutine ( int targetHash )
{
animator . SetTrigger ( targetHash ) ;
yield return new WaitForSeconds ( 0.1f ) ;
animator . ResetTrigger ( targetHash ) ;
}
#endregion
2026-06-05 15:59:33 +07:00
}