using UnityEngine; using UnityEngine.Events; public class CharacterController2D : MonoBehaviour { [SerializeField] private float m_JumpForce = 50f; // Amount of force added when the player jumps. [Range(0, .3f)] [SerializeField] private float m_RunSmoothing = .15f; // How much to smooth out the movement [Range(0, .3f)] [SerializeField] private float m_DashSmoothing = .3f; // How much to smooth out the movement [SerializeField] private bool m_AirControl = false; // Whether or not a player can steer while jumping; [SerializeField] public LayerMask m_WhatIsGround; // A mask determining what is ground to the character [SerializeField] private Vector3 m_halfExtents; [SerializeField] public Transform m_GroundCheckBottom; // A position marking where to check if the player is grounded. [SerializeField] public Transform m_GroundCheckTop; // A position marking where to check if the player is grounded. public bool m_Grounded; // Whether or not the player is grounded. public float maxDropSpeed = 100f; public float maxHorizontalSpeed = 100f; public Rigidbody2D m_Rigidbody2D; public PlayerMovement m_PlayerMovement; public AudioSource audioSource; public AudioClip audioClipLand; public AudioClip audioClipJump; public AudioClip audioClipDoubleJump; public AudioClip audioClipWallKick; public AudioClip audioClipDash; public AudioClip audioClipDashStart; public AudioClip audioClipBlowback; public AudioClip audioClipAttack; public AudioClip audioClipFootstep1; public AudioClip audioClipFootstep2; public AudioClip audioClipExecutionCharge; public AudioClip audioClipExecution; public AudioClip audioClipArrowBarrageCharge; public AudioClip audioClipPickup; public AudioClip audioClipCraft; public AudioClip audioClipCraftFail; public AudioClip audioClipLevelUp; public float volume = 0.5f; public bool m_FacingRight = true; // For determining which way the player is currently facing. private Vector3 m_Velocity = Vector3.zero; public float lowGravity = 2; public float highGravity = 5; [Header("Events")] [Space] public UnityEvent OnLandEvent; [System.Serializable] public class BoolEvent : UnityEvent { } private void Awake() { m_Rigidbody2D = GetComponent(); if (OnLandEvent == null) OnLandEvent = new UnityEvent(); State.state.quests.RefreshAvailableQuests(); } private void FixedUpdate() { bool wasGrounded = m_Grounded; m_Grounded = false; // The player is grounded if a circlecast to the groundcheck position hits anything designated as ground // This can be done using layers instead but Sample Assets will not overwrite your project settings. // Collider2D[] colliders = Physics2D.OverlapCircleAll(m_GroundCheck.position, k_GroundedRadius, m_WhatIsGround); // Bottom ground check must touch. Top ground check (slightly above bottom of hitbox) must not touch. Collider2D[] collidersBottom = Physics2D.OverlapBoxAll(m_GroundCheckBottom.position, m_halfExtents, 0, m_WhatIsGround); Collider2D[] collidersTop = Physics2D.OverlapBoxAll(m_GroundCheckTop.position, m_halfExtents, 0, m_WhatIsGround); bool collidedBottom = false, collidedTop = false; if (m_Rigidbody2D.velocity.y <= 1) { for (int i = 0; i < collidersBottom.Length; i++) { if (collidersBottom[i].gameObject != gameObject) { collidedBottom = true; } } for (int i = 0; i < collidersTop.Length; i++) { if (collidersTop[i].gameObject != gameObject) { collidedTop = true; } } if (collidedBottom && !collidedTop) { m_Grounded = true; if (!wasGrounded) { Debug.Log("Land"); OnLandEvent.Invoke(); } } } } public void Move(float move, bool jump, bool dash = false) { // Only control the player if grounded or airControl is turned on if (m_Grounded || m_AirControl) { float intendedSpeed = move * 10f; bool shouldControlVelocity = true; // If the player is in the air and has already passed the max natural speed in that direction, // don't slow them down. if (!m_Grounded && m_AirControl && (move > 0f) == (m_Rigidbody2D.velocity.x > 0f) && (move < 0f) == (m_Rigidbody2D.velocity.x < 0f) && Mathf.Abs(m_Rigidbody2D.velocity.x) > Mathf.Abs(intendedSpeed)) { shouldControlVelocity = false; } // Blowback Shot and wall-kicking negate air control. if (!m_Grounded && (m_PlayerMovement.alreadyBlowbackShot || m_PlayerMovement.alreadyKicked)) { shouldControlVelocity = false; } Vector3 newVelocity = m_Rigidbody2D.velocity; if (shouldControlVelocity) { // Move the character by finding the target velocity Vector3 targetVelocity = new Vector2(intendedSpeed, m_Rigidbody2D.velocity.y); // And then smoothing it out and applying it to the character if (dash) { newVelocity = Vector3.SmoothDamp(m_Rigidbody2D.velocity, targetVelocity, ref m_Velocity, m_DashSmoothing); } else { newVelocity = Vector3.SmoothDamp(m_Rigidbody2D.velocity, targetVelocity, ref m_Velocity, m_RunSmoothing); } // 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(); } } newVelocity.y = Mathf.Max(m_Rigidbody2D.velocity.y, maxDropSpeed); m_Rigidbody2D.velocity = newVelocity; } // Add force at the start of your jump. if (m_Grounded && jump) { m_Grounded = false; m_Rigidbody2D.velocity = new Vector2(m_Rigidbody2D.velocity.x, m_Rigidbody2D.velocity.y + 2.0f); m_Rigidbody2D.AddForce(new Vector2(0f, m_JumpForce)); PlaySound(audioClipJump); } m_Rigidbody2D.velocity = new Vector2( Mathf.Max(Mathf.Min(m_Rigidbody2D.velocity.x, maxHorizontalSpeed), -1f * maxHorizontalSpeed), m_Rigidbody2D.velocity.y); } public 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 scale = transform.localScale; scale.x *= -1; transform.localScale = scale; } public void PlaySound(AudioClip clip) { audioSource.PlayOneShot(clip, volume); } }