using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; /* -------------------------------------- Project: Programing assessment Standard: 91906 (AS3.7) v.1 School: Tauranga Boys' College Author: Rauputu Noah Phizacklea Date: August 2024 Unity: 2021.3.18f --------------------------------------- */ public class PlayerMovement : MonoBehaviour { [SerializeField] private Transform groundCheck; // Transform to check if the player is grounded [SerializeField] private LayerMask groundLayer; // Layer mask for ground detection [SerializeField] private Rigidbody2D myRigidbody; // Reference to the Rigidbody2D component [SerializeField] private Transform wallCheck; // Transform to check if the player is against a wall [SerializeField] private LayerMask wallLayer; // Layer mask for wall detection [SerializeField] private StaminaBar staminaBar; // Reference to the stamina bar UI public Animator myAnimator; // Reference to the Animator component private float horizontal; // Horizontal movement input private float speed = 8f; // Movement speed private float jumpingPower = 16f; // Jumping power private bool isFacingRight = true; // Whether the player is facing right private bool canRoll = true; // Whether the player can roll private bool isRolling; // Whether the player is currently rolling private float rollingPower = 24f; // Power of the roll private float rollingTime = 0.2f; // Duration of the roll private float rollingCooldown = 1f; // Cooldown time before rolling again private bool isWallSliding; // Whether the player is sliding down a wall private float wallSlidingSpeed = 2f; // Speed at which the player slides down a wall private bool isWallJumping; // Whether the player is performing a wall jump private float wallJumpingDirection; // Direction of the wall jump private float wallJumpingTime = 0.2f; // Time during which wall jumping is possible private float wallJumpingCounter; // Counter to track wall jump time private float wallJumpingDuration = 0.4f; // Duration of the wall jump private Vector2 wallJumpingPower = new Vector2(8f, 16f); // Power of the wall jump private bool doubleJump; // Whether the player can double jump private float coyoteTime = 0.2f; // Time after leaving the ground where jumping is still allowed private float coyoteTimeCounter; // Counter for coyote time private float jumpBufferTime = 0.2f; // Time to allow jump input after leaving the ground private float jumpBufferCounter; // Counter for jump buffer time public int rollCost = 30; // Stamina cost for rolling private PlayerStamina playerStamina; // Reference to the PlayerStamina component Vector2 moveInput; // Movement input vector CapsuleCollider2D myBodyCollider; // Reference to the CapsuleCollider2D component private bool isMovementDisabled; // Whether player movement is currently disabled // Original collider size and offset private Vector2 originalColliderSize; private Vector2 originalColliderOffset; // Rolling collider size and offset private Vector2 rollingColliderSize = new Vector2(0.5f, 0.3f); // Adjust as necessary private Vector2 rollingColliderOffset = new Vector2(0f, 0.4f); // Adjust as necessary AudioManager audioManager; // Reference to the AudioManager component void Awake() { // Initialize audio manager audioManager = GameObject.FindGameObjectWithTag("Audio").GetComponent(); } void Start() { // Initialize components and variables myAnimator = GetComponent(); myBodyCollider = GetComponent(); playerStamina = GetComponent(); originalColliderSize = myBodyCollider.size; // Save original collider size originalColliderOffset = myBodyCollider.offset; // Save original collider offset } void Update() { // Skip update if movement is disabled or player is rolling if (isMovementDisabled || isRolling) { return; } // Update animator and rolling capability based on ground state if (!isGrounded()) { myAnimator.SetBool("isJumping", true); canRoll = false; } // Get horizontal input for movement horizontal = Input.GetAxisRaw("Horizontal"); // Reset double jump if grounded and jump button not pressed if (isGrounded() && !Input.GetButton("Jump")) { doubleJump = false; } // Update state if grounded if (isGrounded()) { canRoll = true; myAnimator.SetBool("isJumping", false); coyoteTimeCounter = coyoteTime; // Reset coyote time } else { coyoteTimeCounter -= Time.deltaTime; // Decrease coyote time } // Handle jump input buffering if (Input.GetButtonDown("Jump")) { jumpBufferCounter = jumpBufferTime; // Start jump buffer timer } else { jumpBufferCounter -= Time.deltaTime; // Decrease jump buffer timer } // Handle roll input and stamina usage if (Input.GetKeyDown(KeyCode.LeftShift) && canRoll && playerStamina.UseStamina(rollCost)) { StartCoroutine(Roll()); // Start rolling coroutine } // Handle jumping with buffer and coyote time if (jumpBufferCounter > 0f && (coyoteTimeCounter > 0f || doubleJump)) { myRigidbody.velocity = new Vector2(myRigidbody.velocity.x, jumpingPower); // Apply jump force jumpBufferCounter = 0f; // Reset jump buffer doubleJump = !doubleJump; // Toggle double jump } // Modify jump if jump button released early if (Input.GetButtonUp("Jump") && myRigidbody.velocity.y > 0f) { myRigidbody.velocity = new Vector2(myRigidbody.velocity.x, myRigidbody.velocity.y * 0.5f); // Reduce jump height coyoteTimeCounter = 0f; // Reset coyote time } // Handle wall sliding and wall jumping WallSlide(); WallJump(); // Flip player sprite based on movement direction if (!isWallJumping) { Flip(); } // Update running animation based on movement UpdateRunningAnimation(); } void FixedUpdate() { // Ensure rolling animation overrides running animation if (myAnimator.GetBool("isRolling")) { myAnimator.SetBool("isRunning", false); } // Skip FixedUpdate if movement is disabled or player is rolling if (isMovementDisabled || isRolling) { return; } // Apply movement velocity if not wall jumping if (!isWallJumping) { myRigidbody.velocity = new Vector2(horizontal * speed, myRigidbody.velocity.y); } } // Check if player is grounded private bool isGrounded() { return Physics2D.OverlapCircle(groundCheck.position, 0.2f, groundLayer); } // Check if player is against a wall private bool isWalled() { return Physics2D.OverlapCircle(wallCheck.position, 0.2f, wallLayer); } // Handle wall sliding mechanics private void WallSlide() { if (isWalled() && !isGrounded() && horizontal != 0f) { isWallSliding = true; myRigidbody.velocity = new Vector2(myRigidbody.velocity.x, Mathf.Clamp(myRigidbody.velocity.y, -wallSlidingSpeed, float.MaxValue)); } else { isWallSliding = false; } } // Handle wall jumping mechanics private void WallJump() { if (isWallSliding) { isWallJumping = false; wallJumpingDirection = -transform.localScale.x; wallJumpingCounter = wallJumpingTime; // Reset wall jumping timer CancelInvoke(nameof(StopWallJumping)); // Cancel previous wall jump invocations } else { wallJumpingCounter -= Time.deltaTime; // Decrease wall jumping timer } // Perform wall jump if jump button pressed and wall jump time is available if (Input.GetButtonDown("Jump") && wallJumpingCounter > 0f) { isWallJumping = true; myRigidbody.velocity = new Vector2(wallJumpingDirection * wallJumpingPower.x, wallJumpingPower.y); // Apply wall jump force wallJumpingCounter = 0f; // Reset wall jumping timer // Flip player sprite based on wall jump direction if (transform.localScale.x != wallJumpingDirection) { isFacingRight = !isFacingRight; Vector3 localScale = transform.localScale; localScale.x *= -1f; transform.localScale = localScale; } Invoke(nameof(StopWallJumping), wallJumpingDuration); // Stop wall jumping after duration } } // Stop wall jumping private void StopWallJumping() { isWallJumping = false; } // Flip player sprite based on movement direction private void Flip() { if (isFacingRight && horizontal < 0f || !isFacingRight && horizontal > 0f) { isFacingRight = !isFacingRight; Vector3 localScale = transform.localScale; localScale.x *= -1f; transform.localScale = localScale; } } // Handle rolling mechanic private IEnumerator Roll() { audioManager.PlaySFX(audioManager.rollSFX); // Play roll sound effect canRoll = false; isRolling = true; float orgGravity = myRigidbody.gravityScale; // Save original gravity scale myRigidbody.gravityScale = 0f; // Disable gravity during roll myRigidbody.velocity = new Vector2(transform.localScale.x * rollingPower, 0f); // Apply roll force myAnimator.SetBool("isRolling", true); // Set rolling animation // Adjust collider size and offset for rolling myBodyCollider.size = rollingColliderSize; myBodyCollider.offset = rollingColliderOffset; Physics2D.IgnoreLayerCollision(gameObject.layer, LayerMask.NameToLayer("Enemy"), true); // Ignore collisions with enemies yield return new WaitForSeconds(rollingTime); // Wait for roll duration // Restore original collider size and offset myBodyCollider.size = originalColliderSize; myBodyCollider.offset = originalColliderOffset; Physics2D.IgnoreLayerCollision(gameObject.layer, LayerMask.NameToLayer("Enemy"), false); // Resume collisions with enemies myRigidbody.gravityScale = orgGravity; // Restore original gravity scale isRolling = false; myAnimator.SetBool("isRolling", false); // Stop rolling animation yield return new WaitForSeconds(rollingCooldown); // Wait for roll cooldown canRoll = true; } // Update running animation based on horizontal speed private void UpdateRunningAnimation() { bool playerHasHorizontalSpeed = Mathf.Abs(horizontal) > Mathf.Epsilon; myAnimator.SetBool("isRunning", playerHasHorizontalSpeed); } // Disable player movement public void DisableMovement() { isMovementDisabled = true; myRigidbody.velocity = Vector2.zero; // Stop player movement myAnimator.SetBool("isRunning", false); // Stop running animation myAnimator.SetBool("isJumping", false); // Stop jumping animation myAnimator.SetBool("isRolling", false); // Stop rolling animation } // Enable player movement public void EnableMovement() { isMovementDisabled = false; } }