using System;
using Framework.Input.States;
using UnityEngine;
namespace Framework.Actors.Platformer {
///
/// Moves the actor forward for a certain distance or until an obstacle is hit
///
public class DashMove : PlayerMove {
/// Called when the actor has started the move
public override void OnStarted() {
base.OnStarted();
this.isFirstFrame = true;
this.endedByJump = false;
// Calculate how long the dash will take. We dash based on time
// rather than distance because we don't want the dash to be an
// all-purpose momentum canceller.
this.dashDuration = Abilities.DashDistance / Abilities.DashingSpeed;
this.elapsedDashTime = 0.0f;
EnableGravity(false);
ClearVerticalVelocity();
}
/// Called when the move has completed or was interrupted
public override void OnEnded() {
OnStopDash(this.endedByJump);
base.OnEnded();
}
/// Called once per physics frame
///
/// Updates the world position of the actor
///
public override void FixedUpdate() {
base.FixedUpdate();
// If this is the first dash frame, calculate the dash velocity
// and duration
if(this.isFirstFrame) {
// Initialize the dash timer and calculate dash velocity
OnBeginDash();
this.isFirstFrame = false;
} else {
// See if we hit something (some actual collision tests would be nice, too,
// but velocity is the safest way to tell if the actor hit something).
float currentSquaredVelocity = GetVelocity().sqrMagnitude;
bool hitSomething =
(currentSquaredVelocity < this.lastFrameSquaredVelocity / 2.0f) ||
(currentSquaredVelocity < this.dashVelocity / 2.0f);
this.lastFrameSquaredVelocity = currentSquaredVelocity;
// If we hit something while dashing, stop the dash without accelerating
if(hitSomething) {
switchToNonDashMove();
ActorController.ActiveMove.FixedUpdate();
return;
}
}
// Nothing hit, maintain the actor's horizontal velocity
ChangeVelocity(this.dashVelocity);
ClearVerticalVelocity();
PlatformerInputState inputState = InputManager.GetState();
// If the player presses the jump key while dashing, jump keeping
// the full force of the jump
if(inputState.Jump.WasTriggered) {
if(ActorController.ConsecutiveJumpCount < Abilities.JumpCount) {
this.endedByJump = true;
SwitchToAirMove(jumped: true);
return;
}
}
// Could also remember the start time, but then accuracy would get incrementally
// worse as the game is running (this, on the other hand, will suffer from
// accuracy issues, but since delta time is fixed, they will be minimal and
// identical each dash).
this.elapsedDashTime += Time.fixedDeltaTime;
// If the dash is over, return to normal movement
if(this.elapsedDashTime >= this.dashDuration) {
switchToNonDashMove();
}
}
///
/// Switches the presenter to the animation state appropriate for this move
///
protected override void SetPresenterState() {
PlatformerActorPresenter presenter = ActorPresenter;
if(presenter != null) {
presenter.SetDashState();
}
}
/// Switches to the appropriate move to use when the dash ends
private void switchToNonDashMove() {
bool isGrounded = RecheckGrounding();
if(isGrounded) {
SwitchToGroundMove();
} else {
SwitchToAirMove(jumped: false);
}
}
/// Called to set up the dash
protected virtual void OnBeginDash() {
this.dashVelocity = ActorPhysics.Velocity.x;
// If the actor dashes in reverse to the direction it is moving,
// we allow an instant reversal of direction up to the dashing speed,
// but not above.
if(ActorController.FacingDirection < 0.0f) {
if(dashVelocity > 0.0f) {
dashVelocity -= Math.Min(dashVelocity, Abilities.DashingSpeed);
}
dashVelocity -= Abilities.DashingSpeed;
} else if(ActorController.FacingDirection > 0.0f) {
if(dashVelocity < 0.0f) {
dashVelocity += Math.Max(dashVelocity, -Abilities.DashingSpeed);
}
dashVelocity += Abilities.DashingSpeed;
}
// Determine maximum speed
float maximumSpeed;
{
if(Abilities.CanSprint) {
maximumSpeed = Abilities.RunningSpeed;
} else {
maximumSpeed = Abilities.WalkingSpeed;
}
}
// When the actor comes out of the dash, we want it to decrease
// to normal runnign speed (or if he was already moving faster than
// dashing, counter the dash force back to the level of running).
this.undashedVelocity = this.dashVelocity;
this.undashedVelocity -= (
Abilities.DashingSpeed - maximumSpeed
) * Math.Sign(this.undashedVelocity);
}
/// Called to end the dash
protected virtual void OnStopDash(bool endedByJump) {
if(!this.endedByJump) {
// Counter the dashing velocity by the amount it would take to
// return to running velocity (works even if the actor being thrown
// about at a much higher velocity and used the dash during this).
ChangeVelocity(this.undashedVelocity);
}
}
/// Whether the next update is the first dash frame
private bool isFirstFrame;
/// Whether the dash was broken by a jump
private bool endedByJump;
/// Time the actor has spend in dash mode
private float elapsedDashTime;
/// How long the player will spend in dash mdoe
private float dashDuration;
/// Horizontal velocity the actor should have while dashing
private float dashVelocity;
/// Horizontal velocity the actor had before it was dashing
private float undashedVelocity;
/// Velocity (squared) the actor had in the last frame
private float lastFrameSquaredVelocity;
}
} // namespace Framework.Actors.Platformer