using System; using UnityEngine; using UnityEngine.InputSystem; [RequireComponent(typeof(Rigidbody2D), typeof(BoxCollider2D))] public class Player : MonoBehaviour { private Rigidbody2D _rigidbody; private bool _wantsJump; private bool _isJumping; private bool _isGrounded; private float _jumpStartTime; private float _dir; private Vector3 _oldPos; public InputAction movement; public InputAction jump; public float speed = 4; [Tooltip("This is the desired height, relative to the initial jump point, in unity units (meters)")] public float jumpHeight; [Tooltip("This is how long it takes to achieve the `jumpHeight` value")] public float jumpDuration; private void Start() { _rigidbody = GetComponent(); movement.Enable(); jump.Enable(); jump.started += JumpStart; jump.canceled += JumpEnd; Application.targetFrameRate = 144; } private void JumpStart(InputAction.CallbackContext callbackContext) { _wantsJump = true; } private void JumpEnd(InputAction.CallbackContext callbackContext) { _wantsJump = false; _isJumping = false; } private void Update() { _dir = movement.ReadValue(); _oldPos = _rigidbody.position; _oldPos.x += _dir * speed * Time.deltaTime; if (_wantsJump && _isGrounded) { _isJumping = true; _isGrounded = false; _jumpStartTime = Time.time; } if (_isJumping && _wantsJump && _jumpStartTime + jumpDuration > Time.time) { _rigidbody.gravityScale = 0; var metersPerSecond = jumpHeight / jumpDuration; // This simulates "acceleration" or rather the immediate force of a jump and the falloff due to gravity, // without actually using gravity. That sounds odd, and maybe it can be improved, but this was the first // solution that gives the designer a maximum height to hit over a duration while allowing the user to // end the jump early (like most SNES era platformers). All this while still looking good or "correct". // TODO: I think the closer the end value (b in the lerp function) is to 0 the more "correct" it will look. var accelerationCoefficient = Mathf.Lerp(1.5f, 0.5f, (Time.time - _jumpStartTime) / jumpDuration); _oldPos.y += accelerationCoefficient * metersPerSecond * Time.deltaTime; } else { _rigidbody.gravityScale = 1; } _rigidbody.position = _oldPos; } private void OnCollisionEnter2D(Collision2D other) { foreach (var contacts in other.contacts) { if (contacts.point.y < _rigidbody.position.y) { // we are grounded and can exit! _isGrounded = true; return; } } } }