I’m making a 2D platformer, and this is the player script:
public float moveSpeed;
public float accelerationMod;
public float jumpForce;
public float smashForce;
public float diveForce;
public LayerMask ground;
private Rigidbody2D Rigidbody;
private float horizontalInput;
private Vector2 direction;
private bool onGround;
private bool ignoreHorizontalInput;
private bool diving;
void Start()
{
Rigidbody = GetComponent<Rigidbody2D>();
}
void Update()
{
if (!ignoreHorizontalInput)
{
horizontalInput = Input.GetAxisRaw("Horizontal");
}
else
{
horizontalInput = 0;
}
if (horizontalInput != 0)
{
direction = new Vector2(horizontalInput, 0);
}
}
void FixedUpdate()
{
Rigidbody.AddForce(Vector2.right * moveSpeed * horizontalInput * accelerationMod);
if (Mathf.Abs(Rigidbody.velocity.x) > moveSpeed)
{
Rigidbody.velocity = new Vector2(Mathf.Sign(Rigidbody.velocity.x) * moveSpeed, Rigidbody.velocity.y);
}
onGround = Physics2D.Raycast(transform.position, Vector2.down, 1.05f, ground);
if (onGround)
{
ignoreHorizontalInput = false;
diving = false;
}
if ((Input.GetKey(KeyCode.Space) || Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W)) && onGround)
{
Rigidbody.velocity = new Vector2(Rigidbody.velocity.x, 0);
Rigidbody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
}
if (Input.GetKeyDown(KeyCode.DownArrow) || Input.GetKeyDown(KeyCode.S))
{
if (onGround)
{
// Slide script to be added.
}
else
{
if (horizontalInput == 0 || diving)
{
Rigidbody.velocity = Vector2.zero;
Rigidbody.AddForce(Vector2.down * smashForce, ForceMode2D.Impulse);
ignoreHorizontalInput = true;
diving = false;
}
else
{
Rigidbody.velocity = Vector2.zero;
Rigidbody.AddForce(direction * diveForce, ForceMode2D.Impulse);
ignoreHorizontalInput = true;
diving = true;
}
}
}
}
The two main issues are the smash and dive. The smash works sometimes, but most of the time it doesn’t register the input. I suspect that it’s because of the way FixedUpdate() and Update() work. The dive has the same issue.
This isn’t a 2D or physics thing; input is updated per-frame and you’re only checking it per fixed-update which clearly doesn’t run per-frame. That’ll likely be why your input isn’t consistent. You could easily debug this yourself though which is where you should always start.
GetKeyDown is only valid on the frame that the key is pressed. Therefore, it can only be reliably read during Update(). When using physics to move the character, it is common to split up the reading of inputs and the change in velocity. Read the inputs in Update and apply the changes in FixedUpdate.
For example:
[SerializeField] private Rigidbody2D myBody;
[SerializeField] private float jumpVelocity;
[SerializeField] private float maxSpeed;
private Vector2 currentDirection = Vector2.zero;
private bool jumpRequested = false;
private bool isGrounded = true;
// Input-reading functions like GetKeyDown are only valid on the frame
// that the key is pressed, so these need to be checked in Update.
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
jumpRequested = true;
}
currentDirection.x = Input.GetAxis("Horizontal");
currentDirection.y = Input.GetAxis("Vertical");
}
//Changes to physics of the Rigidbody should be done in FixedUpdate
private void FixedUpdate()
{
isGrounded = CheckGrounded();
Vector2 targetVelocity = myBody.velocity;
if(jumpRequested)
{
if(isGrounded)
{
targetVelocity.y = jumpVelocity;
}
//You can optionally implement jump buffering here so that jump
//can be pressed just before landing, but this example is the simplest
//approach
jumpRequested = false;
}
targetVelocity.x = currentDirection.x * maxSpeed;
myBody.velocity = targetVelocity;
}
Thanks, but now that I can properly test the dive mechanic, it travels much too quickly and although it travels fast, it doesn’t travel far. And if I try to increase the force, it’s almost instantaneous:
Since you are setting the velocity directly anyway, could you just set the exact velocity you want in the direction of the diveForce rather than using AddForce? Also, do you have a non-zero Linear Drag on your object which may reduce velocity over time?