ofb/Assets/Scripts/PlayerController.cs

420 lines
15 KiB
C#
Raw Normal View History

using System.Collections;
2023-04-09 21:59:32 -07:00
using UnityEngine;
using UnityEngine.InputSystem;
2023-04-09 21:59:32 -07:00
2023-04-17 00:01:49 -07:00
public class PlayerMovement : MonoBehaviour
2023-04-09 21:59:32 -07:00
{
//Scriptable object which holds all the player's movement parameters. If you don't want to use it
//just paste in all the parameters, though you will need to manuly change all references in this script
//HOW TO: to add the scriptable object, right-click in the project window -> create -> Player Data
//Next, drag it into the slot in playerMovement on your player
public PlayerData Data;
2023-04-17 00:01:49 -07:00
#region Variables
//Components
public Rigidbody2D RB { get; private set; }
2023-04-17 00:01:49 -07:00
//Variables control the various actions the player can perform at any time.
//These are fields which can are public allowing for other sctipts to read them
//but can only be privately written to.
public bool IsFacingRight { get; private set; }
public bool IsJumping { get; private set; }
//Timers (also all fields, could be private and a method returning a bool could be used)
public float LastOnGroundTime { get; private set; }
2023-04-28 16:18:50 -07:00
2023-05-03 13:16:44 -07:00
// trumpet
2023-04-28 16:18:50 -07:00
public int trumpet = 0;
2023-04-27 15:56:36 -07:00
public bool in_range = false;
public GameObject enemy;
2023-05-03 13:16:44 -07:00
bool unlockedTrumpet;
[SerializeField] SpriteRenderer trumpetSprite;
GameObject trumpetAnimationObject;
2023-05-03 13:16:44 -07:00
bool trumpetActive = false;
2023-05-05 19:54:18 -07:00
//clarinet
private float tempFallSpeed;
//Jump
private bool _isJumpCut;
private bool _isJumpFalling;
private bool isRegFalling;
2023-04-17 16:47:12 -07:00
private Vector2 _moveInput;
public float LastPressedJumpTime { get; private set; }
Tutorial_GrapplingRope grapplingRope;
bool wasGrappling = false;
//Set all of these up in the inspector
[Header("Checks")]
//Size of groundCheck depends on the size of your character generally you want them slightly small than width (for ground) and height (for the wall check)
[SerializeField] private Vector2 _groundCheckSize = new Vector2(0.49f, 0.03f);
2023-04-30 00:19:52 -07:00
[SerializeField] private float _groundCheckOffset;
[Space(5)]
[SerializeField] private Vector2 _wallCheckSize = new Vector2(0.5f, 1f);
[Header("Layers & Tags")]
[SerializeField] private LayerMask _groundLayer;
// Static classes
[HideInInspector] private PlayerBehavior playerBehavior;
[HideInInspector] private AudioSource audioSource;
[HideInInspector] private bool soundPlaying = false;
[HideInInspector] private GameUIController gameUI;
#endregion
2023-05-06 12:25:29 -07:00
private void Awake() {
RB = GetComponent<Rigidbody2D>();
playerBehavior = this.gameObject.GetComponent<PlayerBehavior>();
grapplingRope = playerBehavior.grapplingRope;
audioSource = GetComponent<AudioSource>();
gameUI = GameObject.FindGameObjectWithTag("GameUICanvas").GetComponent<GameUIController>();
2023-05-03 13:16:44 -07:00
trumpetSprite.enabled = false;
trumpetAnimationObject = trumpetSprite.gameObject.transform.GetChild(0).gameObject;
trumpetAnimationObject.SetActive(false);
2023-04-17 00:01:49 -07:00
}
2023-04-09 21:59:32 -07:00
2023-05-06 12:25:29 -07:00
private void Start() {
SetGravityScale(Data.gravityScale);
IsFacingRight = true;
2023-05-05 19:54:18 -07:00
tempFallSpeed = Data.maxFallSpeed;
2023-04-09 21:59:32 -07:00
}
2023-05-06 12:25:29 -07:00
void OnMove(InputValue value) {
if (playerBehavior.playerIsAlive) {
this._moveInput = value.Get<Vector2>();
}
2023-05-06 12:25:29 -07:00
else {
this._moveInput = Vector2.zero;
}
}
2023-05-06 12:25:29 -07:00
void OnJump() {
if (playerBehavior.playerIsAlive) {
OnJumpInput();
}
}
2023-05-06 12:25:29 -07:00
private void Update() {
unlockedTrumpet = StateController.Instance.HasTrumpet();
2023-05-03 11:53:37 -07:00
#region TIMERS
LastOnGroundTime -= Time.deltaTime;
LastPressedJumpTime -= Time.deltaTime;
#endregion
#region COLLISION CHECKS
2023-05-06 12:25:29 -07:00
if (!IsJumping) {
2023-05-03 11:53:37 -07:00
//Ground Check
2023-05-06 12:25:29 -07:00
if (IsGrounded()) {
//checks if set box overlaps with ground
2023-05-03 11:53:37 -07:00
LastOnGroundTime = Data.coyoteTime; //if so sets the lastGrounded to coyoteTime
2023-05-06 12:25:29 -07:00
if (unlockedTrumpet) {
2023-05-05 20:52:28 -07:00
trumpet = 1;
2023-05-03 11:53:37 -07:00
gameUI.ToggleTrumpet(true);
}
2023-05-06 12:25:29 -07:00
else {
2023-05-03 11:53:37 -07:00
trumpet = -1;
}
wasGrappling = false;
isRegFalling = false;
}
2023-05-06 12:25:29 -07:00
else {
2023-05-03 11:53:37 -07:00
// print("not jumping");
2023-05-06 12:25:29 -07:00
if (!_isJumpFalling && !isRegFalling) {
if (unlockedTrumpet) {
2023-05-03 11:53:37 -07:00
trumpet = 1;
gameUI.ToggleTrumpet(true);
}
2023-05-06 12:25:29 -07:00
else {
trumpet = -1;
}
2023-05-03 11:53:37 -07:00
isRegFalling = true;
}
2023-05-03 11:53:37 -07:00
}
}
#endregion
2023-05-03 11:53:37 -07:00
#region JUMP CHECKS
2023-05-06 12:25:29 -07:00
if (IsJumping && RB.velocity.y <= 0) {
2023-05-03 11:53:37 -07:00
IsJumping = false;
// print("isJumping " + IsJumping);
_isJumpFalling = true;
2023-05-03 11:53:37 -07:00
}
2023-05-06 12:25:29 -07:00
if (LastOnGroundTime > 0 && !IsJumping) {
2023-05-03 11:53:37 -07:00
_isJumpCut = false;
2023-05-03 11:53:37 -07:00
if (!IsJumping)
_isJumpFalling = false;
}
2023-05-05 20:52:28 -07:00
2023-05-03 11:53:37 -07:00
//Jump
2023-05-06 12:25:29 -07:00
if (CanJump() && LastPressedJumpTime > 0) {
2023-05-03 11:53:37 -07:00
IsJumping = true;
_isJumpCut = false;
_isJumpFalling = false;
2023-05-05 20:52:28 -07:00
bool inCoyoteTime = LastOnGroundTime > 0;
2023-05-05 21:37:59 -07:00
//print("coyote time: " + inCoyoteTime);
2023-05-05 20:52:28 -07:00
2023-05-03 11:53:37 -07:00
Jump();
2023-05-03 13:16:44 -07:00
// determine if trumpet jump
2023-05-06 12:25:29 -07:00
if (!IsGrounded() && in_range && trumpet > 0 && !inCoyoteTime) {
2023-05-03 13:16:44 -07:00
StartCoroutine(ActivateTrumpetSprite());
2023-05-03 11:53:37 -07:00
gameObject.transform.Find("Trumpet").GetComponent<AudioSource>().Play();
enemy.GetComponent<EnemyPatrol>().DefeatEnemy();
enemy = null;
in_range = false;
2023-05-03 09:37:22 -07:00
}
2023-05-06 12:25:29 -07:00
else if (!IsGrounded() && !in_range && trumpet > 0 && !inCoyoteTime) {
2023-05-03 11:53:37 -07:00
trumpet -= 1;
2023-05-03 09:37:22 -07:00
}
2023-05-03 11:53:37 -07:00
// check if double jump, play sound
2023-05-06 12:25:29 -07:00
if (trumpet == 0) {
2023-05-03 13:16:44 -07:00
StartCoroutine(ActivateTrumpetSprite());
2023-05-03 11:53:37 -07:00
gameObject.transform.Find("Trumpet").GetComponent<AudioSource>().Play();
2023-05-03 09:37:22 -07:00
}
2023-05-03 11:53:37 -07:00
}
2023-05-03 11:53:37 -07:00
// stop sound if needed
2023-05-06 12:25:29 -07:00
if (soundPlaying && (isRegFalling || IsJumping || _isJumpFalling)) {
2023-05-03 11:53:37 -07:00
print("footsteps stop");
audioSource.Stop();
soundPlaying = false;
}
#endregion
#region GRAPPLE CHECKS
// set wasGrappling to true if the player starts grappling
2023-05-06 12:25:29 -07:00
if (grapplingRope.isGrappling) {
2023-05-03 11:53:37 -07:00
wasGrappling = true;
}
#endregion
2023-05-03 11:53:37 -07:00
#region GRAVITY
//Higher gravity if we've released the jump input or are falling
2023-05-03 12:06:29 -07:00
// if (IsSliding)
// {
// SetGravityScale(0);
// }
2023-05-06 12:25:29 -07:00
if (RB.velocity.y < 0 && _moveInput.y < 0) {
2023-05-03 11:53:37 -07:00
//Much higher gravity if holding down
SetGravityScale(Data.gravityScale * Data.fastFallGravityMult);
//Caps maximum fall speed, so when falling over large distances we don't accelerate to insanely high speeds
RB.velocity = new Vector2(RB.velocity.x, Mathf.Max(RB.velocity.y, -Data.maxFastFallSpeed));
}
2023-05-06 12:25:29 -07:00
else if (_isJumpCut) {
2023-05-03 11:53:37 -07:00
//Higher gravity if jump button released
SetGravityScale(Data.gravityScale * Data.jumpCutGravityMult);
RB.velocity = new Vector2(RB.velocity.x, Mathf.Max(RB.velocity.y, -Data.maxFallSpeed));
}
2023-05-06 12:25:29 -07:00
else if ((IsJumping || _isJumpFalling) && Mathf.Abs(RB.velocity.y) < Data.jumpHangTimeThreshold) {
2023-05-03 11:53:37 -07:00
SetGravityScale(Data.gravityScale * Data.jumpHangGravityMult);
}
2023-05-06 12:25:29 -07:00
else if (RB.velocity.y < 0) {
2023-05-03 11:53:37 -07:00
//Higher gravity if falling
SetGravityScale(Data.gravityScale * Data.fallGravityMult);
//Caps maximum fall speed, so when falling over large distances we don't accelerate to insanely high speeds
RB.velocity = new Vector2(RB.velocity.x, Mathf.Max(RB.velocity.y, -Data.maxFallSpeed));
}
2023-05-06 12:25:29 -07:00
else {
2023-05-03 11:53:37 -07:00
//Default gravity if standing on a platform or moving upwards
SetGravityScale(Data.gravityScale);
}
#endregion
#region SOUND CHECKS
2023-05-06 12:25:29 -07:00
if (!IsJumping && !_isJumpFalling && !isRegFalling && _moveInput.x != 0) {
if (!soundPlaying) {
2023-05-03 11:53:37 -07:00
// print("footsteps PLAY");
audioSource.Play();
soundPlaying = true;
2023-05-03 09:37:22 -07:00
}
}
2023-05-06 12:25:29 -07:00
else if (soundPlaying && audioSource.clip.name == "footsteps") {
2023-05-03 11:53:37 -07:00
// print("footsteps stop");
audioSource.Stop();
soundPlaying = false;
}
#endregion
#region UPDATE UI
2023-05-06 12:25:29 -07:00
if (trumpet == 0) {
2023-05-03 11:53:37 -07:00
gameUI.ToggleTrumpet(false);
2023-05-03 00:35:59 -07:00
}
2023-05-03 11:53:37 -07:00
#endregion
}
2023-05-06 12:25:29 -07:00
private void FixedUpdate() {
Run(1);
2023-05-03 08:15:59 -07:00
}
#region INPUT CALLBACKS
//Methods which whandle input detected in Update()
2023-05-06 12:25:29 -07:00
public void OnJumpInput() {
LastPressedJumpTime = Data.jumpInputBufferTime;
2023-04-17 00:01:49 -07:00
}
2023-05-06 12:25:29 -07:00
public void OnJumpUpInput() {
if (CanJumpCut()) {
_isJumpCut = true;
2023-05-06 12:25:29 -07:00
}
2023-04-17 00:01:49 -07:00
}
#endregion
2023-04-17 00:01:49 -07:00
#region GENERAL METHODS
2023-05-06 12:25:29 -07:00
public void SetGravityScale(float scale) {
RB.gravityScale = scale;
}
#endregion
//MOVEMENT METHODS
#region RUN METHODS
2023-05-06 12:25:29 -07:00
private void Run(float lerpAmount) {
//Calculate the direction we want to move in and our desired velocity
float targetSpeed = _moveInput.x * Data.runMaxSpeed;
//We can reduce are control using Lerp() this smooths changes to are direction and speed
targetSpeed = Mathf.Lerp(RB.velocity.x, targetSpeed, lerpAmount);
2023-04-17 00:01:49 -07:00
#region Calculate AccelRate
float accelRate;
2023-04-17 00:01:49 -07:00
//Gets an acceleration value based on if we are accelerating (includes turning)
//or trying to decelerate (stop). As well as applying a multiplier if we're air borne.
2023-05-06 12:25:29 -07:00
if (LastOnGroundTime > 0) {
accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? Data.runAccelAmount : Data.runDeccelAmount;
}
2023-05-06 12:25:29 -07:00
else if (wasGrappling) {
accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? Data.runAccelAmount * Data.accelInAir : Data.runDeccelAmount * (Data.deccelInAir / 5);
}
2023-05-06 12:25:29 -07:00
else {
accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? Data.runAccelAmount * Data.accelInAir : Data.runDeccelAmount * Data.deccelInAir;
}
#endregion
2023-04-17 00:01:49 -07:00
#region Add Bonus Jump Apex Acceleration
//Increase are acceleration and maxSpeed when at the apex of their jump, makes the jump feel a bit more bouncy, responsive and natural
2023-05-06 12:25:29 -07:00
if ((IsJumping || _isJumpFalling) && Mathf.Abs(RB.velocity.y) < Data.jumpHangTimeThreshold) {
accelRate *= Data.jumpHangAccelerationMult;
targetSpeed *= Data.jumpHangMaxSpeedMult;
}
#endregion
2023-04-17 00:01:49 -07:00
#region Conserve Momentum
//We won't slow the player down if they are moving in their desired direction but at a greater speed than their maxSpeed
2023-05-06 12:25:29 -07:00
if ((Data.doConserveMomentum && Mathf.Abs(RB.velocity.x) > Mathf.Abs(targetSpeed) && Mathf.Sign(RB.velocity.x) == Mathf.Sign(targetSpeed) && Mathf.Abs(targetSpeed) > 0.01f && LastOnGroundTime < 0) || grapplingRope.isGrappling) {
//Prevent any deceleration from happening, or in other words conserve are current momentum
//You could experiment with allowing for the player to slightly increae their speed whilst in this "state"
accelRate = 0;
}
#endregion
2023-04-17 00:01:49 -07:00
//Calculate difference between current velocity and desired velocity
float speedDif = targetSpeed - RB.velocity.x;
//Calculate force along x-axis to apply to thr player
2023-04-17 00:01:49 -07:00
float movement = speedDif * accelRate;
2023-04-17 00:01:49 -07:00
//Convert this to a vector and apply to rigidbody
RB.AddForce(movement * Vector2.right, ForceMode2D.Force);
2023-04-17 00:01:49 -07:00
/*
* For those interested here is what AddForce() will do
* RB.velocity = new Vector2(RB.velocity.x + (Time.fixedDeltaTime * speedDif * accelRate) / RB.mass, RB.velocity.y);
* Time.fixedDeltaTime is by default in Unity 0.02 seconds equal to 50 FixedUpdate() calls per second
*/
}
2023-05-06 12:25:29 -07:00
private void Turn() {
//stores scale and flips the player along the x axis,
Vector3 scale = transform.localScale;
scale.x *= -1;
transform.localScale = scale;
IsFacingRight = !IsFacingRight;
}
#endregion
#region JUMP METHODS
2023-05-06 12:25:29 -07:00
private void Jump() {
//Ensures we can't call Jump multiple times from one press
LastPressedJumpTime = 0;
LastOnGroundTime = 0;
#region Perform Jump
//We increase the force applied if we are falling
//This means we'll always feel like we jump the same amount
//(setting the player's Y velocity to 0 beforehand will likely work the same, but I find this more elegant :D)
float force = Data.jumpForce;
2023-05-06 12:25:29 -07:00
if (RB.velocity.y < 0) {
force -= RB.velocity.y;
2023-05-06 12:25:29 -07:00
}
RB.AddForce(Vector2.up * force, ForceMode2D.Impulse);
#endregion
}
#endregion
#region CHECK METHODS
2023-05-06 12:25:29 -07:00
private bool CanJump() {
if (!IsGrounded() && trumpet > 0) {
return true;
}
return LastOnGroundTime > 0 && !IsJumping;
}
2023-05-06 12:25:29 -07:00
private bool CanJumpCut() {
return IsJumping && RB.velocity.y > 0;
}
2023-05-06 12:25:29 -07:00
public bool IsGrounded() {
2023-04-27 20:12:07 -07:00
// print(Physics2D.OverlapBox(this.transform.position, _groundCheckSize, 0, _groundLayer) && !IsJumping);
2023-04-30 00:19:52 -07:00
return (Physics2D.OverlapBox(new Vector2(this.transform.position.x, this.transform.position.y - _groundCheckOffset), _groundCheckSize, 0, _groundLayer) && !IsJumping);
}
2023-05-05 19:54:18 -07:00
2023-05-06 12:25:29 -07:00
public void FloatGravity(float grav) {
2023-05-05 20:40:06 -07:00
Data.maxFallSpeed = grav;
2023-05-05 19:54:18 -07:00
}
2023-05-06 12:25:29 -07:00
public void EndFloatGravity() {
2023-05-05 19:54:18 -07:00
Data.maxFallSpeed = tempFallSpeed;
}
#endregion
#region EDITOR METHODS
2023-05-06 12:25:29 -07:00
private void OnDrawGizmosSelected() {
Gizmos.color = Color.green;
2023-04-30 00:19:52 -07:00
Gizmos.DrawWireCube(new Vector2(this.transform.position.x, this.transform.position.y - _groundCheckOffset), _groundCheckSize);
Gizmos.color = Color.blue;
Gizmos.DrawWireCube(this.transform.position, _wallCheckSize);
Gizmos.DrawWireCube(this.transform.position, _wallCheckSize);
2023-04-09 21:59:32 -07:00
}
#endregion
2023-05-03 13:16:44 -07:00
#region ADDITIONAL TRUMPET METHODS
2023-05-06 12:25:29 -07:00
IEnumerator ActivateTrumpetSprite() {
if (!trumpetActive) {
2023-05-03 13:16:44 -07:00
trumpetActive = true;
trumpetSprite.enabled = true;
trumpetAnimationObject.SetActive(true);
trumpetAnimationObject.GetComponent<Animator>().Play("poof");
2023-05-03 13:16:44 -07:00
yield return new WaitForSeconds(.5f);
trumpetAnimationObject.SetActive(false);
2023-05-03 13:16:44 -07:00
trumpetSprite.enabled = false;
trumpetActive = false;
}
}
#endregion
2023-04-09 21:59:32 -07:00
}