Files
BABA_YAGA/Assets/Third Parties/Opsive/UltimateCharacterController/Scripts/Character/Abilities/StopMovementAnimation.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

157 lines
6.9 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>
/// Prevents the movement animation from playing when the character would run into a solid object. The move direction is predicted if the character is using
/// root motion because with root motion the movement is applied after the animation plays.
/// </summary>
[DefaultStartType(AbilityStartType.Manual)]
public class StopMovementAnimation : Ability
{
[Tooltip("The distance that is checked to determine if there is a collision.")]
[SerializeField] protected float m_CollisionCheckDistance = 0.1f;
[Tooltip("The maximum Character Locomotion Wall Glide Curve value that the ability should start with. The higher the value to more the ability will ignore the Wall Glide Curve value.")]
[SerializeField] protected float m_WallGlideCurveThreshold = 0.1f;
public float CollisionCheckDistance { get { return m_CollisionCheckDistance; } set { m_CollisionCheckDistance = value; } }
public float WallGlideCurveThreshold { get { return m_WallGlideCurveThreshold; } set { m_WallGlideCurveThreshold = value; } }
private RaycastHit m_RaycastHit;
private RestrictPosition m_RestrictPosition;
/// <summary>
/// Cache the Restrict Position ability.
/// </summary>
public override void Start()
{
base.Start();
m_RestrictPosition = m_CharacterLocomotion.GetAbility<RestrictPosition>();
}
/// <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 StoredInputAbilityBase;
}
/// <summary>
/// Called when the current ability is attempting to start and another ability is active.
/// Returns true or false depending on if the active ability should be stopped.
/// </summary>
/// <param name="activeAbility">The ability that is currently active.</param>
/// <returns>True if the ability should be stopped.</returns>
public override bool ShouldStopActiveAbility(Ability activeAbility)
{
return activeAbility is StoredInputAbilityBase;
}
/// <summary>
/// Starts the ability if there will be a collision.
/// </summary>
public override void InactiveUpdate()
{
if (PredicatedCollision()) {
m_CharacterLocomotion.InputVector = Vector2.zero;
StartAbility();
}
}
/// <summary>
/// Predicts if there will be a collision based on the input vector. If using root motion this isn't completely accurate because the animation is
/// updated before the movement but it still works well.
/// </summary>
/// <returns>True if the character would collide with an object based on the input vector.</returns>
private bool PredicatedCollision()
{
if (m_CharacterLocomotion.InputVector.sqrMagnitude < m_CharacterLocomotion.ColliderSpacingCubed) {
return false;
}
// Don't activate if a non-concurrent ability is running with a higher priority.
for (int i = 0; i < m_CharacterLocomotion.ActiveAbilityCount; ++i) {
if ((!m_CharacterLocomotion.ActiveAbilities[i].IsConcurrent || (m_CharacterLocomotion.ActiveAbilities[i] is MoveTowards)) && m_CharacterLocomotion.ActiveAbilities[i].Index < Index) {
return false;
}
}
// If restrict position is stopping the move then the animation should also stop.
var direction = m_Transform.TransformDirection(new Vector3(m_CharacterLocomotion.InputVector.x, 0, m_CharacterLocomotion.InputVector.y)).normalized * m_CollisionCheckDistance;
var targetPosition = m_Transform.position + direction;
if (m_RestrictPosition != null && m_RestrictPosition.IsActive && m_RestrictPosition.RestrictedPosition(ref targetPosition)) {
return true;
}
// Stop the animation if the character would collide with a solid object.
if (m_CharacterLocomotion.SingleCast(direction, Vector3.zero, m_CharacterLayerManager.SolidObjectLayers, ref m_RaycastHit)) {
if (m_RaycastHit.rigidbody != null && !m_RaycastHit.rigidbody.isKinematic) {
return false;
}
var slope = Vector3.Angle(m_CharacterLocomotion.Up, m_RaycastHit.normal);
if (slope <= m_CharacterLocomotion.SlopeLimit + m_CharacterLocomotion.SlopeLimitSpacing) {
return false;
}
// The character may be able to glide across the wall.
var hitStrength = 1 - Vector3.Dot(direction.normalized, -m_RaycastHit.normal);
if (m_CharacterLocomotion.WallGlideCurve.Evaluate(hitStrength) > m_WallGlideCurveThreshold) {
return false;
}
// The character will stop because of the hit object.
return true;
}
return false;
}
/// <summary>
/// Stops the movement animation if there is a collision.
/// </summary>
public override void Update()
{
// Stop the ability as soon as there are no more collisions.
if (!PredicatedCollision()) {
StopAbility();
return;
}
m_CharacterLocomotion.InputVector = Vector3.zero;
}
/// <summary>
/// Verify the position values.
/// </summary>
public override void ApplyPosition()
{
var localMoveDirection = m_Transform.InverseTransformDirection(m_CharacterLocomotion.MoveDirection);
localMoveDirection.x = localMoveDirection.z = 0;
m_CharacterLocomotion.MoveDirection = m_Transform.TransformDirection(localMoveDirection);
}
/// <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.ExecuteEvent(m_GameObject, "OnStoredInputAbilityResetStoredInputs");
}
}
}