You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

534 lines
20 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
[RequireComponent(typeof(MoveController))]
[RequireComponent(typeof(BoxCollider2D))]
public class PlayerController : MonoBehaviour {
public float jumpHeight = 4f;
public float maxForwardJumpBoost = 2.50f;
public float timeToJumpApex = 0.4f;
public float lastDashTime = 0f;
public float dashTime = 0.125f;
public float dashSpeed = 50f;
public float dashCooldown = 1.25f;
public float moveSpeed = 6f;
public float maxFallSpeed = -20f;
public float timeToMaxRunSpeed = 0.15f;
public float timeToMaxAirmoveSpeed = 0.025f;
public float maxApexTime = 0.05f; // amount of time spent at max jump height before falling again
public float coyoteTime = 0.1f;
public int maxJumps = 2;
public GameObject tracerPrefab;
public GameObject leftDashIndicator;
public GameObject rightDashIndicator;
public PlayerControls controls;
// this has to be public to be readable by the display, which is a code
// smell.
public JumpState jumpState;
public JumpDirection jumpDirection;
private float jumpStateStart;
private float jumpStartTime;
private float jumpHeldDuration;
public int jumpCount;
private bool wasHoldingJump;
public float apexTime;
private float jumpVelocity = 8f;
private float gravity = -20f;
private Vector3 velocity;
private MoveController moveController;
private float velocityXSmoothing;
private GameObject tracers;
void Start() {
if (!tracers) {
tracers = new GameObject();
tracers.name = "Player Debug Tracers";
}
// Debug.Log("Creating new controls in Start!");
// controls = new PlayerControls();
GameObject[] checkpoints = GameObject.FindGameObjectsWithTag("Checkpoint");
foreach (GameObject cp in checkpoints) {
if (cp.GetComponent<Checkpoint>().isLastCheckpoint) {
transform.position = new Vector3(
cp.transform.position.x,
cp.transform.position.y,
0f
);
}
}
setJumpState(JumpState.Falling);
moveController = GetComponent<MoveController>();
gravity = -(2* jumpHeight) / Mathf.Pow(timeToJumpApex, 2);
jumpVelocity = Mathf.Abs(gravity) * timeToJumpApex;
}
void Awake() {
Debug.Log("Creating new controls in Awake!");
}
void Update() {
InputActionPhase jumpPhase = InputActionPhase.Waiting;
bool jumpTriggered = false;
if (controls == null) {
Debug.Log("HOW IS GAMEPLAY NULL");
}
if (controls.Gameplay.Jump != null) {
jumpPhase = controls.Gameplay.Jump.phase;
jumpTriggered = controls.Gameplay.Jump.triggered;
}
// Debug.LogFormat("Jump Phase: {0} Triggered: {1}", jumpPhase, jumpTriggered);
GameObject tracerObj = Instantiate(tracerPrefab, transform.position, Quaternion.identity);
tracerObj.transform.parent = tracers.transform;
TracerDot tracer = tracerObj.GetComponent<TracerDot>();
Vector2 input = controls.Gameplay.Move.ReadValue<Vector2>();
// Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
float targetX = input.x * moveSpeed;
if (Mathf.Abs(input.x) < 0.1) {
targetX = 0;
}
Vector3 initialVelocity = velocity;
if (moveController.collisions.above) {
velocity.y = 0;
}
if (moveController.collisions.left || moveController.collisions.right) {
velocity.x = 0;
}
switch (jumpState) {
case JumpState.Grounded:
tracer.color = Color.white;
if (dash()) {
break;
} else if (controls.Gameplay.Jump.phase == InputActionPhase.Started) {
// } else if (Input.GetButton("Jump")) {
velocity.y = jumpVelocity;
if (input.x >= 0.25f) {
velocity.x = moveSpeed*maxForwardJumpBoost;
} else if (input.x <= -0.25f) {
velocity.x = -moveSpeed*maxForwardJumpBoost;
} else {
velocity.x = 0f;
}
setJumpState(JumpState.Ascending);
break;
} else {
velocity.y = gravity * Time.deltaTime;
velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxRunSpeed);
}
break;
case JumpState.Dash:
tracer.color = Color.red;
velocity.x = Mathf.SmoothDamp(velocity.x, velocity.x >= 0 ? moveSpeed : -moveSpeed, ref velocityXSmoothing, dashTime);
velocity.y = 0;
float timeDashing = Time.time - lastDashTime;
if (timeDashing >= dashTime) {
if (moveController.isGrounded) {
setJumpState(JumpState.Grounded);
} else {
setJumpState(JumpState.Falling);
}
}
break;
case JumpState.Ascending:
tracer.color = Color.green;
// n starts at 0 and goes to 1 as we ascend
float n = Mathf.InverseLerp(0, timeToJumpApex, Time.time - jumpStateStart);
// the drag coefficient gets bigger as we ascend, terminating at 1
float dragCoefficient = n * n;
if (dash()) {
break;
// } else if (Input.GetButtonDown("Jump") && jumpCount < maxJumps) {
} else if (controls.Gameplay.Jump.triggered && jumpCount < maxJumps) {
velocity.y = jumpVelocity;
if (input.x >= 0.25f) {
velocity.x = moveSpeed*maxForwardJumpBoost;
} else if (input.x <= -0.25f) {
velocity.x = -moveSpeed*maxForwardJumpBoost;
} else {
velocity.x = 0f;
}
setJumpState(JumpState.Ascending);
break;
// } else if (Input.GetButton("Jump")) {
} else if (controls.Gameplay.Jump.phase == InputActionPhase.Started) {
float jumpDrag = jumpVelocity * dragCoefficient;
if (n <= 0.7) {
jumpDrag = 0;
}
velocity.y = jumpVelocity - jumpDrag;
} else {
// if we're not pressing jump any more we add all the gravity
velocity.y += 4 * gravity * Time.deltaTime;
// the amount of time spent floating is equal to the amount of
// time the player held down the jump button.
if (wasHoldingJump) {
apexTime = Time.time - jumpStartTime;
}
}
float forwardBoost = maxForwardJumpBoost - (maxForwardJumpBoost * dragCoefficient);
forwardBoost = Mathf.Clamp(forwardBoost, 1, maxForwardJumpBoost);
switch (jumpDirection) {
case JumpDirection.Neutral:
// you can wiggle out of a neutral jump in either direction
velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
case JumpDirection.Left:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX < 0 ? targetX * forwardBoost : 0, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
case JumpDirection.Right:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX > 0 ? targetX * forwardBoost : 0, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
}
// if we were rising in the last frame but will be falling in this
// frame, we should zero out the velocity to float instead.
if (initialVelocity.y >= 0 && velocity.y <= 0) {
velocity.y = 0;
// if (wasHoldingJump && Input.GetButton("Jump")) {
if (wasHoldingJump && controls.Gameplay.Jump.phase == InputActionPhase.Started) {
apexTime = maxApexTime;
}
setJumpState(JumpState.Apex);
}
break;
case JumpState.Apex:
tracer.color = Color.magenta;
if (dash()) {
break;
// } else if (Input.GetButtonDown("Jump") && jumpCount < maxJumps) {
} else if (controls.Gameplay.Jump.triggered && jumpCount < maxJumps) {
velocity.y = jumpVelocity;
if (input.x >= 0.25f) {
velocity.x = moveSpeed*maxForwardJumpBoost;
} else if (input.x <= -0.25f) {
velocity.x = -moveSpeed*maxForwardJumpBoost;
} else {
velocity.x = 0f;
}
setJumpState(JumpState.Ascending);
break;
} else {
switch (jumpDirection) {
case JumpDirection.Neutral:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
case JumpDirection.Left:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX < 0 ? targetX : 0, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
case JumpDirection.Right:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX > 0 ? targetX : 0, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
}
float timeAtApex = Time.time - jumpStateStart;
float apexTimeRemaining = apexTime - timeAtApex;
if (apexTimeRemaining < 0) {
velocity.y += gravity * -apexTimeRemaining;
setJumpState(JumpState.Descending);
} else {
velocity.y = 0;
}
}
break;
case JumpState.Descending:
tracer.color = Color.yellow;
// if (Input.GetButtonDown("Jump") && jumpCount < maxJumps) {
if (controls.Gameplay.Jump.triggered && jumpCount < maxJumps) {
velocity.y = jumpVelocity;
if (input.x >= 0.25f) {
velocity.x = moveSpeed*maxForwardJumpBoost;
} else if (input.x <= -0.25f) {
velocity.x = -moveSpeed*maxForwardJumpBoost;
} else {
velocity.x = 0f;
}
setJumpState(JumpState.Ascending);
break;
}
float n2 = (Time.time - jumpStateStart) / timeToJumpApex;
n2 = Mathf.Clamp(n2, 0, 1);
switch (jumpDirection) {
case JumpDirection.Neutral:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
case JumpDirection.Left:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX < 0 ? targetX : 0, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
case JumpDirection.Right:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX > 0 ? targetX : 0, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
}
// fall speed is increasing when descending
velocity.y += gravity * Time.deltaTime * (Mathf.Clamp(4 * n2 * n2, 1, 4));
break;
case JumpState.CoyoteTime:
tracer.color = Color.blue;
velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxRunSpeed);
if (dash()) {
break;
// } else if (Input.GetButtonDown("Jump")) {
} else if (controls.Gameplay.Jump.triggered) {
setJumpState(JumpState.Ascending);
velocity.y = jumpVelocity;
} else {
float elapsedCoyoteTime = Time.time - jumpStateStart;
float coyoteTimeRemaining = coyoteTime - elapsedCoyoteTime;
if (coyoteTimeRemaining < 0) {
setJumpState(JumpState.Falling);
}
velocity.y += gravity * Time.deltaTime;
}
break;
case JumpState.Falling:
tracer.color = Color.grey;
// if (Input.GetButtonDown("Jump") && jumpCount < maxJumps) {
if (controls.Gameplay.Jump.triggered && jumpCount < maxJumps) {
velocity.y = jumpVelocity;
if (input.x >= 0.25f) {
velocity.x = moveSpeed*maxForwardJumpBoost;
} else if (input.x <= -0.25f) {
velocity.x = -moveSpeed*maxForwardJumpBoost;
} else {
velocity.x = 0f;
}
setJumpState(JumpState.Ascending);
break;
}
switch (jumpDirection) {
case JumpDirection.Neutral:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
case JumpDirection.Left:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX < 0 ? targetX : 0, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
case JumpDirection.Right:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX > 0 ? targetX : 0, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
break;
}
velocity.y += gravity * Time.deltaTime;
break;
default:
throw new System.Exception("bad jump state: " + jumpState);
}
if (velocity.y < maxFallSpeed) {
velocity.y = maxFallSpeed;
}
moveController.Move(velocity * Time.deltaTime);
if (jumpState == JumpState.Grounded && !moveController.isGrounded) {
setJumpState(JumpState.CoyoteTime);
}
if (jumpState != JumpState.Grounded && moveController.isGrounded) {
setJumpState(JumpState.Grounded);
}
// wasHoldingJump = Input.GetButton("Jump");
wasHoldingJump = controls.Gameplay.Jump.phase == InputActionPhase.Started;
CheckCollisions();
}
void CheckCollisions() {
if (moveController.collisions.below) {
Crumble crumble = moveController.collisions.below.GetComponent<Crumble>();
if (crumble) {
crumble.Hit();
}
CollideWith(moveController.collisions.below);
}
CollideWith(moveController.collisions.above);
CollideWith(moveController.collisions.left);
CollideWith(moveController.collisions.right);
}
void CollideWith(Collider2D other) {
if (other == null) {
return;
}
TouchHazard hazard = other.GetComponent<TouchHazard>();
if (hazard) {
if (jumpState == JumpState.Dash) {
Destroy(hazard.gameObject);
} else {
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex) ;
}
}
Smashable smashable = other.GetComponent<Smashable>();
if (smashable) {
if (jumpState == JumpState.Dash) {
Destroy(smashable.gameObject);
}
}
Seeker seeker = other.GetComponent<Seeker>();
if (seeker) {
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex) ;
}
}
public void OnEnable() {
Debug.Log("Enabling the controls!");
if (controls == null) {
controls = new PlayerControls();
}
controls.Enable();
controls.Gameplay.Enable();
controls.Gameplay.Jump.Enable();
}
public void OnDisable() {
Debug.Log("Disabling the controls!");
controls.Disable();
controls.Gameplay.Disable();
controls.Gameplay.Jump.Disable();
}
void OnDestroy() {
}
bool dash() {
// if (!Input.GetButtonDown("Fire1")) {
if (!controls.Gameplay.Dash.triggered) {
return false;
}
if ((Time.time - lastDashTime) < dashCooldown) {
return false;
}
if (velocity.x >= 0) {
velocity.x = dashSpeed;
} else {
velocity.x = -dashSpeed;
}
velocity.y = 0;
lastDashTime = Time.time;
setJumpState(JumpState.Dash);
StartCoroutine(dashFade());
return true;
}
public IEnumerator dashFade() {
Material m = gameObject.GetComponent<MeshRenderer>().materials[0];
float dt = 0;
float h = 0;
float s = 0;
float v = 0;
Color.RGBToHSV(m.color, out h, out s, out v);
float maxSaturation = s;
while (dt < dashCooldown) {
s = Mathf.Clamp(dt / dashCooldown, 0f, maxSaturation);
m.color = Color.HSVToRGB(h, s, v);
dt += Time.deltaTime;
yield return true;
}
}
void setJumpState(JumpState state) {
leftDashIndicator.SetActive(state == JumpState.Dash && velocity.x < 0);
rightDashIndicator.SetActive(state == JumpState.Dash && velocity.x >= 0);
if (jumpState != JumpState.Ascending && state == JumpState.Ascending) {
jumpStartTime = Time.time;
}
jumpState = state;
jumpStateStart = Time.time;
if (velocity.x == 0) {
jumpDirection = JumpDirection.Neutral;
} else if (velocity.x > 0) {
jumpDirection = JumpDirection.Right;
} else {
jumpDirection = JumpDirection.Left;
}
if (state == JumpState.Grounded) {
jumpCount = 0;
}
if (state == JumpState.Ascending) {
jumpCount++;
}
}
/*
Possible JumpState transitions:
Grounded -> Ascending : a normal jump
Grounded -> Dash : a normal dash
Grounded -> CoyoteTime : player has walked off ledge
CoyoteTime -> Ascending : player has jumped after leaving a ledge
CoyoteTime -> Falling : player has walked off of a ledge and is now falling
Ascending -> Apex : player has reached the top of their jump normally
Apex -> Descending : player has reached the top of their jump and is now falling
*/
public enum JumpState {
// The player is on the ground
Grounded,
// The player has not jumped; they have walked off of a platform without
// jumping
CoyoteTime,
// The player is rising in a jump
Ascending,
// The player has reached the apex of their jump, where they will
// briefly remain to give a feeling of lightness
Apex,
// The player, having jumped, is in a controlled descent following the
// jump
Descending,
// The player is descending but without control; they are falling but
// did not initially jump.
Falling,
Dash,
}
public enum JumpDirection {
Neutral,
Left,
Right,
}
}