818 lines
31 KiB
C#
818 lines
31 KiB
C#
using UnityEngine;
|
|
using Lightbug.CharacterControllerPro.Core;
|
|
using Lightbug.Utilities;
|
|
using Lightbug.CharacterControllerPro.Implementation;
|
|
|
|
namespace Lightbug.CharacterControllerPro.Demo
|
|
{
|
|
[AddComponentMenu("Character Controller Pro/Demo/Character/States/Normal Movement")]
|
|
public class NormalMovement : CharacterState
|
|
{
|
|
|
|
[Space(10)]
|
|
|
|
public PlanarMovementParameters planarMovementParameters = new PlanarMovementParameters();
|
|
|
|
public VerticalMovementParameters verticalMovementParameters = new VerticalMovementParameters();
|
|
|
|
public CrouchParameters crouchParameters = new CrouchParameters();
|
|
|
|
public LookingDirectionParameters lookingDirectionParameters = new LookingDirectionParameters();
|
|
|
|
|
|
[Header("Animation")]
|
|
|
|
[SerializeField]
|
|
protected string groundedParameter = "Grounded";
|
|
|
|
[SerializeField]
|
|
protected string stableParameter = "Stable";
|
|
|
|
[SerializeField]
|
|
protected string verticalSpeedParameter = "VerticalSpeed";
|
|
|
|
[SerializeField]
|
|
protected string planarSpeedParameter = "PlanarSpeed";
|
|
|
|
[SerializeField]
|
|
protected string horizontalAxisParameter = "HorizontalAxis";
|
|
|
|
[SerializeField]
|
|
protected string verticalAxisParameter = "VerticalAxis";
|
|
|
|
[SerializeField]
|
|
protected string heightParameter = "Height";
|
|
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
// ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
// ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
#region Events
|
|
|
|
/// <summary>
|
|
/// Event triggered when the character jumps.
|
|
/// </summary>
|
|
public event System.Action OnJumpPerformed;
|
|
|
|
/// <summary>
|
|
/// Event triggered when the character jumps from the ground.
|
|
/// </summary>
|
|
public event System.Action<bool> OnGroundedJumpPerformed;
|
|
|
|
/// <summary>
|
|
/// Event triggered when the character jumps while.
|
|
/// </summary>
|
|
public event System.Action<int> OnNotGroundedJumpPerformed;
|
|
|
|
#endregion
|
|
|
|
|
|
protected MaterialController materialController = null;
|
|
protected int notGroundedJumpsLeft = 0;
|
|
protected bool isAllowedToCancelJump = false;
|
|
protected bool wantToRun = false;
|
|
protected float currentPlanarSpeedLimit = 0f;
|
|
|
|
protected bool groundedJumpAvailable = true;
|
|
protected Vector3 jumpDirection = default(Vector3);
|
|
|
|
protected Vector3 targetLookingDirection = default(Vector3);
|
|
protected float targetHeight = 1f;
|
|
|
|
protected bool wantToCrouch = false;
|
|
protected bool isCrouched = false;
|
|
|
|
protected PlanarMovementParameters.PlanarMovementProperties currentMotion = new PlanarMovementParameters.PlanarMovementProperties();
|
|
bool reducedAirControlFlag = false;
|
|
float reducedAirControlInitialTime = 0f;
|
|
float reductionDuration = 0.5f;
|
|
|
|
protected override void Awake()
|
|
{
|
|
base.Awake();
|
|
|
|
notGroundedJumpsLeft = verticalMovementParameters.availableNotGroundedJumps;
|
|
|
|
materialController = this.GetComponentInBranch<CharacterActor, MaterialController>();
|
|
}
|
|
|
|
protected virtual void OnValidate()
|
|
{
|
|
verticalMovementParameters.OnValidate();
|
|
}
|
|
|
|
protected override void Start()
|
|
{
|
|
base.Start();
|
|
|
|
targetHeight = CharacterActor.DefaultBodySize.y;
|
|
|
|
float minCrouchHeightRatio = CharacterActor.BodySize.x / CharacterActor.BodySize.y;
|
|
crouchParameters.heightRatio = Mathf.Max(minCrouchHeightRatio, crouchParameters.heightRatio);
|
|
}
|
|
|
|
protected virtual void OnEnable()
|
|
{
|
|
CharacterActor.OnTeleport += OnTeleport;
|
|
}
|
|
|
|
protected virtual void OnDisable()
|
|
{
|
|
CharacterActor.OnTeleport -= OnTeleport;
|
|
}
|
|
|
|
public override string GetInfo()
|
|
{
|
|
return "This state serves as a multi purpose movement based state. It is responsible for handling gravity and jump, walk and run, crouch, " +
|
|
"react to the different material properties, etc. Basically it covers all the common movements involved " +
|
|
"in a typical game, from a 3D platformer to a first person walking simulator.";
|
|
}
|
|
|
|
void OnTeleport(Vector3 position, Quaternion rotation)
|
|
{
|
|
targetLookingDirection = CharacterActor.Forward;
|
|
isAllowedToCancelJump = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets/Sets the useGravity toggle. Use this property to enable/disable the effect of gravity on the character.
|
|
/// </summary>
|
|
/// <value></value>
|
|
public bool UseGravity
|
|
{
|
|
get => verticalMovementParameters.useGravity;
|
|
set => verticalMovementParameters.useGravity = value;
|
|
}
|
|
|
|
public override void CheckExitTransition()
|
|
{
|
|
|
|
if (CharacterActions.jetPack.value)
|
|
{
|
|
CharacterStateController.EnqueueTransition<JetPack>();
|
|
}
|
|
else if (CharacterActions.dash.Started)
|
|
{
|
|
CharacterStateController.EnqueueTransition<Dash>();
|
|
}
|
|
else if (CharacterActor.Triggers.Count != 0)
|
|
{
|
|
CharacterStateController.EnqueueTransition<LadderClimbing>();
|
|
CharacterStateController.EnqueueTransition<RopeClimbing>();
|
|
}
|
|
else if (!CharacterActor.IsGrounded)
|
|
{
|
|
if (!CharacterActions.crouch.value)
|
|
CharacterStateController.EnqueueTransition<WallSlide>();
|
|
|
|
CharacterStateController.EnqueueTransition<LedgeHanging>();
|
|
}
|
|
}
|
|
|
|
public override void ExitBehaviour(float dt, CharacterState toState)
|
|
{
|
|
reducedAirControlFlag = false;
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Reduces the amount of acceleration and deceleration (not grounded state) until the character reaches the apex of the jump
|
|
/// (vertical velocity close to zero). This can be useful to prevent the character from accelerating/decelerating too quickly (e.g. right after performing a wall jump).
|
|
/// </summary>
|
|
public void ReduceAirControl(float reductionDuration = 0.5f)
|
|
{
|
|
reducedAirControlFlag = true;
|
|
reducedAirControlInitialTime = Time.time;
|
|
this.reductionDuration = reductionDuration;
|
|
}
|
|
|
|
void SetMotionValues(Vector3 targetPlanarVelocity)
|
|
{
|
|
float angleCurrentTargetVelocity = Vector3.Angle(CharacterActor.PlanarVelocity, targetPlanarVelocity);
|
|
|
|
switch (CharacterActor.CurrentState)
|
|
{
|
|
case CharacterActorState.StableGrounded:
|
|
|
|
currentMotion.acceleration = planarMovementParameters.stableGroundedAcceleration;
|
|
currentMotion.deceleration = planarMovementParameters.stableGroundedDeceleration;
|
|
currentMotion.angleAccelerationMultiplier = planarMovementParameters.stableGroundedAngleAccelerationBoost.Evaluate(angleCurrentTargetVelocity);
|
|
|
|
break;
|
|
|
|
case CharacterActorState.UnstableGrounded:
|
|
currentMotion.acceleration = planarMovementParameters.unstableGroundedAcceleration;
|
|
currentMotion.deceleration = planarMovementParameters.unstableGroundedDeceleration;
|
|
currentMotion.angleAccelerationMultiplier = planarMovementParameters.unstableGroundedAngleAccelerationBoost.Evaluate(angleCurrentTargetVelocity);
|
|
|
|
break;
|
|
|
|
case CharacterActorState.NotGrounded:
|
|
|
|
if (reducedAirControlFlag)
|
|
{
|
|
float time = Time.time - reducedAirControlInitialTime;
|
|
if (time <= reductionDuration)
|
|
{
|
|
currentMotion.acceleration = (planarMovementParameters.notGroundedAcceleration / reductionDuration) * time;
|
|
currentMotion.deceleration = (planarMovementParameters.notGroundedDeceleration / reductionDuration) * time;
|
|
}
|
|
else
|
|
{
|
|
reducedAirControlFlag = false;
|
|
|
|
currentMotion.acceleration = planarMovementParameters.notGroundedAcceleration;
|
|
currentMotion.deceleration = planarMovementParameters.notGroundedDeceleration;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
currentMotion.acceleration = planarMovementParameters.notGroundedAcceleration;
|
|
currentMotion.deceleration = planarMovementParameters.notGroundedDeceleration;
|
|
}
|
|
|
|
currentMotion.angleAccelerationMultiplier = planarMovementParameters.notGroundedAngleAccelerationBoost.Evaluate(angleCurrentTargetVelocity);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
// Material values
|
|
if (materialController != null)
|
|
{
|
|
if (CharacterActor.IsGrounded)
|
|
{
|
|
currentMotion.acceleration *= materialController.CurrentSurface.accelerationMultiplier * materialController.CurrentVolume.accelerationMultiplier;
|
|
currentMotion.deceleration *= materialController.CurrentSurface.decelerationMultiplier * materialController.CurrentVolume.decelerationMultiplier;
|
|
}
|
|
else
|
|
{
|
|
currentMotion.acceleration *= materialController.CurrentVolume.accelerationMultiplier;
|
|
currentMotion.deceleration *= materialController.CurrentVolume.decelerationMultiplier;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Processes the lateral movement of the character (stable and unstable state), that is, walk, run, crouch, etc.
|
|
/// This movement is tied directly to the "movement" character action.
|
|
/// </summary>
|
|
protected virtual void ProcessPlanarMovement(float dt)
|
|
{
|
|
//SetMotionValues();
|
|
|
|
float speedMultiplier = materialController != null ?
|
|
materialController.CurrentSurface.speedMultiplier * materialController.CurrentVolume.speedMultiplier : 1f;
|
|
|
|
|
|
bool needToAccelerate = CustomUtilities.Multiply(CharacterStateController.InputMovementReference, currentPlanarSpeedLimit).sqrMagnitude >= CharacterActor.PlanarVelocity.sqrMagnitude;
|
|
|
|
Vector3 targetPlanarVelocity = default;
|
|
switch (CharacterActor.CurrentState)
|
|
{
|
|
case CharacterActorState.NotGrounded:
|
|
|
|
if (CharacterActor.WasGrounded)
|
|
currentPlanarSpeedLimit = Mathf.Max(CharacterActor.PlanarVelocity.magnitude, planarMovementParameters.baseSpeedLimit);
|
|
|
|
|
|
//needToAccelerate = CustomUtilities.Multiply(CharacterStateController.InputMovementReference, currentPlanarSpeedLimit).sqrMagnitude >= CharacterActor.PlanarVelocity.sqrMagnitude;
|
|
targetPlanarVelocity = CustomUtilities.Multiply(CharacterStateController.InputMovementReference, speedMultiplier, currentPlanarSpeedLimit);
|
|
|
|
//GetAccelerationBoost(targetPlanarVelocity)
|
|
break;
|
|
case CharacterActorState.StableGrounded:
|
|
|
|
|
|
// Run ------------------------------------------------------------
|
|
if (planarMovementParameters.runInputMode == InputMode.Toggle)
|
|
{
|
|
if (CharacterActions.run.Started)
|
|
wantToRun = !wantToRun;
|
|
}
|
|
else
|
|
{
|
|
wantToRun = CharacterActions.run.value;
|
|
}
|
|
|
|
if (wantToCrouch || !planarMovementParameters.canRun)
|
|
wantToRun = false;
|
|
|
|
|
|
if (isCrouched)
|
|
{
|
|
currentPlanarSpeedLimit = planarMovementParameters.baseSpeedLimit * crouchParameters.speedMultiplier;
|
|
}
|
|
else
|
|
{
|
|
currentPlanarSpeedLimit = wantToRun ? planarMovementParameters.boostSpeedLimit : planarMovementParameters.baseSpeedLimit;
|
|
}
|
|
|
|
targetPlanarVelocity = CustomUtilities.Multiply(CharacterStateController.InputMovementReference, speedMultiplier, currentPlanarSpeedLimit);
|
|
|
|
//needToAccelerate = CharacterStateController.InputMovementReference != Vector3.zero;
|
|
|
|
|
|
break;
|
|
case CharacterActorState.UnstableGrounded:
|
|
|
|
currentPlanarSpeedLimit = planarMovementParameters.baseSpeedLimit;
|
|
|
|
//needToAccelerate = CustomUtilities.Multiply(CharacterStateController.InputMovementReference, currentPlanarSpeedLimit).sqrMagnitude >= CharacterActor.PlanarVelocity.sqrMagnitude;
|
|
targetPlanarVelocity = CustomUtilities.Multiply(CharacterStateController.InputMovementReference, speedMultiplier, currentPlanarSpeedLimit);
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
SetMotionValues(targetPlanarVelocity);
|
|
|
|
|
|
float acceleration = currentMotion.acceleration;
|
|
if (needToAccelerate)
|
|
{
|
|
acceleration *= currentMotion.angleAccelerationMultiplier;
|
|
|
|
// Affect acceleration based on the angle between target velocity and current velocity
|
|
//float angleCurrentTargetVelocity = Vector3.Angle(CharacterActor.PlanarVelocity, targetPlanarVelocity);
|
|
//float accelerationBoost = 20f * (angleCurrentTargetVelocity / 180f);
|
|
//acceleration += accelerationBoost;
|
|
}
|
|
else
|
|
{
|
|
acceleration = currentMotion.deceleration;
|
|
}
|
|
|
|
CharacterActor.PlanarVelocity = Vector3.MoveTowards(
|
|
CharacterActor.PlanarVelocity,
|
|
targetPlanarVelocity,
|
|
acceleration * dt
|
|
);
|
|
}
|
|
|
|
|
|
|
|
protected virtual void ProcessGravity(float dt)
|
|
{
|
|
if (!verticalMovementParameters.useGravity)
|
|
return;
|
|
|
|
|
|
verticalMovementParameters.UpdateParameters();
|
|
|
|
|
|
float gravityMultiplier = 1f;
|
|
|
|
if (materialController != null)
|
|
gravityMultiplier = CharacterActor.LocalVelocity.y >= 0 ?
|
|
materialController.CurrentVolume.gravityAscendingMultiplier :
|
|
materialController.CurrentVolume.gravityDescendingMultiplier;
|
|
|
|
float gravity = gravityMultiplier * verticalMovementParameters.gravity;
|
|
|
|
|
|
if (!CharacterActor.IsStable)
|
|
CharacterActor.VerticalVelocity += CustomUtilities.Multiply(-CharacterActor.Up, gravity, dt);
|
|
|
|
|
|
}
|
|
|
|
|
|
protected bool UnstableGroundedJumpAvailable => !verticalMovementParameters.canJumpOnUnstableGround && CharacterActor.CurrentState == CharacterActorState.UnstableGrounded;
|
|
|
|
|
|
|
|
public enum JumpResult
|
|
{
|
|
Invalid,
|
|
Grounded,
|
|
NotGrounded
|
|
}
|
|
|
|
JumpResult CanJump()
|
|
{
|
|
JumpResult jumpResult = JumpResult.Invalid;
|
|
|
|
if (!verticalMovementParameters.canJump)
|
|
return jumpResult;
|
|
|
|
if (isCrouched)
|
|
return jumpResult;
|
|
|
|
|
|
switch (CharacterActor.CurrentState)
|
|
{
|
|
case CharacterActorState.StableGrounded:
|
|
|
|
if (CharacterActions.jump.StartedElapsedTime <= verticalMovementParameters.preGroundedJumpTime && groundedJumpAvailable)
|
|
jumpResult = JumpResult.Grounded;
|
|
|
|
break;
|
|
case CharacterActorState.NotGrounded:
|
|
|
|
if (CharacterActions.jump.Started)
|
|
{
|
|
// First check if the "grounded jump" is available. If so, execute a "coyote jump".
|
|
if (CharacterActor.NotGroundedTime <= verticalMovementParameters.postGroundedJumpTime && groundedJumpAvailable)
|
|
{
|
|
jumpResult = JumpResult.Grounded;
|
|
}
|
|
else if (notGroundedJumpsLeft != 0) // Do a not grounded jump
|
|
{
|
|
jumpResult = JumpResult.NotGrounded;
|
|
}
|
|
}
|
|
|
|
break;
|
|
case CharacterActorState.UnstableGrounded:
|
|
|
|
if (CharacterActions.jump.StartedElapsedTime <= verticalMovementParameters.preGroundedJumpTime && verticalMovementParameters.canJumpOnUnstableGround)
|
|
jumpResult = JumpResult.Grounded;
|
|
|
|
break;
|
|
}
|
|
|
|
return jumpResult;
|
|
}
|
|
|
|
|
|
|
|
protected virtual void ProcessJump(float dt)
|
|
{
|
|
ProcessRegularJump(dt);
|
|
ProcessJumpDown(dt);
|
|
}
|
|
|
|
#region JumpDown
|
|
|
|
protected virtual bool ProcessJumpDown(float dt)
|
|
{
|
|
if (!verticalMovementParameters.canJumpDown)
|
|
return false;
|
|
|
|
if (!CharacterActor.IsStable)
|
|
return false;
|
|
|
|
if (!CharacterActor.IsGroundAOneWayPlatform)
|
|
return false;
|
|
|
|
if (verticalMovementParameters.filterByTag)
|
|
{
|
|
if (!CharacterActor.GroundObject.CompareTag(verticalMovementParameters.jumpDownTag))
|
|
return false;
|
|
}
|
|
|
|
if (!ProcessJumpDownAction())
|
|
return false;
|
|
|
|
JumpDown(dt);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
protected virtual bool ProcessJumpDownAction()
|
|
{
|
|
return isCrouched && CharacterActions.jump.Started;
|
|
}
|
|
|
|
|
|
protected virtual void JumpDown(float dt)
|
|
{
|
|
|
|
float groundDisplacementExtraDistance = 0f;
|
|
|
|
Vector3 groundDisplacement = CustomUtilities.Multiply(CharacterActor.GroundVelocity, dt);
|
|
// bool CharacterActor.transform.InverseTransformVectorUnscaled( Vector3.Project( groundDisplacement , CharacterActor.Up ) ).y
|
|
|
|
if (!CharacterActor.IsGroundAscending)
|
|
groundDisplacementExtraDistance = groundDisplacement.magnitude;
|
|
|
|
CharacterActor.ForceNotGrounded();
|
|
|
|
CharacterActor.Position -=
|
|
CustomUtilities.Multiply(
|
|
CharacterActor.Up,
|
|
CharacterConstants.ColliderMinBottomOffset + verticalMovementParameters.jumpDownDistance + groundDisplacementExtraDistance
|
|
);
|
|
|
|
CharacterActor.VerticalVelocity -= CustomUtilities.Multiply(CharacterActor.Up, verticalMovementParameters.jumpDownVerticalVelocity);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Jump
|
|
|
|
protected virtual void ProcessRegularJump(float dt)
|
|
{
|
|
if (CharacterActor.IsGrounded)
|
|
{
|
|
notGroundedJumpsLeft = verticalMovementParameters.availableNotGroundedJumps;
|
|
|
|
groundedJumpAvailable = true;
|
|
}
|
|
|
|
|
|
if (isAllowedToCancelJump)
|
|
{
|
|
if (verticalMovementParameters.cancelJumpOnRelease)
|
|
{
|
|
if (CharacterActions.jump.StartedElapsedTime >= verticalMovementParameters.cancelJumpMaxTime || CharacterActor.IsFalling)
|
|
{
|
|
isAllowedToCancelJump = false;
|
|
}
|
|
else if (!CharacterActions.jump.value && CharacterActions.jump.StartedElapsedTime >= verticalMovementParameters.cancelJumpMinTime)
|
|
{
|
|
// Get the velocity mapped onto the current jump direction
|
|
Vector3 projectedJumpVelocity = Vector3.Project(CharacterActor.Velocity, jumpDirection);
|
|
|
|
CharacterActor.Velocity -= CustomUtilities.Multiply(projectedJumpVelocity, 1f - verticalMovementParameters.cancelJumpMultiplier);
|
|
|
|
isAllowedToCancelJump = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
JumpResult jumpResult = CanJump();
|
|
|
|
switch (jumpResult)
|
|
{
|
|
case JumpResult.Grounded:
|
|
groundedJumpAvailable = false;
|
|
|
|
break;
|
|
case JumpResult.NotGrounded:
|
|
notGroundedJumpsLeft--;
|
|
|
|
break;
|
|
|
|
case JumpResult.Invalid:
|
|
return;
|
|
}
|
|
|
|
// Events ---------------------------------------------------
|
|
if (CharacterActor.IsGrounded)
|
|
{
|
|
|
|
if (OnGroundedJumpPerformed != null)
|
|
OnGroundedJumpPerformed(true);
|
|
}
|
|
else
|
|
{
|
|
if (OnNotGroundedJumpPerformed != null)
|
|
OnNotGroundedJumpPerformed(notGroundedJumpsLeft);
|
|
}
|
|
|
|
if (OnJumpPerformed != null)
|
|
OnJumpPerformed();
|
|
|
|
// Define the jump direction ---------------------------------------------------
|
|
jumpDirection = SetJumpDirection();
|
|
|
|
// Force "not grounded" state.
|
|
if (CharacterActor.IsGrounded)
|
|
CharacterActor.ForceNotGrounded();
|
|
|
|
// First remove any velocity associated with the jump direction.
|
|
CharacterActor.Velocity -= Vector3.Project(CharacterActor.Velocity, jumpDirection);
|
|
CharacterActor.Velocity += CustomUtilities.Multiply(jumpDirection, verticalMovementParameters.jumpSpeed);
|
|
|
|
if (verticalMovementParameters.cancelJumpOnRelease)
|
|
isAllowedToCancelJump = true;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the jump direction vector whenever the jump action is started.
|
|
/// </summary>
|
|
protected virtual Vector3 SetJumpDirection()
|
|
{
|
|
return CharacterActor.Up;
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
void ProcessVerticalMovement(float dt)
|
|
{
|
|
ProcessGravity(dt);
|
|
ProcessJump(dt);
|
|
}
|
|
|
|
|
|
public override void EnterBehaviour(float dt, CharacterState fromState)
|
|
{
|
|
CharacterActor.alwaysNotGrounded = false;
|
|
|
|
targetLookingDirection = CharacterActor.Forward;
|
|
|
|
if (fromState == CharacterStateController.GetState<WallSlide>())
|
|
{
|
|
// "availableNotGroundedJumps + 1" because the update code will consume one jump!
|
|
notGroundedJumpsLeft = verticalMovementParameters.availableNotGroundedJumps + 1;
|
|
|
|
// Reduce the amount of air control (acceleration and deceleration) for 0.5 seconds.
|
|
ReduceAirControl(0.5f);
|
|
}
|
|
|
|
currentPlanarSpeedLimit = Mathf.Max(CharacterActor.PlanarVelocity.magnitude, planarMovementParameters.baseSpeedLimit);
|
|
|
|
CharacterActor.UseRootMotion = false;
|
|
}
|
|
|
|
protected virtual void HandleRotation(float dt)
|
|
{
|
|
HandleLookingDirection(dt);
|
|
}
|
|
|
|
void HandleLookingDirection(float dt)
|
|
{
|
|
if (!lookingDirectionParameters.changeLookingDirection)
|
|
return;
|
|
|
|
switch (lookingDirectionParameters.lookingDirectionMode)
|
|
{
|
|
case LookingDirectionParameters.LookingDirectionMode.Movement:
|
|
|
|
switch (CharacterActor.CurrentState)
|
|
{
|
|
case CharacterActorState.NotGrounded:
|
|
|
|
SetTargetLookingDirection(lookingDirectionParameters.notGroundedLookingDirectionMode);
|
|
|
|
break;
|
|
case CharacterActorState.StableGrounded:
|
|
|
|
SetTargetLookingDirection(lookingDirectionParameters.stableGroundedLookingDirectionMode);
|
|
|
|
break;
|
|
case CharacterActorState.UnstableGrounded:
|
|
|
|
SetTargetLookingDirection(lookingDirectionParameters.unstableGroundedLookingDirectionMode);
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case LookingDirectionParameters.LookingDirectionMode.ExternalReference:
|
|
|
|
if (!CharacterActor.CharacterBody.Is2D)
|
|
targetLookingDirection = CharacterStateController.MovementReferenceForward;
|
|
|
|
break;
|
|
|
|
case LookingDirectionParameters.LookingDirectionMode.Target:
|
|
|
|
targetLookingDirection = (lookingDirectionParameters.target.position - CharacterActor.Position);
|
|
targetLookingDirection.Normalize();
|
|
|
|
break;
|
|
}
|
|
|
|
Quaternion targetDeltaRotation = Quaternion.FromToRotation(CharacterActor.Forward, targetLookingDirection);
|
|
Quaternion currentDeltaRotation = Quaternion.Slerp(Quaternion.identity, targetDeltaRotation, lookingDirectionParameters.speed * dt);
|
|
|
|
if (CharacterActor.CharacterBody.Is2D)
|
|
CharacterActor.SetYaw(targetLookingDirection);
|
|
else
|
|
CharacterActor.SetYaw(currentDeltaRotation * CharacterActor.Forward);
|
|
}
|
|
|
|
void SetTargetLookingDirection(LookingDirectionParameters.LookingDirectionMovementSource lookingDirectionMode)
|
|
{
|
|
if (lookingDirectionMode == LookingDirectionParameters.LookingDirectionMovementSource.Input)
|
|
{
|
|
if (CharacterStateController.InputMovementReference != Vector3.zero)
|
|
targetLookingDirection = CharacterStateController.InputMovementReference;
|
|
else
|
|
targetLookingDirection = CharacterActor.Forward;
|
|
}
|
|
else
|
|
{
|
|
if (CharacterActor.PlanarVelocity != Vector3.zero)
|
|
targetLookingDirection = Vector3.ProjectOnPlane(CharacterActor.PlanarVelocity, CharacterActor.Up);
|
|
else
|
|
targetLookingDirection = CharacterActor.Forward;
|
|
}
|
|
}
|
|
|
|
public override void UpdateBehaviour(float dt)
|
|
{
|
|
HandleSize(dt);
|
|
HandleVelocity(dt);
|
|
HandleRotation(dt);
|
|
}
|
|
|
|
|
|
public override void PreCharacterSimulation(float dt)
|
|
{
|
|
// Pre/PostCharacterSimulation methods are useful to update all the Animator parameters.
|
|
// Why? Because the CharacterActor component will end up modifying the velocity of the actor.
|
|
if (!CharacterActor.IsAnimatorValid())
|
|
return;
|
|
|
|
CharacterStateController.Animator.SetBool(groundedParameter, CharacterActor.IsGrounded);
|
|
CharacterStateController.Animator.SetBool(stableParameter, CharacterActor.IsStable);
|
|
CharacterStateController.Animator.SetFloat(horizontalAxisParameter, CharacterActions.movement.value.x);
|
|
CharacterStateController.Animator.SetFloat(verticalAxisParameter, CharacterActions.movement.value.y);
|
|
CharacterStateController.Animator.SetFloat(heightParameter, CharacterActor.BodySize.y);
|
|
}
|
|
|
|
public override void PostCharacterSimulation(float dt)
|
|
{
|
|
// Pre/PostCharacterSimulation methods are useful to update all the Animator parameters.
|
|
// Why? Because the CharacterActor component will end up modifying the velocity of the actor.
|
|
if (!CharacterActor.IsAnimatorValid())
|
|
return;
|
|
|
|
// Parameters associated with velocity are sent after the simulation.
|
|
// The PostSimulationUpdate (CharacterActor) might update velocity once more (e.g. if a "bad step" has been detected).
|
|
CharacterStateController.Animator.SetFloat(verticalSpeedParameter, CharacterActor.LocalVelocity.y);
|
|
CharacterStateController.Animator.SetFloat(planarSpeedParameter, CharacterActor.PlanarVelocity.magnitude);
|
|
}
|
|
|
|
protected virtual void HandleSize(float dt)
|
|
{
|
|
// Get the crouch input state
|
|
if (crouchParameters.enableCrouch)
|
|
{
|
|
if (crouchParameters.inputMode == InputMode.Toggle)
|
|
{
|
|
if (CharacterActions.crouch.Started)
|
|
wantToCrouch = !wantToCrouch;
|
|
}
|
|
else
|
|
{
|
|
wantToCrouch = CharacterActions.crouch.value;
|
|
}
|
|
|
|
if (!crouchParameters.notGroundedCrouch && !CharacterActor.IsGrounded)
|
|
wantToCrouch = false;
|
|
|
|
if (CharacterActor.IsGrounded && wantToRun)
|
|
wantToCrouch = false;
|
|
}
|
|
else
|
|
{
|
|
wantToCrouch = false;
|
|
}
|
|
|
|
if (wantToCrouch)
|
|
Crouch(dt);
|
|
else
|
|
StandUp(dt);
|
|
}
|
|
|
|
void Crouch(float dt)
|
|
{
|
|
CharacterActor.SizeReferenceType sizeReferenceType = CharacterActor.IsGrounded ?
|
|
CharacterActor.SizeReferenceType.Bottom : crouchParameters.notGroundedReference;
|
|
|
|
bool validSize = CharacterActor.CheckAndInterpolateHeight(
|
|
CharacterActor.DefaultBodySize.y * crouchParameters.heightRatio,
|
|
crouchParameters.sizeLerpSpeed * dt, sizeReferenceType);
|
|
|
|
if (validSize)
|
|
isCrouched = true;
|
|
}
|
|
|
|
void StandUp(float dt)
|
|
{
|
|
CharacterActor.SizeReferenceType sizeReferenceType = CharacterActor.IsGrounded ?
|
|
CharacterActor.SizeReferenceType.Bottom : crouchParameters.notGroundedReference;
|
|
|
|
bool validSize = CharacterActor.CheckAndInterpolateHeight(
|
|
CharacterActor.DefaultBodySize.y,
|
|
crouchParameters.sizeLerpSpeed * dt, sizeReferenceType);
|
|
|
|
if (validSize)
|
|
isCrouched = false;
|
|
}
|
|
|
|
|
|
protected virtual void HandleVelocity(float dt)
|
|
{
|
|
ProcessVerticalMovement(dt);
|
|
ProcessPlanarMovement(dt);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|