2026-06-05 15:59:33 +07:00
|
|
|
using UnityEngine;
|
2026-06-05 16:26:04 +07:00
|
|
|
using UnityEngine.AI;
|
|
|
|
|
using Invector;
|
2026-06-05 23:25:56 +07:00
|
|
|
using Invector.vEventSystems;
|
2026-06-05 16:26:04 +07:00
|
|
|
using System.Collections;
|
2026-06-05 15:59:33 +07:00
|
|
|
|
2026-06-05 16:26:04 +07:00
|
|
|
/// <summary>
|
2026-06-05 23:25:56 +07:00
|
|
|
/// AnimatorAI: Advanced Invector-style Animator synchronization for EnemyAI.
|
|
|
|
|
/// Resolves T-pose by ensuring all core Invector parameters are correctly synced.
|
2026-06-05 16:26:04 +07:00
|
|
|
/// </summary>
|
2026-06-05 23:25:56 +07:00
|
|
|
public class AnimatorAI : MonoBehaviour, vIAnimatorStateInfoController
|
2026-06-05 15:59:33 +07:00
|
|
|
{
|
2026-06-05 16:26:04 +07:00
|
|
|
protected Animator animator;
|
|
|
|
|
protected EnemyAI enemyAI;
|
|
|
|
|
protected NavMeshAgent agent;
|
|
|
|
|
protected Rigidbody rb;
|
2026-06-05 23:25:56 +07:00
|
|
|
protected vHealthController healthController;
|
2026-06-05 16:26:04 +07:00
|
|
|
|
|
|
|
|
[Header("Movement Settings")]
|
|
|
|
|
public float sprintThreshold = 0.8f;
|
|
|
|
|
public float dampTime = 0.1f;
|
2026-06-05 23:25:56 +07:00
|
|
|
public float groundDistanceValue = 0.05f;
|
|
|
|
|
|
|
|
|
|
public vAnimatorStateInfos animatorStateInfos { get; protected set; }
|
2026-06-05 16:26:04 +07:00
|
|
|
|
|
|
|
|
#region Animator Parameters (Invector Style)
|
|
|
|
|
protected vAnimatorParameter isDead;
|
|
|
|
|
protected vAnimatorParameter isGrounded;
|
|
|
|
|
protected vAnimatorParameter isStrafing;
|
|
|
|
|
protected vAnimatorParameter isSprinting;
|
|
|
|
|
protected vAnimatorParameter isAiming;
|
|
|
|
|
protected vAnimatorParameter verticalVelocity;
|
2026-06-05 23:25:56 +07:00
|
|
|
protected vAnimatorParameter horizontalVelocity;
|
|
|
|
|
protected vAnimatorParameter groundDistance;
|
2026-06-05 16:26:04 +07:00
|
|
|
protected vAnimatorParameter moveSet_ID;
|
|
|
|
|
protected vAnimatorParameter attackID;
|
2026-06-05 23:25:56 +07:00
|
|
|
protected vAnimatorParameter hitDirection;
|
2026-06-05 16:26:04 +07:00
|
|
|
protected vAnimatorParameter reactionID;
|
|
|
|
|
protected vAnimatorParameter triggerReaction;
|
|
|
|
|
protected vAnimatorParameter resetState;
|
|
|
|
|
#endregion
|
|
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
protected Vector3 lastPosition;
|
|
|
|
|
protected float currentV;
|
|
|
|
|
protected float currentH;
|
2026-06-05 22:34:41 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
protected virtual void Awake()
|
2026-06-05 16:26:04 +07:00
|
|
|
{
|
|
|
|
|
animator = GetComponentInChildren<Animator>();
|
|
|
|
|
enemyAI = GetComponent<EnemyAI>();
|
|
|
|
|
agent = GetComponent<NavMeshAgent>();
|
|
|
|
|
rb = GetComponent<Rigidbody>();
|
2026-06-05 23:25:56 +07:00
|
|
|
healthController = GetComponent<vHealthController>();
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
if (animator)
|
2026-06-05 16:26:04 +07:00
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
animatorStateInfos = new vAnimatorStateInfos(animator);
|
|
|
|
|
InitializeParameters();
|
2026-06-05 16:26:04 +07:00
|
|
|
}
|
2026-06-05 16:38:25 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
lastPosition = transform.position;
|
2026-06-05 16:26:04 +07:00
|
|
|
}
|
|
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
protected virtual void OnEnable()
|
2026-06-05 16:38:25 +07:00
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
this.Register();
|
|
|
|
|
if (healthController) healthController.onReceiveDamage.AddListener(OnReceiveDamage);
|
2026-06-05 16:38:25 +07:00
|
|
|
}
|
|
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
protected virtual void OnDisable()
|
2026-06-05 16:26:04 +07:00
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
this.UnRegister();
|
|
|
|
|
if (healthController) healthController.onReceiveDamage.RemoveListener(OnReceiveDamage);
|
2026-06-05 16:38:25 +07:00
|
|
|
}
|
|
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
protected virtual void InitializeParameters()
|
2026-06-05 16:38:25 +07:00
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
isDead = new vAnimatorParameter(animator, "isDead");
|
|
|
|
|
isGrounded = new vAnimatorParameter(animator, "IsGrounded");
|
|
|
|
|
isStrafing = new vAnimatorParameter(animator, "IsStrafing");
|
|
|
|
|
isSprinting = new vAnimatorParameter(animator, "IsSprinting");
|
|
|
|
|
isAiming = new vAnimatorParameter(animator, "IsAiming");
|
|
|
|
|
verticalVelocity = new vAnimatorParameter(animator, "VerticalVelocity");
|
|
|
|
|
horizontalVelocity = new vAnimatorParameter(animator, "HorizontalVelocity");
|
|
|
|
|
groundDistance = new vAnimatorParameter(animator, "GroundDistance");
|
|
|
|
|
moveSet_ID = new vAnimatorParameter(animator, "MoveSet_ID");
|
|
|
|
|
attackID = new vAnimatorParameter(animator, "AttackID");
|
|
|
|
|
hitDirection = new vAnimatorParameter(animator, "HitDirection");
|
|
|
|
|
reactionID = new vAnimatorParameter(animator, "ReactionID");
|
|
|
|
|
triggerReaction = new vAnimatorParameter(animator, "TriggerReaction");
|
|
|
|
|
resetState = new vAnimatorParameter(animator, "ResetState");
|
2026-06-05 16:26:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void Update()
|
|
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
if (animator == null || enemyAI == null || agent == null) return;
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
UpdateMovementParameters();
|
|
|
|
|
UpdateCombatParameters();
|
2026-06-05 16:26:04 +07:00
|
|
|
}
|
|
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
protected virtual void UpdateMovementParameters()
|
2026-06-05 16:26:04 +07:00
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
// 1. Grounded & GroundDistance (Critical for T-pose prevention)
|
|
|
|
|
bool grounded = agent.isOnNavMesh || agent.enabled;
|
|
|
|
|
SetBool(isGrounded, grounded);
|
|
|
|
|
SetFloat(groundDistance, grounded ? 0f : groundDistanceValue);
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
// 2. Responsive Velocity Calculation
|
|
|
|
|
// Use a mix of agent velocity and position delta for better responsiveness
|
|
|
|
|
Vector3 worldVelocity = (transform.position - lastPosition) / Time.deltaTime;
|
|
|
|
|
lastPosition = transform.position;
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
if (worldVelocity.magnitude < 0.01f) worldVelocity = Vector3.zero;
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
Vector3 localVelocity = transform.InverseTransformDirection(worldVelocity);
|
|
|
|
|
|
|
|
|
|
float targetV = localVelocity.z / enemyAI.moveSpeed;
|
|
|
|
|
float targetH = localVelocity.x / enemyAI.moveSpeed;
|
|
|
|
|
|
|
|
|
|
// Smooth velocity values
|
|
|
|
|
currentV = Mathf.Lerp(currentV, targetV, 10f * Time.deltaTime);
|
|
|
|
|
currentH = Mathf.Lerp(currentH, targetH, 10f * Time.deltaTime);
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
SetFloat(verticalVelocity, currentV);
|
|
|
|
|
SetFloat(horizontalVelocity, currentH);
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
// 3. Sprinting
|
|
|
|
|
bool sprinting = worldVelocity.magnitude > (enemyAI.moveSpeed * sprintThreshold);
|
|
|
|
|
SetBool(isSprinting, sprinting);
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
// 4. Strafing
|
|
|
|
|
SetBool(isStrafing, enemyAI.playerHasArtifact);
|
2026-06-05 16:26:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void UpdateCombatParameters()
|
|
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
SetBool(isAiming, enemyAI.playerHasArtifact);
|
|
|
|
|
SetInt(moveSet_ID, enemyAI.playerHasArtifact ? 1 : 0);
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
// Shooting burst
|
|
|
|
|
if (enemyAI.IsShootingBurst)
|
|
|
|
|
SetInt(attackID, 1);
|
|
|
|
|
else
|
|
|
|
|
SetInt(attackID, 0);
|
|
|
|
|
|
|
|
|
|
// Dodge logic
|
|
|
|
|
if (enemyAI.IsDodging)
|
|
|
|
|
{
|
|
|
|
|
// In Invector, dodges are often handled via triggers or specific IDs
|
|
|
|
|
SetAnimatorTrigger(triggerReaction);
|
|
|
|
|
}
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
// Death state
|
|
|
|
|
if (healthController) SetBool(isDead, healthController.isDead);
|
2026-06-05 16:26:04 +07:00
|
|
|
}
|
|
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
protected virtual void OnReceiveDamage(vDamage damage)
|
2026-06-05 16:26:04 +07:00
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
if (animator == null || !animator.enabled) return;
|
|
|
|
|
|
|
|
|
|
// Sync damage parameters for hit reactions
|
|
|
|
|
if (hitDirection.isValid && damage.sender)
|
2026-06-05 16:26:04 +07:00
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
float angle = transform.HitAngle(damage.sender.position);
|
|
|
|
|
animator.SetInteger(hitDirection, (int)angle);
|
2026-06-05 16:26:04 +07:00
|
|
|
}
|
2026-06-05 23:25:56 +07:00
|
|
|
|
|
|
|
|
if (reactionID.isValid) animator.SetInteger(reactionID, damage.reaction_id);
|
|
|
|
|
|
|
|
|
|
SetAnimatorTrigger(triggerReaction);
|
|
|
|
|
SetAnimatorTrigger(resetState);
|
2026-06-05 15:59:33 +07:00
|
|
|
}
|
|
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
#region Helpers
|
|
|
|
|
protected void SetBool(vAnimatorParameter param, bool value)
|
2026-06-05 15:59:33 +07:00
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
if (param.isValid && animator.GetBool(param) != value)
|
|
|
|
|
animator.SetBool(param, value);
|
2026-06-05 15:59:33 +07:00
|
|
|
}
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
protected void SetFloat(vAnimatorParameter param, float value)
|
2026-06-05 16:26:04 +07:00
|
|
|
{
|
|
|
|
|
if (param.isValid)
|
2026-06-05 23:25:56 +07:00
|
|
|
animator.SetFloat(param, value, dampTime, Time.deltaTime);
|
2026-06-05 16:26:04 +07:00
|
|
|
}
|
|
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
protected void SetInt(vAnimatorParameter param, int value)
|
|
|
|
|
{
|
|
|
|
|
if (param.isValid && animator.GetInteger(param) != value)
|
|
|
|
|
animator.SetInteger(param, value);
|
|
|
|
|
}
|
2026-06-05 16:26:04 +07:00
|
|
|
|
2026-06-05 23:25:56 +07:00
|
|
|
public void SetAnimatorTrigger(vAnimatorParameter trigger)
|
2026-06-05 16:26:04 +07:00
|
|
|
{
|
2026-06-05 23:25:56 +07:00
|
|
|
if (trigger.isValid) StartCoroutine(SetTriggerRoutine(trigger));
|
2026-06-05 16:26:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
}
|