Player controller
unknown
csharp
4 days ago
8.5 kB
13
Indexable
using System; using System.Collections; using UnityEngine; public class PlayerController : MonoBehaviour, IPlayerController { [SerializeField] private ScriptablePlayerMovement stats; [SerializeField] private InputManager inputManager; private Rigidbody2D _rb; private CapsuleCollider2D _capsuleCollider; private LayerMask _playerLayer; private Vector2 _velocity; private FrameInput _frameInput; private RaycastHit2D _hitGround; public Vector2 moveInput => _frameInput.Move; // Property to get current frame input public event Action<bool, float> GroundedChanged; public event Action Jumped; public event Action<bool, bool> IsMoving; // Event for movement state changes private float _time; [SerializeField] public bool isInFrontCase { private get; set; } public static PlayerController Instance; private void Awake() { if (Instance != null) { Debug.LogWarning("There is more than one instance of PlayerMovement"); return; } Instance = this; _rb = GetComponent<Rigidbody2D>(); _playerLayer = gameObject.layer; _capsuleCollider = GetComponent<CapsuleCollider2D>(); } private void Update() { _time += Time.deltaTime; ManagerInput(); } private void ManagerInput() { _frameInput = new FrameInput { JumpDown = inputManager.JumpDown, JumpHeld = inputManager.JumpHeld, Move = inputManager.MoveInput, RunInput = inputManager.RunInput }; if (_frameInput.JumpDown) { _jumpToConsume = true; _timeJumpWasPressed = _time; } } private void FixedUpdate() { CheckCollisions(); HandleJump(); HandleDirection(); HandleGravity(); ApplyMovement(); } #region Collision private bool _isGrounded = false; private float _frameLeftGrounded = float.MinValue; //Get the minimum value to be sure that under the frame private bool _stopSliding = false; // Flag to indicate if sliding should be stopped private void CheckCollisions() { //~ _playerLayer to detect all other colliders except the player and default _hitGround = Physics2D.CapsuleCast(_capsuleCollider.bounds.center, _capsuleCollider.bounds.size, CapsuleDirection2D.Vertical, 0, Vector2.down, stats.grounderDistance, ~_playerLayer); bool ceilingHit = Physics2D.CapsuleCast(_capsuleCollider.bounds.center, _capsuleCollider.bounds.size, CapsuleDirection2D.Vertical, 0, Vector2.up, stats.grounderDistance, ~_playerLayer); _isGrounded = _hitGround; if (ceilingHit) _velocity.y = Mathf.Min(0, _velocity.y); //If velocity is positive, set it to 0, otherwise keep it if (_isGrounded) { _stopSliding = Vector2.Angle(_hitGround.normal, Vector2.up) < stats.maxSlopeAngle; _coyoteUsable = true; _endedJumpEarly = false; GroundedChanged?.Invoke(true, Mathf.Abs(_velocity.y)); } else { _stopSliding = false; _frameLeftGrounded = _time; GroundedChanged?.Invoke(false, 0); } } #endregion #region Jump private bool _jumpToConsume; // Flag to indicate if jump should be consumed private bool _endedJumpEarly; // Flag to indicate if jump ended early private bool _coyoteUsable; // Flag to indicate if coyote time is usable private float _timeJumpWasPressed; // Time when jump was pressed private bool _jumpHeld; private bool CanUseCoyote => _coyoteUsable && !_isGrounded && (_time < (_frameLeftGrounded + stats.coyoteTime)); // Check if coyote time can be used private void HandleJump() { if(!_endedJumpEarly && _isGrounded && _jumpHeld && _rb.linearVelocity.y >0 ) _endedJumpEarly = true; if(!_jumpToConsume) return; if (_isGrounded || CanUseCoyote) ExecuteJump(); _jumpToConsume = false; } private void ExecuteJump() { _endedJumpEarly = false; _timeJumpWasPressed = 0; _coyoteUsable = false; _velocity.y = stats.jumpForce; // Set the vertical velocity to jump power Jumped?.Invoke(); // Invoke the jump event } #endregion #region Movement [HideInInspector] public bool isOnLadder; [HideInInspector] public bool isClimbing; private void ClimbLadder() { IsMoving?.Invoke(false, false); _velocity.x = 0; if (_frameInput.Move.y > 0) { _velocity.y = stats.ladderSpeed; } } private void Climbing() { //TODO: Detect the top of the collider and move the bottom of the player at this position //TODO: Stop the climbing after the end of the animation transform.localPosition = new Vector2(transform.localPosition.x, transform.localPosition.y + stats.climbingHeight); isClimbing = false; } private void HandleDirection() { if (isOnLadder) { ClimbLadder(); return; } if (isClimbing && !isOnLadder) { Climbing(); return; } Move(); } private void Move() { // TODO: Make velocity of the player to 0 when the player change direction (Use Vector2.Dot) if(_frameInput.Move.x == 0) { // if (_stopSliding) // { // Debug.Log("Stop sliding"); // _velocity.x = 0; // } // else // { // float deceleration = _isGrounded ? stats.groundDeceleration : stats.airDeceleration; // _velocity.x = Mathf.MoveTowards(_velocity.x, 0, deceleration * Time.fixedDeltaTime); // Decelerate to zero depending on the state // } float deceleration = _isGrounded ? stats.groundDeceleration : stats.airDeceleration; _velocity.x = Mathf.MoveTowards(_velocity.x, 0, deceleration * Time.fixedDeltaTime); // Decelerate to zero depending on the state IsMoving?.Invoke(false, false); // Invoke movement event } else { float speedToReach; bool isRunning = false; if (_frameInput.RunInput) { speedToReach = stats.maxSpeedRun; isRunning = true; } else { speedToReach = stats.maxSpeedWalk; isRunning = false; } //Know if the player is running or walking //Move the initial velocity to the max speed with acceleration _velocity.x = Mathf.MoveTowards(_velocity.x, _frameInput.Move.x * speedToReach, stats.acceleration * Time.fixedDeltaTime); IsMoving?.Invoke(true, isRunning); // Invoke movement event } } #endregion #region Gravity private void HandleGravity() { if(!_isGrounded) { //Can use *Time.deltaTime to make it gradual float inAirGravity = stats.fallAcceleration; if(_endedJumpEarly && _velocity.y > 0) inAirGravity *= stats.jumpEndEarlyGravityModifier; // Apply early jump gravity modifier //Apply fall speed limit //inAirGravity is the maximum delta that can be reach between the current and the target velocity during one frame _velocity.y = Mathf.MoveTowards(_velocity.y, -stats.maxFallSpeed, inAirGravity * Time.fixedDeltaTime); } } #endregion private void ApplyMovement() => _rb.linearVelocity = _velocity; } public struct FrameInput // Struct to hold frame input data { public bool JumpDown; // Indicates if jump button was pressed public bool JumpHeld; // Indicates if jump button is held public bool RunInput; public Vector2 Move; // Vector2 for movement input } public interface IPlayerController { public event Action<bool, float> GroundedChanged; // Event for grounded state changes public event Action Jumped; // Event for jump action public Vector2 moveInput { get; } // Property to get current frame input //First bool isMoving, second bool isRunning public event Action<bool, bool> IsMoving; // public event Action<bool> isClimbing; // public event Action<bool> isOnLadder; }
Editor is loading...
Leave a Comment