Files
BABA_YAGA/Assets/Opsive/UltimateCharacterController/Scripts/Character/Abilities/StoredInputAbilityBase.cs
2026-06-14 23:57:44 +07:00

180 lines
6.5 KiB
C#

/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Character.Abilities
{
using Opsive.Shared.Events;
using UnityEngine;
/// <summary>
/// Acts as the parent for any ability which activates based on stored input values.
/// </summary>
public abstract class StoredInputAbilityBase : Ability
{
[Tooltip("The number of inputs to store when determining if the ability can start.")]
[SerializeField] protected int m_MaxInputCount = 10;
public int MaxInputCount { get { return m_MaxInputCount; } }
protected Vector2[] m_Inputs;
protected int m_InputCount;
protected int m_InputIndex = -1;
protected int m_LastCanStartFrame = -1;
private bool m_AccumulateInputs = true;
protected abstract bool UseRawInput { get; }
protected abstract bool RequireInput { get; }
/// <summary>
/// Initialize the default values.
/// </summary>
public override void Awake()
{
base.Awake();
m_Inputs = new Vector2[m_MaxInputCount];
EventHandler.RegisterEvent<bool>(m_GameObject, "OnCharacterGrounded", OnGrounded);
EventHandler.RegisterEvent<bool>(m_GameObject, "OnCharacterImmediateTransformChange", OnImmediateTransformChange);
EventHandler.RegisterEvent(m_GameObject, "OnStoredInputAbilityResetStoredInputs", ResetStoredInputs);
}
/// <summary>
/// Store the character's input values.
/// </summary>
public override void InactiveUpdate()
{
base.InactiveUpdate();
// When the ability first stops InactiveUpdate will run immediately after CanStopAbility if the AbilityStopType is Automatic. This first
// call should be ignored.
if (!m_AccumulateInputs) {
m_AccumulateInputs = true;
return;
}
if (!m_CharacterLocomotion.Grounded) {
return;
}
if (!m_CharacterLocomotion.Moving) {
if (m_InputCount > 0) {
m_InputCount--;
}
return;
}
m_InputIndex = (m_InputIndex + 1) % m_MaxInputCount;
m_Inputs[m_InputIndex] = UseRawInput ? m_CharacterLocomotion.RawInputVector : m_CharacterLocomotion.InputVector;
// The count will increase until the max value count.
if (m_InputIndex == m_InputCount) {
m_InputCount++;
}
}
/// <summary>
/// Called when the ablity is tried to be started. If false is returned then the ability will not be started.
/// </summary>
/// <returns>True if the ability can be started.</returns>
public override bool CanStartAbility()
{
// The character is updated during FixedUpdate but the input is only updated within Update so it's possible for duplicate inputs to occur.
// Return early if the current frame is the last checked frame to prevent the ability starting from based on duplicate input.
if (m_LastCanStartFrame == Time.frameCount) {
return false;
}
m_LastCanStartFrame = Time.frameCount;
// An attribute may prevent the ability from starting.
if (!base.CanStartAbility()) {
return false;
}
// The ability can't be started if the character isn't on the ground or there are no values.
if (!m_CharacterLocomotion.Grounded || (RequireInput && m_InputCount == 0)) {
return false;
}
return true;
}
/// <summary>
/// The ability has started.
/// </summary>
protected override void AbilityStarted()
{
base.AbilityStarted();
m_AccumulateInputs = false;
ResetStoredInputs();
}
/// <summary>
/// The ability has stopped running.
/// </summary>
/// <param name="force">Was the ability force stopped?</param>
protected override void AbilityStopped(bool force)
{
AbilityStopped(force, true);
}
/// <summary>
/// The ability has stopped running.
/// </summary>
/// <param name="force">Was the ability force stopped?</param>
/// <param name="resetStoredInputs">Should the stored inputs be reset?</param>
protected void AbilityStopped(bool force, bool resetStoredInputs)
{
base.AbilityStopped(force);
if (resetStoredInputs) {
// Reset the inputs for all of the stored input abilities to prevent the other ability from starting so soon after the initial ability stopped.
EventHandler.ExecuteEvent(m_GameObject, "OnStoredInputAbilityResetStoredInputs");
}
}
/// <summary>
/// The character has changed grounded states.
/// </summary>
/// <param name="grounded">Is the character on the ground?</param>
private void OnGrounded(bool grounded)
{
if (!grounded) {
ResetStoredInputs();
}
}
/// <summary>
/// The character's position or rotation has been teleported.
/// </summary>
/// <param name="snapAnimator">Should the animator be snapped?</param>
private void OnImmediateTransformChange(bool snapAnimator)
{
m_AccumulateInputs = false;
ResetStoredInputs();
}
/// <summary>
/// Resets the internal input variables back to their starting values.
/// </summary>
protected void ResetStoredInputs()
{
m_InputIndex = -1;
m_InputCount = 0;
}
/// <summary>
/// Called when the character is destroyed.
/// </summary>
public override void OnDestroy()
{
base.OnDestroy();
EventHandler.UnregisterEvent<bool>(m_GameObject, "OnCharacterGrounded", OnGrounded);
EventHandler.UnregisterEvent<bool>(m_GameObject, "OnCharacterImmediateTransformChange", OnImmediateTransformChange);
EventHandler.UnregisterEvent(m_GameObject, "OnStoredInputAbilityResetStoredInputs", ResetStoredInputs);
}
}
}