using UnityEngine; using UnityEngine.Events; public class CharacterController2D : MonoBehaviour { [SerializeField] private float m_JumpForce = 400f; // Amount of force added when the player jumps. [SerializeField] private float m_WallJumpMultiplier = 3f; // Amount of force added when the player jumps off a wall. [Range(0, 1)] [SerializeField] private float m_CrouchSpeed = .36f; // Amount of maxSpeed applied to crouching movement. 1 = 100% [Range(0, .3f)] [SerializeField] private float m_MovementSmoothing = .05f; // How much to smooth out the movement [SerializeField] private bool m_AirControl = false; // Whether or not a player can steer while jumping; [SerializeField] private LayerMask m_WhatIsGround; // A mask determining what is ground to the character [SerializeField] private Transform m_GroundCheck; // A position marking where to check if the player is grounded. [SerializeField] private Transform m_CeilingCheck; // A position marking where to check for ceilings [SerializeField] private Transform m_WallCheck; [SerializeField] private Collider2D m_CrouchDisableCollider; // A collider that will be disabled when crouching [SerializeField] private float m_wallFriction = 0.1f; const float k_GroundedRadius = .2f; // Radius of the overlap circle to determine if grounded private bool m_Grounded; // Whether or not the player is grounded. private bool m_TouchingWall; const float k_CeilingRadius = .2f; // Radius of the overlap circle to determine if the player can stand up private Rigidbody2D m_Rigidbody2D; private bool m_FacingRight = true; // For determining which way the player is currently facing. private Vector3 m_Velocity = Vector3.zero; private enum Direction { NA=0, L=-1, R=1 }; private Direction m_LastWallJumpDirection = Direction.NA; private int m_forward => (int) Mathf.Sign(transform.localScale.x); [Header("Events")] [Space] public UnityEvent OnJumpEvent; public UnityEvent OnLandEvent; [System.Serializable] public class BoolEvent : UnityEvent { } public BoolEvent OnCrouchEvent; private bool m_wasCrouching = false; private bool m_isJumpDown; private bool m_lastJump; private float m_lastWallJumpTime = 0; private float m_lastMove; private bool m_invertX; private void Awake() { m_Rigidbody2D = GetComponent(); if (OnLandEvent == null) OnLandEvent = new UnityEvent(); if (OnCrouchEvent == null) OnCrouchEvent = new BoolEvent(); } private void FixedUpdate() { CheckGrounded(); CheckTouchingWall(); } private void CheckGrounded() { bool wasGrounded = m_Grounded; m_Grounded = false; Collider2D[] colliders = Physics2D.OverlapCircleAll(m_GroundCheck.position, k_GroundedRadius, m_WhatIsGround); for (int i = 0; i < colliders.Length; i++) { if (colliders[i].gameObject != gameObject) { m_Grounded = true; if (!wasGrounded) OnLandEvent.Invoke(); } } } private void CheckTouchingWall() { m_TouchingWall = false; if(!m_Grounded) { if(Physics2D.OverlapCircle(m_WallCheck.position, 0.2f, m_WhatIsGround)) { m_TouchingWall = true; } } } public void Move(float move, bool crouch, bool jump) { // If crouching, check to see if the character can stand up if (!crouch) { // If the character has a ceiling preventing them from standing up, keep them crouching { crouch = true; } } m_isJumpDown = !m_lastJump && jump; m_lastJump = jump; bool hasMoveChanged = System.Math.Sign(m_lastMove) != System.Math.Sign(move); m_lastMove = move; Debug.Log($"move Changed {hasMoveChanged}"); //only control the player if grounded or airControl is turned on if (m_Grounded || m_AirControl) { // If crouching if (crouch) { if (!m_wasCrouching) { m_wasCrouching = true; OnCrouchEvent.Invoke(true); } // Reduce the speed by the crouchSpeed multiplier move *= m_CrouchSpeed; // Disable one of the colliders when crouching if (m_CrouchDisableCollider != null) m_CrouchDisableCollider.enabled = false; } else { // Enable the collider when not crouching if (m_CrouchDisableCollider != null) m_CrouchDisableCollider.enabled = true; if (m_wasCrouching) { m_wasCrouching = false; OnCrouchEvent.Invoke(false); } } // Move the character by finding the target velocity Vector3 targetVelocity = new Vector2(move * 10f, m_Rigidbody2D.velocity.y); if (m_invertX) { Debug.Log("Inverting Direction"); targetVelocity *= new Vector2(-1, 1); } if (hasMoveChanged || m_Grounded) m_invertX = false; // And then smoothing it out and applying it to the character m_Rigidbody2D.velocity = Vector3.SmoothDamp(m_Rigidbody2D.velocity, targetVelocity, ref m_Velocity, m_MovementSmoothing); // If the input is moving the player right and the player is facing left... if (move > 0 && !m_FacingRight) { // ... flip the player. Flip(); } // Otherwise if the input is moving the player left and the player is facing right... else if (move < 0 && m_FacingRight) { // ... flip the player. Flip(); } } // If the player should jump... if (m_Grounded && m_isJumpDown) { // Add a vertical force to the player. m_Grounded = false; m_Rigidbody2D.AddForce(new Vector2(0f, m_JumpForce)); OnJumpEvent.Invoke(); m_LastWallJumpDirection = Direction.NA; Debug.Log("Regular Jump"); } //else if (m_TouchingWall && jump) //{ // if (m_FacingRight && m_LastWallJumpDirection == Direction.L) // { // m_Rigidbody2D.AddForce(new Vector2(m_JumpForce * m_WallJumpMultiplier * (m_FacingRight ? 1f : -1f), m_JumpForce * m_WallJumpMultiplier)); // OnJumpEvent.Invoke(); // // m_LastWallJumpDirection = Direction.R; // } // else if (!m_FacingRight && m_LastWallJumpDirection == Direction.R) // { // m_Rigidbody2D.AddForce(new Vector2(m_JumpForce * m_WallJumpMultiplier * (m_FacingRight ? 1f : -1f), m_JumpForce * m_WallJumpMultiplier)); // OnJumpEvent.Invoke(); // // m_LastWallJumpDirection = Direction.L; // } // //} else if(m_TouchingWall && m_isJumpDown && (int) m_LastWallJumpDirection != m_forward) { m_Rigidbody2D.AddForce(new Vector2(m_WallJumpMultiplier * -m_forward, m_WallJumpMultiplier),ForceMode2D.Impulse); OnJumpEvent.Invoke(); m_LastWallJumpDirection = (m_forward == -1) ? Direction.L : Direction.R; Debug.Log("Wall Jump"); m_lastWallJumpTime = Time.time; m_invertX = true; } if (m_TouchingWall && move != 0 && !m_Grounded && m_Rigidbody2D.velocity.y < 0) { Vector2 velocity = m_Rigidbody2D.velocity; velocity.y = Mathf.Lerp(velocity.y, 0, m_wallFriction); m_Rigidbody2D.velocity = velocity; } } private void Flip() { // Switch the way the player is labelled as facing. m_FacingRight = !m_FacingRight; // Multiply the player's x local scale by -1. Vector3 theScale = transform.localScale; theScale.x *= -1; transform.localScale = theScale; } private void OnDrawGizmosSelected() { Gizmos.color = m_TouchingWall ? Color.yellow : Color.green; Gizmos.DrawWireSphere(m_WallCheck.position, 0.2f); Gizmos.color = m_Grounded ? Color.yellow : Color.green; Gizmos.DrawWireSphere(m_GroundCheck.position, k_GroundedRadius); } }