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