BITFALL/Assets/Plugins/Character Controller Pro/Demo/Scripts/States/RopeClimbing.cs

189 lines
6.3 KiB
C#
Raw Normal View History

2023-10-04 16:50:27 +08:00
using System.Collections.Generic;
using UnityEngine;
using Lightbug.CharacterControllerPro.Implementation;
using Lightbug.Utilities;
namespace Lightbug.CharacterControllerPro.Demo
{
public class RopeClimbing : CharacterState
{
[Header("Movement")]
[SerializeField]
protected float climbSpeed = 3f;
[SerializeField]
protected float angularSpeed = 120f;
[SerializeField]
protected float jumpVelocity = 10f;
[SerializeField]
protected float verticalAcceleration = 10f;
[SerializeField]
protected float angularAcceleration = 10f;
[Header("Offset")]
[SerializeField]
protected float forwardOffset = -0.25f;
[Header("Animation")]
[SerializeField]
protected string verticalVelocityParameter = "VerticalVelocity";
// ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
protected Rope currentRope = null;
protected Dictionary<Transform, Rope> ropes = new Dictionary<Transform, Rope>();
protected Vector3 verticalVelocity;
protected Vector3 angularVelocity;
Vector3 ReferencePosition => CharacterActor.Top;
Vector3 ClosestVectorToRope
{
get
{
Vector3 characterToTop = currentRope.TopPosition - CharacterActor.Position;
return Vector3.ProjectOnPlane(characterToTop, currentRope.BottomToTop);
}
}
protected override void Awake()
{
base.Awake();
#if UNITY_2023_1_OR_NEWER
Rope[] ropesArray = FindObjectsByType<Rope>(FindObjectsSortMode.None);
#else
Rope[] ropesArray = FindObjectsOfType<Rope>();
#endif
for (int i = 0; i < ropesArray.Length; i++)
ropes.Add(ropesArray[i].transform, ropesArray[i]);
}
public override void CheckExitTransition()
{
if (!currentRope.IsInRange(ReferencePosition) || CharacterActions.jump.Started)
CharacterStateController.EnqueueTransition<NormalMovement>();
}
public override bool CheckEnterTransition(CharacterState fromState)
{
for (int i = 0; i < CharacterActor.Triggers.Count; i++)
{
Trigger trigger = CharacterActor.Triggers[i];
if (!trigger.firstContact)
continue;
Rope rope = ropes.GetOrRegisterValue(trigger.transform);
if (rope != null)
{
if (!rope.IsInRange(ReferencePosition))
return false;
currentRope = rope;
return true;
}
}
return false;
}
public override void EnterBehaviour(float dt, CharacterState fromState)
{
CharacterActor.IsKinematic = false;
CharacterActor.alwaysNotGrounded = true;
CharacterActor.UseRootMotion = false;
CharacterActor.Velocity = Vector3.zero;
Vector3 closestVectorToRope = ClosestVectorToRope;
CharacterActor.SetYaw(closestVectorToRope);
CharacterActor.Position = CharacterActor.Position + closestVectorToRope + CharacterActor.Forward * forwardOffset;
}
public override void ExitBehaviour(float dt, CharacterState toState)
{
CharacterActor.alwaysNotGrounded = false;
currentRope = null;
if (CharacterActions.jump.Started)
{
if (CharacterActions.movement.Detected)
CharacterActor.Velocity = CharacterStateController.InputMovementReference * jumpVelocity;
else
CharacterActor.Velocity = CharacterStateController.MovementReferenceForward * jumpVelocity;
CharacterActor.SetYaw(Vector3.Normalize(CharacterActor.Velocity));
}
else
{
CharacterActor.Velocity = Vector3.zero;
}
}
public override void UpdateBehaviour(float dt)
{
Vector3 characterPosition = CharacterActor.Position;
float targetAngularSpeed = CharacterActions.movement.value.x * angularSpeed;
characterPosition = CustomUtilities.RotatePointAround(
characterPosition,
characterPosition + ClosestVectorToRope,
targetAngularSpeed * dt,
Vector3.Normalize(currentRope.BottomToTop)
);
Vector3 targetAngularVelocity = (characterPosition - CharacterActor.Position) / dt;
angularVelocity = Vector3.MoveTowards(angularVelocity, targetAngularVelocity, angularAcceleration * dt);
Vector3 targetVerticalVelocity = CharacterActions.movement.value.y * climbSpeed * CharacterActor.Up;
verticalVelocity = Vector3.MoveTowards(verticalVelocity, targetVerticalVelocity, verticalAcceleration * dt);
CharacterActor.Velocity = verticalVelocity + angularVelocity;
}
public override void PostUpdateBehaviour(float dt)
{
// Always look towards the rope.
CharacterActor.SetYaw(ClosestVectorToRope);
}
public override void PostCharacterSimulation(float dt)
{
if (!CharacterActor.IsAnimatorValid())
return;
CharacterActor.Animator.SetFloat(verticalVelocityParameter, CharacterActor.LocalVelocity.y);
}
}
}