First of all I want to apologise for my English cause I come from Poland and I'm still learning. I'm a beginner C#/Unity programmer and I have a pretty stupid/noob question/issue where player jumps twice when mashing the space. When the framerate is low for ex. 30, the problem occurs almost everytime and when framerate is for ex. 144 - hardly ever. I did some research and tried different methods. Firstly I checked whether I have all my inputs in Update and not FixedUpdate. That wasn't the problem at all. Then I tried replacing Input.GetKey to Input.GetKeyDown and GetKeyUp in Update as to ensure I have my boolean _spacePressed checked or not. That wasn't the solution too. The third thing I tried was to use Raycasting to check whether the player is grounded. With that I also checked whether when I jump, ray doesn't get checked twice. To make things clear I'm currently trying to make a 2.5D platformer. So to sum up I'm asking what could be the main issue (with input probably) where player jumps twice or even three times in a single frame when mashing space. Here's also my prototype code. I used enums to make a simple state "machine". If you have also any advice for me and my code to make things better apart from my question I would love to hear them. Thank you in advance!
using UnityEngine;
public class Movement : MonoBehaviour
{
[Header("Start Properties")]
[SerializeField] private Transform _playerTransform;
[SerializeField] private Rigidbody _playerRigidbody;
private enum MovementStates { reset, idle, walking, walkingInAir, sprinting, jumping };
private MovementStates _currentState;
[Header("Player Height Adjustables")]
[SerializeField] CapsuleCollider _playerCollider;
[SerializeField] private float _reducedHeight;
private float _originalHeight;
[Header("Player Rotation Adjustables")]
[SerializeField] private float _rotationSpeed;
Quaternion _from;
Quaternion _to;
[Header("Input Properties")]
private float _xAxis;
private bool _rightLook, _leftLook;
private bool _idle, _walking, _walkingInAir, _sprinting, _reset;
private bool _leftShiftPressed;
private bool _spacePressed;
[Header("Player Movement Adjustables")]
[SerializeField] private float _walkingSpeedMultiplier;
[SerializeField] private float _maximumWalkingSpeed;
[SerializeField] private float _walkingInAirSpeedMultiplier;
[SerializeField] private float _maximumWalkingInAirSpeed;
[SerializeField] private float _sprintingSpeedMultiplier;
[SerializeField] private float _maximumSprintingSpeed;
[SerializeField] private float _deaccelerationMultiplier;
[Header("Smooth Damp Adjustables")]
[SerializeField] private float _smoothTime;
private Vector3 _currentVelocity;
private Vector3 _smoothAxis;
[Header("Player Jump Adjustables")]
[SerializeField] private float _jumpMultiplier;
private bool _jumping;
[Header("Ground Check Adjustables")]
[SerializeField] private LayerMask _groundCheck_Layer;
[SerializeField] private float _distanceGroundCheck;
private Ray _groundCheck_Ray;
private RaycastHit _groundCheck_HitInfo;
private bool _grounded;
private void Awake()
{
_originalHeight = _playerCollider.height;
}
// input and ground checks
private void Update()
{
Process_Input();
Process_Rotation();
GroundRay_Check();
}
private void Process_Input()
{
_xAxis = Input.GetAxisRaw("Horizontal");
_rightLook = (_xAxis > 0) ? _rightLook = true : _rightLook = false;
_leftLook = (_xAxis < 0) ? _leftLook = true : _leftLook = false;
if (Input.GetKeyDown(KeyCode.LeftShift)) _leftShiftPressed = true;
if (Input.GetKeyUp(KeyCode.LeftShift)) _leftShiftPressed = false;
if (Input.GetButtonDown("Jump")) _spacePressed = true;
if (Input.GetButtonUp("Jump")) _spacePressed = false;
_idle = (_xAxis == 0 && _grounded) ? _idle = true : _idle = false;
_walking = (_xAxis != 0 && !_leftShiftPressed && _grounded) ? _walking = true : _walking = false;
_walkingInAir = (_xAxis != 0 && !_grounded) ? _walkingInAir = true : _walkingInAir = false;
_sprinting = (_xAxis != 0 && _leftShiftPressed && _grounded) ? _sprinting = true : _sprinting = false;
_jumping = (_spacePressed && _grounded) ? _jumping = true : _jumping = false;
_reset = (!_idle && !_walking && !_walkingInAir && !_sprinting && !_jumping) ? _reset = true : _reset = false;
if (Input.GetKeyDown(KeyCode.Alpha1)) Application.targetFrameRate = 30;
if (Input.GetKeyDown(KeyCode.Alpha2)) Application.targetFrameRate = 60;
if (Input.GetKeyDown(KeyCode.Alpha3)) Application.targetFrameRate = 120;
if (Input.GetKeyDown(KeyCode.Alpha4)) Application.targetFrameRate = 144;
}
private void Process_Rotation()
{
if (_rightLook)
{
_from = _playerTransform.rotation;
_to = Quaternion.Euler(0f, 0f, 0f);
_playerTransform.rotation = Quaternion.Lerp(_from, _to, _rotationSpeed * Time.deltaTime);
}
else if (_leftLook)
{
_from = _playerTransform.rotation;
_to = Quaternion.Euler(0f, 180f, 0f);
_playerTransform.rotation = Quaternion.Lerp(_from, _to, _rotationSpeed * Time.deltaTime);
}
}
private void GroundRay_Check()
{
_groundCheck_Ray.origin = _playerTransform.position;
_groundCheck_Ray.direction = Vector2.down;
Debug.DrawRay(_playerTransform.position, Vector2.down * _distanceGroundCheck, Color.green);
_grounded = Physics.Raycast(_groundCheck_Ray, out _groundCheck_HitInfo, _distanceGroundCheck, _groundCheck_Layer, QueryTriggerInteraction.Ignore);
}
// movement by states
private void FixedUpdate()
{
Process_States();
}
private void Process_States()
{
if (_idle) _currentState = MovementStates.idle;
if (_walking) _currentState = MovementStates.walking;
if (_walkingInAir) _currentState = MovementStates.walkingInAir;
if (_sprinting) _currentState = MovementStates.sprinting;
if (_jumping) _currentState = MovementStates.jumping;
if (_reset) _currentState = MovementStates.reset;
switch (_currentState)
{
case MovementStates.idle:
Process_Idle();
break;
case MovementStates.walking:
Process_Walking();
break;
case MovementStates.walkingInAir:
Process_WalkingInAir();
break;
case MovementStates.sprinting:
Process_Sprinting();
break;
case MovementStates.jumping:
Process_Jumping();
break;
case MovementStates.reset:
print("resetting");
return;
}
}
private void Process_Idle()
{
print("currently idle");
_playerCollider.height = _originalHeight;
Deaccelerate(_deaccelerationMultiplier);
}
private void Process_Walking()
{
print("currently walking");
Move(_walkingSpeedMultiplier, _maximumWalkingSpeed, ForceMode.Force);
}
private void Process_WalkingInAir()
{
print("currently walking in air");
Move(_walkingInAirSpeedMultiplier, _maximumWalkingInAirSpeed, ForceMode.Force);
}
private void Process_Sprinting()
{
print("currently sprinting");
Move(_sprintingSpeedMultiplier, _maximumSprintingSpeed, ForceMode.Force);
}
private void Process_Jumping()
{
print("currently jumping");
Jump(_jumpMultiplier, ForceMode.VelocityChange);
_spacePressed = false;
}
// movement functions
private void Move(float _speedMultiplier, float _maximumSpeed, ForceMode _forceMode)
{
CapMaximumSpeed(_maximumSpeed);
Vector2 _getAxisDirection = _xAxis * Vector2.right;
Vector2 _normalizeAxis = _getAxisDirection.normalized;
Vector2 _multiplyAxis = _normalizeAxis * _speedMultiplier * Time.deltaTime;
_smoothAxis = Vector3.SmoothDamp(_multiplyAxis, _smoothAxis, ref _currentVelocity, _smoothTime);
_playerRigidbody.AddForce(_smoothAxis, _forceMode);
}
private void CapMaximumSpeed(float _maximumSpeed)
{
float _cappedXVelocity = Mathf.Min(Mathf.Abs(_playerRigidbody.velocity.x), _maximumSpeed) * Mathf.Sign(_playerRigidbody.velocity.x);
float _cappedYVelocity = _playerRigidbody.velocity.y;
_playerRigidbody.velocity = new Vector3(_cappedXVelocity, _cappedYVelocity, 0);
}
private void Deaccelerate(float _deaccelerationMultiplier)
{
float _currentSpeed = _playerRigidbody.velocity.magnitude;
float _newSpeed = _currentSpeed - _deaccelerationMultiplier;
if (_newSpeed < 0) _newSpeed = 0;
_playerRigidbody.velocity = _playerRigidbody.velocity.normalized * _newSpeed;
}
private void Jump(float _jumpMultiplier, ForceMode _forceMode)
{
Vector2 _direction = Vector2.up;
Vector2 _multiplyDirection = _direction * _jumpMultiplier;
_playerRigidbody.AddForce(_multiplyDirection, _forceMode);
}
}
CodePudding user response:
Keep in mind that FixedUpdate()
can happen a few times within a single frame. Check if _spacePressed == true
in the beginning of your Process_Jumping()
.