Files
BABA_YAGA/Assets/Scripts/AI NPC/AnimatorAI.cs

207 lines
7.0 KiB
C#
Raw Normal View History

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
}