using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerMovement : MonoBehaviour
{
[SerializeField] private Rigidbody2D rb;
[SerializeField] private SpriteRenderer spriteRenderer;
[SerializeField] private Animator animator;
[Header("Debug")]
public float horizontal = 0f;
public float vertical = 0f;
public float currentSpeed;
public Text currentSpeedText;
[Header("Movement")]
public float maxSpeed = 45;
public float speed;
public float accel;
public float decel;
[SerializeField] private float turnAccel = 40f;
[Header("Jump")]
public float jumpForce;
[SerializeField] private float airAccel = 10f;
[Header("Ground Checks")]
[SerializeField] private Transform groundCheck;
[SerializeField] private float groundCheckRadius = 0.2f;
[SerializeField] private LayerMask groundLayer;
private bool isGrounded = false;
[Header("Coyote Time")]
[SerializeField] private float coyoteTime;
private float coyoteTimeCounter;
[Header("Jump Buffering")]
[SerializeField] private float jumpBufferTime;
private float jumpBufferCounter;
[Header("Jump Cut")]
[SerializeField] private float jumpCutMultiplier = 0.5f;
[Header("Fall Multiplier")]
[SerializeField] private float fallMultiplier;
[Header("BHop")]
[SerializeField] private float bhopWindow = 0.05f;
[SerializeField] private float bhopBoost = 1.05f;
[SerializeField] private float maxBhopMultiplier = 2.0f;
private int bhopChain = 0;
private float bhopMultiplier = 1f;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
if (horizontal > 0)
{
spriteRenderer.flipX = false;
}
else if (horizontal < 0)
{
spriteRenderer.flipX = true;
}
horizontal = Input.GetAxisRaw("Horizontal");
vertical = Input.GetAxisRaw("Vertical");
currentSpeed = rb.velocity.magnitude;
currentSpeedText.text = "CurrentSpeed: " + currentSpeed;
if (rb.velocity.magnitude > maxSpeed)
{
rb.velocity = rb.velocity.normalized * maxSpeed;
}
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
// coyote time check
if (isGrounded)
{
coyoteTimeCounter = coyoteTime;
rb.velocity = new Vector2(rb.velocity.x, Mathf.Max(rb.velocity.y, -2f));
}
else
{
coyoteTimeCounter -= Time.deltaTime;
}
// jump buffer time check
if (Input.GetButtonDown("Jump"))
{
jumpBufferCounter = jumpBufferTime;
}
else
{
jumpBufferCounter -= Time.deltaTime;
}
// jump buffer time check
if (Input.GetButtonUp("Jump") && rb.velocity.y > 0f)
{
rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * jumpCutMultiplier);
}
// does the juump
if (jumpBufferCounter > 0f && coyoteTimeCounter > 0f)
{
Jump();
jumpBufferCounter = 0f;
}
// more gravity when fall
if (rb.velocity.y < 0)
{
rb.velocity += Vector2.up * Physics2D.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
}
// Update horizontal input
horizontal = Input.GetAxisRaw("Horizontal");
// Pass speed to animator
animator.SetFloat("Speed", Mathf.Abs(rb.velocity.x));
// Adjust animation speed based on movement
float animSpeed = Mathf.Abs(rb.velocity.x) / speed;
animator.speed = Mathf.Clamp(animSpeed, 0.5f, 2f);
}
void FixedUpdate()
{
float targetX = horizontal * speed * bhopMultiplier;
float accelRate;
if (Mathf.Abs(horizontal) > 0.01f)
{
if (isGrounded)
{
// Stronger turning accel on ground
if (Mathf.Sign(horizontal) != Mathf.Sign(rb.velocity.x) && Mathf.Abs(rb.velocity.x) > 0.1f)
accelRate = turnAccel;
else
accelRate = accel;
}
else
{
// In air → weaker accel
accelRate = airAccel;
}
float newX = Mathf.MoveTowards(rb.velocity.x, targetX, accelRate * Time.fixedDeltaTime);
rb.velocity = new Vector2(newX, rb.velocity.y);
}
else if (isGrounded)
{
float newX = Mathf.MoveTowards(rb.velocity.x, 0, decel * Time.fixedDeltaTime);
rb.velocity = new Vector2(newX, rb.velocity.y);
}
}
public void Jump()
{
bool perfectBhop = (coyoteTimeCounter < bhopWindow && rb.velocity.magnitude > speed * 0.8f);
if (perfectBhop)
{
bhopChain++;
bhopMultiplier = Mathf.Min(bhopMultiplier * bhopBoost, maxBhopMultiplier);
rb.velocity = new Vector2(rb.velocity.x * bhopMultiplier, jumpForce);
}
else
{
bhopChain = 0;
bhopMultiplier = 1f;
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}
}
}