Files
BABA_YAGA/Assets/Third Parties/Opsive/UltimateCharacterController/Scripts/Character/Abilities/Fall.cs
Scove 3e39117acc Consolidate third-party plugins into Assets/Plugins
Move and consolidate many third-party plugin files and metadata from various locations (notably Assets/Third Parties/Plugins 1 and scattered Opsive/Photon folders) into a unified Assets/Plugins directory. Includes DOTween, PrimeTween, Native/BackroomsNoise, Sirenix/Odin Inspector, and Opsive UltimateCharacterController/shared libs, plus updates to several .meta files and removal of obsolete installer/legacy files. This standardizes plugin layout and cleans up duplicate/obsolete assets.
2026-06-16 18:41:44 +07:00

176 lines
7.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 Opsive.Shared.Game;
using Opsive.Shared.Utility;
using Opsive.UltimateCharacterController.SurfaceSystem;
using Opsive.UltimateCharacterController.Utility;
using UnityEngine;
/// <summary>
/// The Fall ability allows the character to play a falling animation when the character has a negative y velocity.
/// </summary>
[DefaultStopType(AbilityStopType.Automatic)]
[DefaultAbilityIndex(2)]
[DefaultUseRootMotionPosition(AbilityBoolOverride.False)]
[DefaultUseRootMotionRotation(AbilityBoolOverride.False)]
public class Fall : Ability
{
[Tooltip("The minimum height between the ground and character that the ability can start. Set to 0 to start at any height.")]
[SerializeField] protected float m_MinFallHeight = 0.2f;
[Tooltip("A reference to the Surface Impact triggered when the character hits the ground.")]
[SerializeField] protected SurfaceImpact m_LandSurfaceImpact;
[Tooltip("The minimum velocity required for the Surface Impact to play.")]
[SerializeField] protected float m_MinSurfaceImpactVelocity = -4f;
[Tooltip("Specifies if the ability should wait for the OnAnimatorFallComplete animation event or wait for the specified duration before ending the fall.")]
[SerializeField] protected AnimationEventTrigger m_LandEvent = new AnimationEventTrigger(true, 0f);
public float MinFallHeight { get { return m_MinFallHeight; } set { m_MinFallHeight = value; } }
public SurfaceImpact LandSurfaceImpact { get { return m_LandSurfaceImpact; } set { m_LandSurfaceImpact = value; } }
public float MinSurfaceImpactVelocity { get { return m_MinSurfaceImpactVelocity; } set { m_MinSurfaceImpactVelocity = value; } }
public AnimationEventTrigger LandEvent { get { return m_LandEvent; } set { m_LandEvent = value; } }
public override int AbilityIntData { get { return m_StateIndex; } }
public override float AbilityFloatData { get { return m_CharacterLocomotion.LocalLocomotionVelocity.y; } }
private int m_StateIndex;
private bool m_Landed;
[Snapshot] protected bool Landed { get { return m_Landed; } set { m_Landed = value; } }
/// <summary>
/// Can the ability be started?
/// </summary>
/// <returns>True if the ability can be started.</returns>
public override bool CanStartAbility()
{
// An attribute may prevent the ability from starting.
if (!base.CanStartAbility()) {
return false;
}
// Fall can't be started if the character is on the ground.
if (m_CharacterLocomotion.Grounded) {
return false;
}
// The ground distance must be greater then the minimum fall height if a value is set.
RaycastHit hit;
if (m_MinFallHeight != 0 && Physics.Raycast(m_Transform.position, -m_CharacterLocomotion.Up, out hit, m_MinFallHeight, m_CharacterLayerManager.SolidObjectLayers, QueryTriggerInteraction.Ignore)) {
return false;
}
return true;
}
/// <summary>
/// The ability has started.
/// </summary>
protected override void AbilityStarted()
{
base.AbilityStarted();
m_StateIndex = 0;
m_Landed = false;
EventHandler.RegisterEvent<bool>(m_GameObject, "OnCharacterGrounded", OnGrounded);
EventHandler.RegisterEvent(m_GameObject, "OnAnimatorFallComplete", Land);
EventHandler.RegisterEvent<bool>(m_GameObject, "OnCharacterImmediateTransformChange", OnImmediateTransformChange);
}
/// <summary>
/// Called when another ability is attempting to start and the current ability is active.
/// Returns true or false depending on if the new ability should be blocked from starting.
/// </summary>
/// <param name="startingAbility">The ability that is starting.</param>
/// <returns>True if the ability should be blocked.</returns>
public override bool ShouldBlockAbilityStart(Ability startingAbility)
{
return startingAbility is HeightChange;
}
/// <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) {
// Allow the SurfaceManager to play an effect when the character hits the ground.
if (m_LandSurfaceImpact != null && m_CharacterLocomotion.LocalVelocity.y < m_MinSurfaceImpactVelocity) {
SurfaceManager.SpawnEffect(m_CharacterLocomotion.GroundRaycastHit, m_LandSurfaceImpact, m_CharacterLocomotion.GravityDirection, m_CharacterLocomotion.TimeScale, m_GameObject, m_Transform.forward, false);
}
// Move to the fall end state when the character lands.
m_StateIndex = 1;
if (!m_LandEvent.WaitForAnimationEvent) {
Scheduler.ScheduleFixed(m_LandEvent.Duration, Land);
}
} else {
m_StateIndex = 0;
}
}
/// <summary>
/// Update the Animator for the ability.
/// </summary>
public override void UpdateAnimator()
{
SetAbilityIntDataParameter(m_StateIndex);
}
/// <summary>
/// The character has landed.
/// </summary>
private void Land()
{
m_Landed = true;
// The controller may no longer be grounded during the time that it takes for the land animation to send the OnAnimatorFallComplete event.
if (m_CharacterLocomotion.Grounded) {
StopAbility();
}
}
/// <summary>
/// Can the ability be stopped?
/// </summary>
/// <returns>True if the ability can be stopped.</returns>
public override bool CanStopAbility()
{
return m_Landed;
}
/// <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)
{
if (!m_CharacterLocomotion.Grounded && !snapAnimator) {
return;
}
// The character is on the ground but fall is still active. Stop the fall.
m_Landed = true;
StopAbility();
}
/// <summary>
/// The ability has stopped running.
/// </summary>
/// <param name="force">Was the ability force stopped?</param>
protected override void AbilityStopped(bool force)
{
base.AbilityStopped(force);
EventHandler.UnregisterEvent<bool>(m_GameObject, "OnCharacterGrounded", OnGrounded);
EventHandler.UnregisterEvent(m_GameObject, "OnAnimatorFallComplete", Land);
EventHandler.UnregisterEvent<bool>(m_GameObject, "OnCharacterImmediateTransformChange", OnImmediateTransformChange);
}
}
}