using System; using UnityEngine; using Framework.Input; using Framework.Layers; namespace Framework.Actors.Platformer { /// Move that can be performed by the player public class PlayerMove : PlatformerMove { /// Called when the actor has started the move public override void OnStarted() { base.OnStarted(); SetPresenterState(); } /// /// Switches the presenter to the animation state appropriate for this move /// /// /// This is called when the move becomes active. It is typically used to trigger /// the animation related to the move (or change exposed variables in Mecanim /// to switch to the correct animation state). /// protected virtual void SetPresenterState() {} /// Injects the instance's dependencies /// /// Input selector the instance uses to determine the active input source /// /// /// This method is called automatically by the services framework. Any parameters /// it requires will be filled out by looking up the respective services and creating /// them as needed. /// protected void Inject(IInputManager inputManager) { this.inputManager = inputManager; } /// Called once when the movement is assigned to an actor /// Actor the movement has been assigned to protected override void Awake(ActorController actor) { base.Awake(actor); this.moveRepository = actor.GetComponent(); this.abilities = ActorController.Abilities; } /// Abilities the actor possesses protected Abilities Abilities { get { return this.abilities; } } /// Input manager from which the input state can be queried protected IInputManager InputManager { get { return this.inputManager; } } /// Adds a jump impulse to the actor /// Apex height the actor should achieve from the jump protected void GiveVerticalJumpImpulse(float apexHeight) { Vector3 groundVelocity = ActorController.GroundChecker.GetMostRecentGroundVelocity(); float gravityStrength = Physics.gravity.magnitude; if(Rigidbody != null) { float jumpImpulse = PhysicsHelper.GetJumpOffImpulse( gravityStrength, apexHeight, Time.fixedDeltaTime ); float relativeImpulse = groundVelocity.y + jumpImpulse; relativeImpulse -= Rigidbody.velocity.y; Rigidbody.AddForce( new Vector3(0.0f, relativeImpulse * Rigidbody.mass, 0.0f), ForceMode.Impulse ); } else if(ActorPhysics != null) { gravityStrength *= ActorPhysics.GravityScale; float jumpImpulse = PhysicsHelper.GetJumpOffImpulse( gravityStrength, apexHeight, Time.fixedDeltaTime ); float relativeImpulse = groundVelocity.y + jumpImpulse; relativeImpulse -= ActorPhysics.Velocity.y; ActorPhysics.QueueImpulse( new Vector3(0.0f, relativeImpulse * ActorPhysics.Mass, 0.0f) ); } else { Debug.LogError( "No Rigidbody and no ActorPhysics component present, cannot give jump impulse." ); } } /// Sets the vertical velocity of the actor to zero protected void ClearVerticalVelocity() { if(Rigidbody != null) { Vector3 velocity = Rigidbody.velocity; velocity.y = 0.0f; Rigidbody.velocity = velocity; } else if(ActorPhysics != null) { Vector3 velocity = ActorPhysics.Velocity; velocity.y = 0.0f; ActorPhysics.Velocity = velocity; } else { Debug.LogError( "No Rigidbody and no ActorPhysics component present, cannot clear vertical velocity." ); } } /// Changes the character's velocity to the specified velocity /// Velocity the character will assume protected void ChangeVelocity(float targetVelocity) { if(Rigidbody != null) { float currentVelocity = Rigidbody.velocity.x; float impulse = (targetVelocity - currentVelocity) * Rigidbody.mass; Rigidbody.AddForce(new Vector3(impulse, 0.0f, 0.0f), ForceMode.Impulse); } else if(ActorPhysics != null) { float currentVelocity = ActorPhysics.Velocity.x; float impulse = (targetVelocity - currentVelocity) * ActorPhysics.Mass; ActorPhysics.QueueImpulse(new Vector3(impulse, 0.0f, 0.0f)); } else { Debug.LogError( "No Rigidbody and no ActorPhysics component present, cannot set velocity." ); } } /// Accelerates the character to the specified target velocity /// Velocity the character should reach /// Acceleration in units per second squared protected void AccelerateToVelocity(float targetVelocity, float acceleration) { float deltaTime = Time.fixedDeltaTime; if(Rigidbody != null) { float currentVelocity = Rigidbody.velocity.x; // Get force that would be required to achieve target velocity instantly // and limit it to the acceleration specified by the caller float force = (targetVelocity - currentVelocity) / deltaTime; force = Mathf.Clamp(force, -acceleration, acceleration); force *= Rigidbody.mass; Rigidbody.AddForce(new Vector3(force, 0.0f, 0.0f), ForceMode.Force); } else if(ActorPhysics != null) { float currentVelocity = ActorPhysics.Velocity.x; // Get force that would be required to achieve target velocity instantly // and limit it to the acceleration specified by the caller float force = (targetVelocity - currentVelocity) / deltaTime; force = Mathf.Clamp(force, -acceleration, acceleration); force *= ActorPhysics.Mass; ActorPhysics.QueueForce(new Vector3(force, 0.0f, 0.0f)); } else { Debug.LogError( "No Rigidbody and no ActorPhysics component present, cannot accelerate." ); } } /// Enables or disables gravity for the actor /// True to enable gravity, false to disable it protected void EnableGravity(bool active = true) { if(Rigidbody != null) { Rigidbody.useGravity = active; } else if(ActorPhysics != null) { ActorPhysics.IsAffectedByGravity = active; } else { Debug.LogError( "No Rigidbody and no ActorPhysics component present, cannot toggle gravity." ); } } /// /// Uses the GroundChecker component to update the grounded state of the actor /// protected bool RecheckGrounding() { if(ActorPhysics != null) { return ActorController.GroundChecker.CheckIfGrounded(ActorPhysics) || ActorPhysics.UnreliableIsGrounded; } else { Collider collider = ActorController.GetComponent(); if(collider != null) { int layerMask = LayerMaskHelper.GetLayerCollisionMaskFor( ActorController.gameObject.layer ); return ActorController.GroundChecker.CheckIfGrounded(collider, layerMask); } } return false; } /// Checks whether the actor is standing on solid ground protected bool IsGrounded { get { if(ActorPhysics != null) { return ActorController.GroundChecker.WasGroundedInMostRecentCheck || ActorPhysics.UnreliableIsGrounded; } else { return ActorController.GroundChecker.WasGroundedInMostRecentCheck; } } } /// Retrieves the current velocity of the actor /// The actor's current velocity protected Vector3 GetVelocity() { if(Rigidbody != null) { return Rigidbody.velocity; } else if(ActorPhysics != null) { return ActorPhysics.Velocity; } else { Debug.LogError( "No Rigidbody and no ActorPhysics component present, velocity retrieval impossible" ); return Vector3.zero; } } /// Retrieves the current vertical velocity of the actor /// The actor's current vertical velocity protected float GetVerticalVelocity() { if(Rigidbody != null) { return Rigidbody.velocity.y; } else if(ActorPhysics != null) { return ActorPhysics.Velocity.y; } else { Debug.LogError( "No Rigidbody and no ActorPhysics component present, velocity check impossible" ); return float.NaN; } } /// Switches the actor controller to the ground move /// Velocity the actor had when hitting the ground protected void SwitchToGroundMove(float impactVelocity = 0.0f) { Move groundMode; if(this.moveRepository == null) { groundMode = new GroundMove(); } else { groundMode = this.moveRepository.GetGroundMove(); } ActorController.ActiveMove = groundMode; } /// Switches the actor controller to the air move /// Whether the character has jumped protected void SwitchToAirMove(bool jumped) { Move airMove; if(this.moveRepository == null) { airMove = new AirMove() { WasActivatedByJumping = jumped }; } else { airMove = this.moveRepository.GetAirMove(jumped); } ActorController.ActiveMove = airMove; } /// Switches the actor controller to the dash move protected void SwitchToDashMove() { Move dashMove; if(this.moveRepository == null) { dashMove = new DashMove(); } else { dashMove = this.moveRepository.GetDashMove(); } ActorController.ActiveMove = dashMove; } /// Switches the actor controller to the squat move protected void SwitchToSquatMove() { Move squatMove; if(this.moveRepository == null) { squatMove = new SquatMove(); } else { squatMove = this.moveRepository.GetSquatMove(); } ActorController.ActiveMove = squatMove; } /// Switches the actor controller to the squat move protected void SwitchToSlideMove() { Move slideMove; if(this.moveRepository == null) { slideMove = new SlideMove(); } else { slideMove = this.moveRepository.GetSlideMove(); } ActorController.ActiveMove = slideMove; } /// Repository storing other moves to which this one can switch private PlayerMoveRepository moveRepository; /// Input manager from which inputs states will be queried private IInputManager inputManager; /// Defines the abilities of the platformer actor private Abilities abilities; } } // namespace Framework.Actors.Platformer