using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; public class PlayerMovement : MonoBehaviour { public Rigidbody2D rb; // Player's Rigidbody2D component public Animator animator; // Animator for controlling animations private bool isFacingRight = true; // Tracks which direction the player is facing [Header("Movement")] public float moveSpeed = 5f; // Horizontal movement speed private float horizontalMovement; // Input value for horizontal movement [Header("Jumping")] public float jumpPower = 10f; // Force applied when jumping public int maxJumps = 2; // Maximum allowed jumps (for double jump) private int jumpsRemaining; // Remaining jumps [Header("Ground Check")] public Transform groundCheckPos; // Position to check for ground public Vector2 groundCheckSize = new Vector2(0.5f, 0.05f); // Size of ground check box public LayerMask groundLayer; // Ground layer for collision check private bool isGrounded; // Whether the player is on the ground [Header("Gravity")] public float baseGravity = 2f; // Base gravity scale public float maxFallSpeed = 18f; // Maximum downward fall speed public float fallSpeedMultiplier = 2f; // Extra gravity applied while falling [Header("Wall Check")] public Transform WallCheckPos; // Position to check for wall contact public Vector2 WallCheckSize = new Vector2(0.5f, 0.05f); // Size of wall check box public LayerMask WallLayer; // Wall layer for collision check [Header("Wall Movement")] public float wallSlideSpeed = 2f; // Downward slide speed when on wall private bool isWallSliding; // Whether the player is currently sliding on a wall private bool isWallJumping; // Whether the player is currently wall-jumping private float walljumpDirection; // Direction away from wall public float wallJumpTime = 0.5f; // Grace period to trigger wall jump private float wallJumpTimer; // Timer countdown for wall jump public Vector2 wallJumpPower = new Vector2(5f, 10f); // Force applied during wall jump private void Start() { isFacingRight = transform.localScale.x > 0; // Set initial facing direction } private void Update() { GroundCheck(); // Check if player is on ground ProcessGravity(); // Adjust gravity/fall behavior ProcessWallSlide(); // Handle wall slide state ProcessWallJump(); // Handle wall jump timing if (!isWallJumping) { rb.linearVelocity = new Vector2(horizontalMovement * moveSpeed, rb.linearVelocity.y); // Apply horizontal movement Flip(); // Flip sprite direction based on movement } // Update animation parameters animator.SetFloat("yVelocity", rb.linearVelocity.y); animator.SetFloat("magnitude", Mathf.Abs(rb.linearVelocity.x)); animator.SetBool("isWallSliding", isWallSliding); } /// /// Called when movement input is received /// public void Move(InputAction.CallbackContext context) { horizontalMovement = context.ReadValue().x; // Read horizontal input } /// /// Called when jump input is received /// public void Jump(InputAction.CallbackContext context) { if (context.performed) { if (wallJumpTimer > 0f) { // Perform wall jump isWallJumping = true; rb.linearVelocity = new Vector2(walljumpDirection * wallJumpPower.x, wallJumpPower.y); wallJumpTimer = 0; animator.SetTrigger("jump"); // Flip character if jumping in opposite direction if ((walljumpDirection > 0 && !isFacingRight) || (walljumpDirection < 0 && isFacingRight)) { isFacingRight = !isFacingRight; Vector3 ls = transform.localScale; ls.x *= -1f; transform.localScale = ls; } Invoke(nameof(CancelWallJump), wallJumpTime + 0.1f); // End wall jump after delay } else if (jumpsRemaining > 0) { // Normal jump rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpPower); jumpsRemaining--; animator.SetTrigger("jump"); } } // Apply variable jump height when button is released if (context.canceled && rb.linearVelocity.y > 0) { rb.linearVelocity = new Vector2(rb.linearVelocity.x, rb.linearVelocity.y * 0.5f); } } /// /// Checks if the player is grounded /// private void GroundCheck() { isGrounded = Physics2D.OverlapBox(groundCheckPos.position, groundCheckSize, 0, groundLayer); if (isGrounded) { jumpsRemaining = maxJumps; // Reset jumps when on ground } } /// /// Checks if the player is touching a wall /// private bool WallCheck() { return Physics2D.OverlapBox(WallCheckPos.position, WallCheckSize, 0, WallLayer); } /// /// Adjusts gravity for falling or rising states /// private void ProcessGravity() { if (rb.linearVelocity.y < 0) { rb.gravityScale = baseGravity * fallSpeedMultiplier; rb.linearVelocity = new Vector2(rb.linearVelocity.x, Mathf.Max(rb.linearVelocity.y, -maxFallSpeed)); } else { rb.gravityScale = baseGravity; } } /// /// Handles wall slide behavior /// private void ProcessWallSlide() { if (!isGrounded && WallCheck() && horizontalMovement != 0) { isWallSliding = true; rb.linearVelocity = new Vector2(rb.linearVelocity.x, Mathf.Max(rb.linearVelocity.y, -wallSlideSpeed)); } else { isWallSliding = false; } } /// /// Handles timing logic and setup for wall jump /// private void ProcessWallJump() { if (isWallSliding) { isWallJumping = false; walljumpDirection = -Mathf.Sign(transform.localScale.x); // Set direction away from wall wallJumpTimer = wallJumpTime; CancelInvoke(nameof(CancelWallJump)); } else if (wallJumpTimer > 0f) { wallJumpTimer -= Time.deltaTime; // Decrease wall jump timer } } /// /// Cancels wall jump state after timeout /// private void CancelWallJump() { isWallJumping = false; } /// /// Flips the character sprite based on movement direction /// private void Flip() { if ((isFacingRight && horizontalMovement < 0) || (!isFacingRight && horizontalMovement > 0)) { isFacingRight = !isFacingRight; Vector3 ls = transform.localScale; ls.x *= -1f; transform.localScale = ls; } } /// /// Draws gizmos in the editor for ground and wall checks /// private void OnDrawGizmosSelected() { Gizmos.color = Color.white; if (groundCheckPos != null) Gizmos.DrawWireCube(groundCheckPos.position, groundCheckSize); Gizmos.color = Color.blue; if (WallCheckPos != null) Gizmos.DrawWireCube(WallCheckPos.position, WallCheckSize); } }