using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using _Game.Scripts.Scriptable_Object; using _Game.Scripts.Player.PlayerAttacks.Earth; [RequireComponent(typeof(Rigidbody2D), typeof(CapsuleCollider2D))] public class MovementController : MonoBehaviour { [SerializeField] private PlayerState r_State; private CapsuleCollider2D r_Collider; private Animator r_Animator; [Header("Movement")] [SerializeField] private float m_Speed = 10f; [SerializeField] private float m_JumpForce = 400f; // Amount of force added when the player jumps. [SerializeField] private float m_fallMultiplier = 2.5f; [SerializeField] private float m_lowJumpMultiplier = 2.0f; [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 bool m_CanSwim = true; [Header("Ground Check Control")] [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 float m_GroundCheckDistance = 1.5f; [SerializeField] private float m_GroundedRadius = .2f; [SerializeField] private Vector2 m_GroundedOffset = Vector2.zero; [SerializeField] private float m_SecondsWaitToJump = 0.3f; [SerializeField] private int m_MaxJumpCount = 1; private bool m_Grounded = false; // Whether or not the player is grounded. private bool m_Swimming = false; private bool m_Airborn = true; 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 int m_TempJumpCount = 0; private bool m_Jumped = false; private RaycastHit2D[] m_RaycastHits2D = new RaycastHit2D[16]; private ContactFilter2D m_ContactFilter2D; private Vector3 m_CurrentGroundNormal; //private bool m_isMoving = false; //private bool m_isJumping = false; private Vector2 m_Movement = Vector2.zero; [Header("Events")] [Space] //Wird Aktiviert wenn Spieler Landet public UnityEvent OnLandEvent; public UnityEvent OnSwimEvent; public UnityEvent OnJumpEvent; private void Awake() { m_Rigidbody2D = GetComponent(); r_Collider = GetComponent(); r_Animator = GetComponent(); UpdateMovementParameters(); m_ContactFilter2D.SetLayerMask(m_WhatIsGround); if (OnLandEvent == null) OnLandEvent = new UnityEvent(); } private void FixedUpdate() { bool wasGrounded = m_Grounded; m_Grounded = false; Vector3 col = r_Collider.offset; m_GroundCheck.position = transform.position + col; GroundCheckDistance = r_Collider.size.y / 2 * transform.localScale.y + 0.2f; // 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. Vector3 pos = m_GroundCheck.position + new Vector3(m_GroundedOffset.x, m_GroundedOffset.y, 0); var c = Physics2D.CircleCast(pos, m_GroundedRadius, Vector2.down, m_ContactFilter2D, m_RaycastHits2D, m_GroundCheckDistance); for (var i = 0; i < c; i++) { if (m_RaycastHits2D[i].transform.gameObject == gameObject) continue; m_Grounded = true; if (r_State.GetCurrentElement() != InfusedElement.Air) { m_TempJumpCount = m_MaxJumpCount; } else { m_TempJumpCount = r_State.GetCharges(); } m_CurrentGroundNormal = m_RaycastHits2D[i].normal; if (!wasGrounded) { OnLandEvent.Invoke(); } } r_Animator.SetFloat("Speed", m_Movement.magnitude); r_Animator.SetBool("isJumping", m_Jumped); r_Animator.SetBool("isAirborn", m_Airborn); r_Animator.SetBool("isSwimming", m_Swimming); } public void Move(Vector2 move, bool jump) { m_Movement = move; //only control the player if grounded or airControl is turned on if (m_Grounded || (m_AirControl && (m_Airborn && !m_Grounded)) || (m_Swimming && m_CanSwim)) { // Move the character by finding the target velocity Vector2 targetVelocity = new Vector2((move.x * m_Speed * Time.deltaTime) * 10f, m_Rigidbody2D.velocity.y); // 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.x > 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.x < 0 && m_FacingRight) { // ... flip the player. Flip(); } } // If the player should jump... if (jump) { if (m_Grounded || (m_Swimming && m_CanSwim) && !m_Jumped) { m_Grounded = false; m_Rigidbody2D.velocity = Vector2.up * m_JumpForce; OnJumpEvent.Invoke(); StartCoroutine(Wait(m_SecondsWaitToJump)); } else if ((m_TempJumpCount > 0) && !m_Jumped) { m_Grounded = false; m_TempJumpCount--; if (r_State.GetCurrentElement() == InfusedElement.Air) r_State.ChangeCharges(-1); m_Rigidbody2D.velocity = Vector2.up * m_JumpForce; OnJumpEvent.Invoke(); StartCoroutine(Wait(m_SecondsWaitToJump)); } } if (m_Rigidbody2D.velocity.y < 0 && !m_Swimming && !m_Grounded) { m_Rigidbody2D.velocity += Vector2.up * Physics2D.gravity.y * (m_fallMultiplier - 1) * Time.deltaTime; } else if (m_Rigidbody2D.velocity.y > 0 && !Input.GetButton("Jump")) { m_Rigidbody2D.velocity += Vector2.up * Physics2D.gravity.y * (m_lowJumpMultiplier - 1) * Time.deltaTime; } } public void UpdateMovementParameters() { if (GetComponentInParent().isStomping) { Invoke("UpdateMovementParameters", 0.01f); } else { MovementState state = r_State.GetCurrentMovementState(); m_JumpForce = state.jumpForce; m_MovementSmoothing = state.smoothing; m_AirControl = state.airControl; m_CanSwim = state.canSwim; m_Speed = state.moveSpeed; m_MaxJumpCount = state.maxJumpCount; m_Rigidbody2D.mass = state.mass; m_Rigidbody2D.gravityScale = state.gravity; } } public void SetSwimming(bool value) { m_Swimming = value; if (value) { OnSwimEvent.Invoke(); m_TempJumpCount = m_MaxJumpCount; } } public bool Swimming { get => m_Swimming; } public bool Grounded { get => m_Grounded; } public float GroundCheckDistance { set => m_GroundCheckDistance = value; get => m_GroundCheckDistance; } public Vector2 GroundCheckOffset { set => m_GroundedOffset = value; get => m_GroundedOffset; } public float GroundCheckRadius { set => m_GroundedRadius = value; get => m_GroundedRadius; } public void AddJumpCharge(int amount) { m_TempJumpCount += amount; } private IEnumerator Wait(float s) { m_Jumped = true; yield return new WaitForSeconds(s); m_Jumped = false; } 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 OnCollisionEnter2D(Collision2D other) { if (m_WhatIsGround == (m_WhatIsGround | (1 << other.gameObject.layer))){ m_Airborn = false; } } private void OnCollisionExit2D(Collision2D other) { if (m_WhatIsGround == (m_WhatIsGround | (1 << other.gameObject.layer))) { m_Airborn = true; } } void OnDrawGizmos() { Gizmos.color = Color.green; Vector3 pos = m_GroundCheck.position + new Vector3(m_GroundedOffset.x, m_GroundedOffset.y, 0); //Gizmos.DrawWireSphere (m_GroundCheck.position + new Vector3(m_GroundedOffset.x, m_GroundedOffset.y, 0), m_GroundedRadius); Gizmos.DrawRay(pos, Vector3.down * m_GroundCheckDistance); Gizmos.DrawWireSphere(pos + Vector3.down * m_GroundCheckDistance, m_GroundedRadius); } }