using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Windows; public class PlayerController : MonoBehaviour { [Header("References")] public PlayerControllerStats ControllStats; [SerializeField] private Collider2D FeetColl; [SerializeField] private Collider2D BodyColl; public Rigidbody2D rb; private PlayerControls playerControls; //movement vars private Vector2 MoveVelocity; private bool isFacingRight; //Collision check vars private RaycastHit2D GroundHit; private RaycastHit2D HeadHit; private bool IsGrounded; private bool BumpedHead; //Jump vars public float VerticalVelocity { get; private set; } private bool IsJumping; private bool IsFastFalling; private bool IsFalling; private float FastFallTime; private float FastFallReleaseSpeed; private int NumberOfJumpsUsed; //Apex vars private float ApexPoint; private float TimePastApexThreshold; private bool IsPastApexThreshold; //Jump buffer vars private float JumpBufferTimer; private bool JumpReleasedDuringBuffer; //coyote time vars private float CoyoteTimer; private void Awake() { playerControls = new PlayerControls(); isFacingRight = true; } private void OnEnable() { playerControls.Enable(); } private void OnDisable() { playerControls.Disable(); } void FixedUpdate() { CollisionChecks(); Jump(); if(IsGrounded) { Move(ControllStats.GroundAcceleration, ControllStats.GroundDeceleration, playerControls.mainActions.Move.ReadValue<Vector2>()); } else { Move(ControllStats.AirAcceleration, ControllStats.AirDeceleration, playerControls.mainActions.Move.ReadValue<Vector2>()); } } private void Update() { CountTimers(); JumpChecks(); } private void JumpChecks() { //WHEN WE PRESS THE JUMP BUTTON if(playerControls.mainActions.Jump.WasPressedThisFrame()) { JumpBufferTimer = ControllStats.JumpBufferTime; JumpReleasedDuringBuffer = false; } //WHEN WE RELEASE THE JUMP BUTTON if(playerControls.mainActions.Jump.WasReleasedThisFrame()) { if(JumpBufferTimer > 0f) { JumpReleasedDuringBuffer = true; } if(IsJumping && VerticalVelocity > 0f) { if(IsPastApexThreshold) { IsPastApexThreshold = false; IsFastFalling = true; FastFallTime = ControllStats.TimeForUpwadsCancel; VerticalVelocity = 0f; } else { IsFastFalling = true; FastFallReleaseSpeed = VerticalVelocity; } } } //INITIATE JUMP WITH JUMP BUFFERING AND COYOTE TIME if (JumpBufferTimer > 0f && !IsJumping && (IsGrounded || CoyoteTimer > 0f)) { InitiateJump(1); if (JumpReleasedDuringBuffer) { IsFastFalling = true; FastFallReleaseSpeed = VerticalVelocity; } } //DOUBLE JUMP else if (JumpBufferTimer > 0f && IsJumping && NumberOfJumpsUsed < ControllStats.NumberOfJumpsAllowed) { IsFastFalling = false; InitiateJump(1); } //AIR JUMP AFTER COYOTE TIME LAPSED else if (JumpBufferTimer > 0f && IsJumping && NumberOfJumpsUsed < ControllStats.NumberOfJumpsAllowed -1) { IsFastFalling = false; InitiateJump(2); } //LANDED if ((IsJumping || IsFalling) && IsGrounded && VerticalVelocity <= 0f) { IsJumping = false; IsFalling = false; IsFastFalling = false; FastFallTime = 0f; IsPastApexThreshold = false; NumberOfJumpsUsed = 0; VerticalVelocity = Physics2D.gravity.y; } } private void InitiateJump(int NumberOfJumpsUsed) { if(!IsJumping) { IsJumping = true; } JumpBufferTimer = 0f; this.NumberOfJumpsUsed += NumberOfJumpsUsed; VerticalVelocity = ControllStats.InitialJumpVelocity; } private void Jump() { //APPLY GRAVITY WHILE JUMPING if(IsJumping) { //CHECK FOR HEAD BUMP if(BumpedHead) { IsFastFalling = true; } //GRAVITY ON ASCENING if(VerticalVelocity >= 0f) { //APEX CONTROLLS ApexPoint = Mathf.InverseLerp(ControllStats.InitialJumpVelocity, 0f, VerticalVelocity); if(ApexPoint > ControllStats.ApexThreshold) { if(!IsPastApexThreshold) { IsPastApexThreshold = true; TimePastApexThreshold = 0f; } if(IsPastApexThreshold) { TimePastApexThreshold += Time.fixedDeltaTime; if(TimePastApexThreshold <ControllStats.ApexHangTime) { VerticalVelocity = 0f; } else { VerticalVelocity = -0.01f; } } } } //GRAVITY ON ASCENDING BUT NOT PAST APEX THRESHOLD else { VerticalVelocity += ControllStats.Gravity * Time.fixedDeltaTime; if(IsPastApexThreshold) { IsPastApexThreshold = false; } } } //GRAVITY ON DESCENDING else if(!IsFastFalling) { VerticalVelocity += ControllStats.GravityOnReleaseMultiplier * Time.fixedDeltaTime; } else if(VerticalVelocity < 0f) { if(!IsFalling) { IsFalling = true; } } //JUMP CUT if(IsFastFalling) { if(FastFallTime >= ControllStats.TimeForUpwadsCancel) { VerticalVelocity = ControllStats.Gravity * ControllStats.GravityOnReleaseMultiplier * Time.fixedDeltaTime; } else if(FastFallTime < ControllStats.TimeForUpwadsCancel) { VerticalVelocity = Mathf.Lerp(FastFallReleaseSpeed, 0f, (FastFallTime / ControllStats.TimeForUpwadsCancel)); } FastFallTime += Time.fixedDeltaTime; } //NORMAL GRAVITY WHILE FALLING if(!IsGrounded && !IsJumping) { if(!IsFalling) { IsFalling = true; } VerticalVelocity += ControllStats.Gravity * Time.fixedDeltaTime; } //CLAMP FALL SPEED VerticalVelocity = Mathf.Clamp(VerticalVelocity, -ControllStats.MaxFallSpeed, 50); rb.velocity = new Vector2(rb.velocity.x, VerticalVelocity); } private void CountTimers() { JumpBufferTimer -= Time.deltaTime; if(!IsGrounded) { CoyoteTimer -= Time.deltaTime; } else { CoyoteTimer = ControllStats.JumpCoyoteTime; } } private void Move(float Acceleration, float Deceleration, Vector2 MoveInput) { if(MoveInput != Vector2.zero) { TurnCheck(MoveInput); Vector2 TargetVelocity = Vector2.zero; if (playerControls.mainActions.Sprint.ReadValue<bool>()) { TargetVelocity = new Vector2(MoveInput.x, 0f) * ControllStats.MaxRunSpeed; } else { TargetVelocity = new Vector2(MoveInput.x, 0f) * ControllStats.MaxWalkSpeed; } MoveVelocity = Vector2.Lerp(MoveVelocity, TargetVelocity, Acceleration * Time.fixedDeltaTime); rb.velocity = new Vector2(MoveVelocity.x, rb.velocity.y); } else if(MoveInput == Vector2.zero) { MoveVelocity = Vector2.Lerp(MoveVelocity, Vector2.zero, Deceleration * Time.fixedDeltaTime); rb.velocity = new Vector2(MoveVelocity.x, rb.velocity.y); } } private void TurnCheck(Vector2 MoveInput) { if (!isFacingRight && MoveInput.x < 0) { Turn(false); } else if (!isFacingRight && MoveInput.x > 0) { Turn(true); } } private void Turn(bool TurnRight) { if(TurnRight) { isFacingRight = true; transform.Rotate(0f, 180f, 0f); } else { isFacingRight = false; transform.Rotate(0f, -180f, 0f); } } private void Grounded() { Vector2 BoxCastOrigin = new Vector2(FeetColl.bounds.center.x, FeetColl.bounds.min.y); Vector2 BoxCastSize = new Vector2(FeetColl.bounds.size.x, ControllStats.GroundDetectionRayLength); GroundHit = Physics2D.BoxCast(BoxCastOrigin, BoxCastSize, 0f, Vector2.down, ControllStats.GroundDetectionRayLength, ControllStats.GroundLayer); if (GroundHit.collider != null) { IsGrounded = true; } else { IsGrounded = false; } } private void HasBumpedHead() { Vector2 BoxCastOrigin = new Vector2(FeetColl.bounds.center.x, BodyColl.bounds.max.y); Vector2 BoxCastSize = new Vector2(FeetColl.bounds.size.x * ControllStats.HeadWidth, ControllStats.HeadDetectionRayLength); GroundHit = Physics2D.BoxCast(BoxCastOrigin, BoxCastSize, 0f, Vector2.up, ControllStats.HeadDetectionRayLength, ControllStats.GroundLayer); if (HeadHit.collider != null) { BumpedHead = true; } else { BumpedHead = false; } } private void CollisionChecks() { Grounded(); } }