ludumdare50/Assets/Scripts/PlayerMovement.cs
2022-04-02 19:07:27 -04:00

285 lines
7.7 KiB
C#

#nullable enable
using NaughtyAttributes;
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(PlayerInput), typeof(Rigidbody2D))]
public class PlayerMovement : MonoBehaviour {
[SerializeField] [Required]
GameFlowManager gameFlowManager = null!;
[SerializeField] [field: Required]
PlayerStats stats = null!;
[SerializeField] GameObject globalCamera;
[field: Required]
Rigidbody2D rb = null!;
[SerializeField] GameObject safeZonePrompt;
Vector2 moveDirection;
BaseState currentState = null!;
SafeZone? safeZone;
VampireEntity vampireEntity;
Animator animator;
bool facingRight = true;
bool lastJumpButton;
public bool IsInSafeZone => currentState is ImmobileMovementState;
#region Unity Messages
void Awake() {
rb = GetComponent<Rigidbody2D>();
vampireEntity = GetComponent<VampireEntity>();
animator = GetComponentInChildren<Animator>();
currentState = new ImmobileMovementState(this);
safeZonePrompt.SetActive(false);
}
void Start() {
globalCamera.SetActive(true);
currentState.EnterState();
}
void Update() {
if (gameFlowManager.Paused)
return;
if (currentState.UpdateState() is {} newState)
SwitchState(newState);
}
void FixedUpdate() {
if (gameFlowManager.Paused)
return;
if (safeZone != null && IsInSafeZone) {
safeZonePrompt.SetActive(false);
} else {
safeZonePrompt.SetActive(true);
}
if (currentState.FixedUpdateState() is {} newState)
SwitchState(newState);
}
void OnDrawGizmos() => currentState?.OnDrawGizmos();
#endregion
#region Flip
public void Flip() {
facingRight = !facingRight;
Vector3 scaler = transform.localScale;
scaler.x *= -1;
transform.localScale = scaler;
}
public void FlipAccordingToInput() {
if ((!facingRight && moveDirection.x > 0) || (facingRight && moveDirection.x < 0)) {
Flip();
}
}
#endregion
public SafeZone? GetSafeZoneIfImmobile() {
return currentState is ImmobileMovementState ? safeZone : null;
}
#region Inputs
public void OnMove(InputAction.CallbackContext ctx) {
moveDirection = ctx.ReadValue<Vector2>();
if (moveDirection.sqrMagnitude > 1.0f)
moveDirection.Normalize();
FlipAccordingToInput();
}
public void OnJump(InputAction.CallbackContext ctx) {
if (!ctx.WasPressedThisFrame(ref lastJumpButton))
return;
if (gameFlowManager.Paused || safeZone == null)
return;
if (IsInSafeZone) {
if (moveDirection.magnitude >= safeZone.Stats.MinJumpJoystickValue)
SwitchState(new ExitSafeZoneMovementState(this, safeZone, moveDirection));
} else if (currentState is NormalMovementState) //TODO if (AngleBetween(moveDirection, toSafeZone) < 90)
SwitchState(new EnterSafeZoneMovementState(this, safeZone));
}
#endregion
#region Rigidbody
void OnTriggerEnter2D(Collider2D other) {
if (other.GetComponent<SafeZone>() is {} triggeredSafeZone)
safeZone = triggeredSafeZone;
}
//Probably should remove, mostly for ImmobileState's draw gizmos
void OnTriggerStay2D(Collider2D other) {
if (other.GetComponent<SafeZone>() is {} triggeredSafeZone)
safeZone = triggeredSafeZone;
}
void OnTriggerExit2D(Collider2D other) {
if (other.GetComponent<SafeZone>() is {})
safeZone = null;
}
#endregion
#region States
void SwitchState(BaseState newState) {
currentState.LeaveState();
currentState = newState;
newState.EnterState();
}
abstract class BaseStatePlayerMovement : BaseState {
protected PlayerMovement playerMovement;
public BaseStatePlayerMovement(PlayerMovement playerMovement) {
this.playerMovement = playerMovement;
}
}
class NormalMovementState : BaseStatePlayerMovement {
public NormalMovementState(PlayerMovement playerMovement) : base(playerMovement) {}
public override void EnterState() {
// playerMovement.animator.SetBool("Running", true);
}
public override void LeaveState() {
// playerMovement.animator.SetBool("Running", false);
}
public override BaseState? FixedUpdateState() {
playerMovement.rb.velocity = (Vector3)playerMovement.moveDirection * playerMovement.stats.movementSpeed;
if(playerMovement.rb.velocity.magnitude > 0.01f) {
playerMovement.animator.Play("Player_Run");
} else {
playerMovement.animator.Play("Player_Idle");
}
return null;
}
}
class JumpingMovementState : BaseStatePlayerMovement {
readonly float duration;
readonly Vector3 target;
Vector3 startPosition;
float startTime;
public JumpingMovementState(PlayerMovement playerMovement, float duration, Vector3 target) : base(playerMovement) {
this.duration = duration;
this.target = target;
}
public override void EnterState() {
startPosition = playerMovement.transform.position;
startTime = Time.time;
// playerMovement.animator.SetBool("Jumping", true);
playerMovement.animator.Play("Player_Jump");
}
public override void LeaveState() {
// playerMovement.animator.SetBool("Jumping", false);
playerMovement.animator.Play("Player_Idle");
}
public override BaseState? FixedUpdateState() {
float currentTime = Time.time - startTime;
if (currentTime >= duration)
return Transition();
playerMovement.rb.MovePosition(Vector3.Lerp(
startPosition,
target,
ModifyLerpTime(currentTime / duration)
));
return null;
}
protected virtual BaseState Transition() => new NormalMovementState(playerMovement);
protected virtual float ModifyLerpTime(float t) => t;
}
class EnterSafeZoneMovementState : JumpingMovementState {
readonly SafeZone safeZone;
public EnterSafeZoneMovementState(PlayerMovement playerMovement, SafeZone safeZone) : base(playerMovement, safeZone.Stats.JumpDuration, safeZone.transform.position) {
this.safeZone = safeZone;
}
public override void EnterState() {
base.EnterState();
playerMovement.rb.SetEnabled(false);
}
protected override BaseState Transition() => new ImmobileMovementState(playerMovement);
protected override float ModifyLerpTime(float t) => safeZone.Stats.JumpSpeedCurve.Evaluate(t);
}
class ExitSafeZoneMovementState : JumpingMovementState {
readonly SafeZone safeZone;
public ExitSafeZoneMovementState(PlayerMovement playerMovement, SafeZone safeZone, Vector2 direction) : base(playerMovement, safeZone.Stats.JumpDuration, safeZone.GetOutsidePosition(direction)) {
this.safeZone = safeZone;
}
public override void LeaveState() {
base.EnterState();
playerMovement.rb.SetEnabled(true);
}
protected override float ModifyLerpTime(float t) => safeZone.Stats.JumpSpeedCurve.Evaluate(t);
}
class ImmobileMovementState : BaseStatePlayerMovement {
public ImmobileMovementState(PlayerMovement playerMovement) : base(playerMovement) {}
public override void EnterState() {
base.EnterState();
playerMovement.globalCamera.SetActive(true);
if (!playerMovement.rb.isKinematic)
Debug.LogWarning("Rigidbody should probably be kinematic when immobile (when in safe zone).");
}
public override void LeaveState() {
base.LeaveState();
playerMovement.globalCamera.SetActive(false);
}
#if UNITY_EDITOR
public override void OnDrawGizmos() {
if (playerMovement.safeZone is null)
return;
Vector3 dropPosition = playerMovement.safeZone.GetOutsidePosition(playerMovement.moveDirection);
bool canJump = playerMovement.moveDirection.magnitude >= playerMovement.safeZone.Stats.MinJumpJoystickValue;
Gizmos.color = canJump ? Color.green : Color.red;
Gizmos.DrawLine(playerMovement.transform.position, dropPosition);
if (canJump)
Gizmos.DrawSphere(dropPosition, .5f);
else
Gizmos.DrawWireSphere(dropPosition, .5f);
}
#endif
}
#endregion
}