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

130 lines
5.7 KiB
C#

/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.ThirdPersonController.Character.Abilities
{
using Opsive.Shared.Events;
using Opsive.UltimateCharacterController.Character.Abilities;
using Opsive.UltimateCharacterController.Character.MovementTypes;
using Opsive.UltimateCharacterController.Utility;
using UnityEngine;
/// <summary>
/// Follows a path specified by the 2.5D movement type. Ensures the character stays at a consistant horizontal offset.
/// </summary>
[DefaultStartType(AbilityStartType.Manual)]
public class FollowPseudo3DPath : Ability
{
public override bool IsConcurrent { get { return true; } }
private MovementTypes.Pseudo3D m_Pseudo3DMovementType;
private int m_PathIndex;
private float m_Offset;
/// <summary>
/// Initialize the default values.
/// </summary>
public override void Awake()
{
base.Awake();
EventHandler.RegisterEvent<MovementType, bool>(m_GameObject, "OnCharacterChangeMovementType", OnCharacterChangeMovementType);
}
/// <summary>
/// The specified movement type has been activated or deactivated.
/// </summary>
/// <param name="movementType">The movement type that has been activated or deactivated.</param>
/// <param name="active">Is the movement type active?</param>
private void OnCharacterChangeMovementType(MovementType movementType, bool active)
{
if (!(movementType is MovementTypes.Pseudo3D)) {
return;
}
if (!IsActive && active) {
// If the movement type is active then the ability should start if a path exists.
var pseudo3DMovementType = movementType as MovementTypes.Pseudo3D;
if (pseudo3DMovementType.Path != null) {
StartAbility();
return;
}
} else if (IsActive && !active) {
// If the movement type is no longer active then the ability should stop.
StopAbility();
}
}
/// <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()
{
if (!base.CanStartAbility()) {
return false;
}
return (m_CharacterLocomotion.ActiveMovementType is MovementTypes.Pseudo3D) && (m_CharacterLocomotion.ActiveMovementType as MovementTypes.Pseudo3D).Path != null;
}
/// <summary>
/// The ability has started.
/// </summary>
protected override void AbilityStarted()
{
base.AbilityStarted();
m_Pseudo3DMovementType = m_CharacterLocomotion.ActiveMovementType as MovementTypes.Pseudo3D;
// Store the horizontal offset between the character and the path. This offset will ensure the character stays at a constant horizontal position.
var pathRotation = Quaternion.LookRotation(m_Pseudo3DMovementType.Path.GetTangent(m_Transform.position, ref m_PathIndex));
var closestPoint = m_Pseudo3DMovementType.Path.GetClosestPoint(m_Transform.position, ref m_PathIndex);
m_Offset = MathUtility.InverseTransformPoint(m_Transform.position, pathRotation, closestPoint).x;
}
/// <summary>
/// Stop the ability if the path no longer exists.
/// </summary>
public override void Update()
{
if (m_Pseudo3DMovementType.Path == null) {
StopAbility();
}
}
/// <summary>
/// Update the controller's position values.
/// </summary>
public override void UpdatePosition()
{
// Retireve the closest point and tangent of the path at the target position. This position will be used when determining if the character needs to change
// horizontal offsets.
var targetPosition = m_Transform.position + m_CharacterLocomotion.MoveDirection;
var closestPoint = m_Pseudo3DMovementType.Path.GetClosestPoint(targetPosition, ref m_PathIndex);
var pathRotation = Quaternion.LookRotation(m_Pseudo3DMovementType.Path.GetTangent(targetPosition, ref m_PathIndex));
// Update the offset if the character can move along the relative x axis. The depth term is used in relation to the camera's depth axis.
if (m_Pseudo3DMovementType.AllowDepthMovement && m_CharacterLocomotion.RawInputVector.y != 0) {
m_Offset -= MathUtility.InverseTransformDirection(m_CharacterLocomotion.MotorThrottle, pathRotation).x;
}
// Update the target position based on the difference between the current offset and the stored offset.
var offset = MathUtility.InverseTransformPoint(targetPosition, pathRotation, closestPoint).x;
targetPosition = MathUtility.TransformPoint(targetPosition, pathRotation, Vector3.right * (offset - m_Offset));
m_CharacterLocomotion.MoveDirection = targetPosition - m_Transform.position;
}
/// <summary>
/// The character has been destroyed.
/// </summary>
public override void OnDestroy()
{
base.OnDestroy();
EventHandler.UnregisterEvent<MovementType, bool>(m_GameObject, "OnCharacterChangeMovementType", OnCharacterChangeMovementType);
}
}
}