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