# How will I make my character jump only when it is pressed once

I have scoured through the internet to find a solution of this but this is the only solution that I could find but it cancels the yMove += jumpingVelocity * deltatime as soon as collision is not detected anymore thus it instantly just goes down. If I remove the isJumping the character continues to go up as long as the jump button is pressed.

xMove = 0;
yMove = 0;

if (handler.getKeyManager().left) xMove -= movementVelocity * deltaTime;
if (handler.getKeyManager().right) xMove += movementVelocity * deltaTime;
if (handler.getKeyManager().jump && onFloor && !isJumping) {
yMove += jumpingVelocity * deltaTime;
isJumping = true;
}
else{
isJumping = false;
}
// gravity
yMove += gravity * deltaTime;


This is the code that updates the player position

public void moveX(){

if (xMove > 0){

int tx = (int) (x + xMove + bounds.x + bounds.width) / Tile.TILEWIDTH;
if (!collisionWithTile(tx,(int) (y + bounds.y) / Tile.TILEHEIGHT) && !collisionWithTile(tx,(int) (y + bounds.y + bounds.height) / Tile.TILEHEIGHT) && tx < handler.getWorld().getWidth()){
x += xMove;
}
else{
x = tx * Tile.TILEWIDTH - bounds.x - bounds.width - 1;
}
}
else if (xMove < 0){
int tx = (int) (x + xMove + bounds.x) / Tile.TILEWIDTH;

if (!collisionWithTile(tx,(int) (y + bounds.y) / Tile.TILEHEIGHT) && !collisionWithTile(tx,(int) (y + bounds.y + bounds.height) / Tile.TILEHEIGHT)){
x += xMove;
}
else{
x = tx * Tile.TILEWIDTH + Tile.TILEWIDTH - bounds.x;
}
}
}

public void moveY(){
if (yMove < 0){
int ty = (int) (y + yMove + bounds.y) / Tile.TILEWIDTH;

if (!collisionWithTile((int) (x + bounds.x) / Tile.TILEWIDTH, ty) && !collisionWithTile((int) (x + bounds.x + bounds.width) / Tile.TILEWIDTH, ty)){
y += yMove;
}
else{
y = ty * Tile.TILEHEIGHT + Tile.TILEHEIGHT - bounds.y;
}
}
else if (yMove > 0){
int ty = (int) (y + yMove + bounds.y + bounds.height) / Tile.TILEWIDTH;

if (!collisionWithTile((int) (x + bounds.x) / Tile.TILEWIDTH, ty) && !collisionWithTile((int) (x + bounds.x + bounds.width) / Tile.TILEWIDTH, ty)){
y += yMove;
onFloor = false;
}
else {
y = ty * Tile.TILEHEIGHT - bounds.y - bounds.height - 1;
onFloor = true;
}

}

}

• Lots of errors. onFloor and isJumping should be integrated in one state machine, not two boolean variables. Gravity should affect velocity, not position. When the player releases the jump button, instead of assigning isJumping a false value, it should go to the falling state. Sep 25, 2022 at 4:23
• Comments are not for extended discussion; this conversation has been moved to chat. Sep 26, 2022 at 13:00

Your code is missing the concept of velocity and inertia. This is what keeps the character rising in the air after the initial thrust of their jump, and what allows them to come to a gradual stop at the peak of their jump and then start accelerating downward. To get this familiar behaviour, you need to track velocity as a persistent physical property of the object that carries over from frame to frame.

Here's a minimal adaptation of your current code to incorporate velocity. The other points BlackGlory raises in the comments above about implementing a movement state machine are also useful improvements, but to keep this answer manageable in scope I'll stick closely to your current version:

// Note that we do not zero yVelocity (at least not till we hit the ground!)
xVelocity = 0;

if (handler.getKeyManager().left) xVelocity -= movementVelocity;
if (handler.getKeyManager().right) xVelocity += movementVelocity;

if (handler.getKeyManager().jump && onFloor && !isJumping) {
yVelocity += jumpingVelocity;
// From the moment we jump,
// we're airborne, and can't jump again till we land.
onFloor = false;
}

yVelocity += gravity * deltaTime;


Note that only gravity is scaled by deltaTime, because it's an acceleration. Acceleration multiplied by a time interval gives you a change in velocity.

Your other parameters here are velocities already, so they don't need to be scaled by time until we want to integrate them into changes in position:

public void moveX() {
// Now we integrate our velocity by time to get a displacement
// (change in position)
float xMove = xVelocity * deltaTime;

// The rest can stay the same.
// ...

public void moveY() {
// Same here.
float yMove = yVelocity * deltaTime;

// The rest can stay the same, except your collision clauses,
// where we need to kill our vertical velocity.
// ...
// First: the rising case, where we hit our head on a ceiling:
else{
y = ty * Tile.TILEHEIGHT + Tile.TILEHEIGHT - bounds.y;
yVelocity = 0;
}

// ...

// Then: the falling case, where we stop upon hitting a floor:
else {
y = ty * Tile.TILEHEIGHT - bounds.y - bounds.height - 1;
onFloor = true;
yVelocity = 0;
}


So here xVelocity and yVelocity are member variables that persist from frame to frame, and xMove/yMove have been demoted to temporary variables we use just for integrating our position change and doing collision checks.

You might want to build on this by making your character have some acceleration/deceleration when moving left/right, or carrying some inertia while airborne so they can't stop on a dime in mid-air. To do that, make sure you add similar velocity-zeroing to your moveX() function when you hit an obstacle. You can use an acceleration scheme similar to this to let your character accelerate up to but not past a maximum speed, with controllable traction (so you can make them sharper to control on dry ground, and more floaty in the air or slippery on ice)