using System; using UnityEngine; using Framework.Actors; namespace Framework.Actors.Platformer { /// Manages the visual state of a character in a platformer game public class PlatformerActorPresenter : ActorPresenter, IWalkingActorPresenter { /// Whether the presenter should manage the character's direction /// /// If this is enabled, the character will be facing either left, center or /// right and any custom rotations will likely be lost (depending on script /// update order). For characters that don't just move left or right but freely /// rotate, set this to false. /// public bool ManageDirection = true; /// Direction the actor is looking at /// > /// This value works like a normalized angle: /// -1.0 means the actor is facing left /// 0.0 means the actor is facing the camera /// 1.0 means the actor is facing right /// public float Direction = 0.0f; /// How quickly the character will turn to face the desired direction /// /// A rate of 4 would mean that the actor turns by 4 units (quarter turns) /// per second, requiring 1/4th of a second to turn from the camera to a side /// and 2/4ths to turn from one side to the other. /// public float TurnRate = 10.0f; /// Speed at which the actor is moving horizontally in units/second public float HorizontalVelocity; /// Speed at which the actor is moving vertically in units/second public float VerticalVelocity; /// Orients the character to face the left side (negative X) public void FaceLeft() { Direction = -1.0f; } /// Orients the character to face the left side (negative X) /// Whether to turn without interpolation public void FaceLeft(bool instantly) { Direction = -1.0f; if(instantly) { this.direction = -1.0f; } } /// Orients the character to face the right side (positive X) public void FaceRight() { Direction = +1.0f; } /// Orients the character to face the right side (positive X) /// Whether to turn without interpolation public void FaceRight(bool instantly) { Direction = +1.0f; if(instantly) { this.direction = +1.0f; } } /// Orients the character to face the camera public void FaceCamera() { Direction = 0.0f; } /// Shows the actor during normal ground movement /// /// Override this to transition your actor's animations into the grounded state, /// used while standing on solid ground, walking and running. /// public virtual void SetGroundedState() { State = 0; } /// Shows the actor while it is airborne /// /// Override this to show your actor while it is airborne. Using different /// animations depending on the actor's vertical velocity is recommended. /// public virtual void SetAirState() { State = 1; } /// Shows the actor squatting while standing still /// /// This is a lowered stance often used to make actors shoot enemies /// that wouldn't be hit from an upright stance. If sneaking is /// enabled, you may want to use the same animation state. /// public virtual void SetSquatState() { State = 2; } /// Shows the actor sneaking in a lowered stance /// /// This is a lowered stance often used to indicate silent / covert /// movement. Can be shared with the squat animation. /// public virtual void SetSneakState() { State = 3; } /// Shows the actor crawling on all fours /// /// Allows the actor to crawl through tight spaces. Can be used for /// covert movement, too. /// public virtual void SetCrawlState() { State = 4; } /// Shows the actor dashing across the ground /// /// Dashing is very quick forward movement used for evasion and to make /// extra far jumps. This can be a rocket-propelled forward lunge as seen /// in Megaman titles or a sideways jump as seen in Castlevania games, /// for example. /// public virtual void SetDashState() { State = 5; } /// Shows the actor sliding over the ground /// /// This is a more realistic variant of the dash. Rather than keeping /// speed and ignoring gravity, a slide works like in an action move, /// the actor throws itself to the ground and covers a limited distance /// while rapidly losing momentum. /// public virtual void SetSlideState() { State = 6; } /// Called once per physics update frame protected virtual void FixedUpdate() { if(this.Direction < this.direction) { float amount = this.TurnRate * Time.deltaTime; this.direction = Math.Max(this.direction - amount, -1.0f); } if(this.Direction > this.direction) { float amount = this.TurnRate * Time.deltaTime; this.direction = Math.Min(this.direction + amount, 1.0f); } } /// Called once per visual frame protected override void Update() { base.Update(); // Update speed if it has changed. If the user doesn't use speed at all, // we will simply leave it alone. Animator animator = Animator; if(animator != null) { if(this.HorizontalVelocity != this.horizontalVelocity) { this.horizontalVelocity = this.HorizontalVelocity; Animator.SetFloat("HorizontalVelocity", this.HorizontalVelocity); } if(this.VerticalVelocity != this.verticalVelocity) { this.verticalVelocity = this.VerticalVelocity; Animator.SetFloat("VerticalVelocity", this.VerticalVelocity); } } if(this.ManageDirection) { updateDirection(); } } #if false /// Speed at which the actor is moving horizontally in units/second float IWalkingActorPresenter.HorizontalSpeed { get { return this.HorizontalSpeed; } set { this.HorizontalSpeed = value; } } /// Speed at which the actor is moving vertically in units/second float IWalkingActorPresenter.VerticalSpeed { get { return this.VerticalSpeed; } set { this.VerticalSpeed = value; } } #endif /// Faces the character to the left, right or an intermediate angle private void updateDirection() { Vector3 target = gameObject.transform.position; { float x = Math.Min(Math.Max(this.direction, -1.0f), +1.0f); target.x += x; // Calculate the Z coordinate using the square root of the sine function // to achieve a linear progression of the viewing angle. float z = (float)Math.Sqrt(Math.Sin(((x + 1.0) / 2.0) * Math.PI)); target.z -= z; } gameObject.transform.LookAt(target); } /// Direction the character is currently facing private float direction; /// Speed at which the character is moving horizontally private float horizontalVelocity; /// Speed at which the character is moving vertically private float verticalVelocity; } } // namespace Framework.Actors.Platformer