Strange "Double Jumping" Behavior + Momentum Question

Started by
7 comments, last by KittyPlaysViolin 11 years, 5 months ago
Hello everyone!

"Double Jumping/Flying"
I took a quick break from my RogueLike to screw around with a sidescroller. Previously I had made a top down(Pokemon/Golden Sun) "movement style" game since I didn't really understand how to implement gravity. I decided to give it a shot, and it's working pretty fine. I even managed to implement some running behavior into the character and gave him some momentum. However for some reason(and it's most likely the fact that I should be sleeping right now instead of racking my brain with coding) after I jump I am able to execute a "double jump" I put double jump in quotes because the second jump does not end until you let go of the spacebar button. So it's more like a "jump and fly" kind of thing. Anyway, this behavior is undesirable and I am looking for input on how to fix it. My brain is fried beyond immediate repair so I would greatly appreciate it

Floors, what floors?
Also, one more thing that is bothering me. I understand why it's happening, but I can't think of a non-gimmicky way of fixing it. Whenever I ramp up the terminal velocity too far, or increase the rate of acceleration, my character will just fall right through the floor. I understand this is because of the way I am checking the collisions with the bounds. If I add too much to the velocity, I will overshoot the value which I am checking. I am not entirely sure how I would remedy this, so again feedback would be wonderful!

Running and Momentum
Also, I had some questions on the running behavior of my character as well.
1: Is this "true" momentum, or just some hacky/gimmicky representation of it?(I've taken calculus based physics but never tried programming physics before)
2: If you were playing this game for fun, would you like this behavior? If not, how could I modify it?
3: Do you see this behavior causing any future conflicts with code?

I will post my code at the end but if someone wishes me to upload the entire project(not very large, only four classes) let me know.
Thanks in advance!
-Adrian
[source lang="java"]import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;

public class Player
{
public int xPos;
public int yPos;
public int size;

public int dx;
public int maxDx = 8;
public int dy;

public int gravity = 1;
public int terminalV = 8;

public Rectangle player;
public Rectangle bounding;

public boolean left;
public boolean right;
public boolean up;
public boolean down;

public boolean jumping;
public boolean alreadyLanded;

public Player(int x, int y, int size)
{
xPos = x;
yPos = y;
this.size = size;

player = new Rectangle(x, y, size, size);
bounding = new Rectangle(x + (size / 4), y + (size / 4), size / 2, size / 2);
}

public void draw(Graphics g)
{
g.setColor(Color.WHITE);
g.fillRect(player.x, player.y, player.width, player.height);
}

public void update()
{
if (left && !right)
{
if (dx > -maxDx)
{
dx += -1;
System.out.println(dx);
}
}
if (right && !left)
{
if (dx < maxDx)
{
dx += 1;
System.out.println(dx);
}
}
}

public void move()
{
bounding.x += dx;
player.x = bounding.x - (size / 4);

bounding.y += dy;
player.y = bounding.y - (size / 4);

// Slow down the horizontal (FRICTION)
if (!right && !left)
{
if (dx < 0)
{
dx += 1;
}
if (dx > 0)
{
dx -= 1;
}
}

// Accelerate downward to a cap (GRAVITY)
if ((dy < terminalV))
{
dy += gravity;
}

if (jumping)
{
dy = -16;
alreadyLanded = false;
}

// BOUNDS
if (bounding.x <= 0)
{
bounding.x = 0;
}
if ((bounding.x) >= Frame.width - bounding.width + 1)
{
bounding.x = Frame.width - (bounding.width - 1);
}

if (bounding.y <= 0)
{
bounding.y = 0;
}
if ((bounding.y) >= Frame.height - bounding.height + 1)
{
bounding.y = Frame.height - (bounding.height + 1);
jumping = false;
alreadyLanded = true;
}
}

public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
switch (keyCode)
{
case KeyEvent.VK_W:
up = true;
break;
case KeyEvent.VK_S:
down = true;
break;
case KeyEvent.VK_A:
left = true;
break;
case KeyEvent.VK_D:
right = true;
break;
case KeyEvent.VK_SPACE:
if (alreadyLanded)
{
jumping = true;
}
break;
}

update();
}

public void keyReleased(KeyEvent e)
{
int keyCode = e.getKeyCode();
switch (keyCode)
{
case KeyEvent.VK_W:
up = false;
break;
case KeyEvent.VK_S:
down = false;
break;
case KeyEvent.VK_A:
left = false;
break;
case KeyEvent.VK_D:
right = false;
break;
case KeyEvent.VK_SPACE:
jumping = false;
break;
}

update();
}
}
[/source]

NOTE: I understand that I am not using xPos and yPos, but I am still torn on how to use xPos,yPos and the rect.x , rect.y variables. Also, I understand that my collision detection is currently working only on the bounding rectangle. This will be used as a hitbox later, I just wanted to test if it was working; normally the collision with tiles would be registered by the player rectangle, but again if you have feedback on this as well let me know.
Advertisement
In response to your "Floors, what floors?" section.
If, when you check for collisions, your object has moved more distance than its size (for instance it moved +100 on the x axis when it has a width of 20) you should divide the distance of travel into the size of the object then check collision that many times across the line of which your object has travelled.
I may not be putting it right so I'll just give an example.
Your player is 100 pixels in height and it is travelling at downwards velocity at 300 pixels. You should not just check it's position at current x and y but also the previous x and y's. So, 300/100 = 3 different collision checks. Then you'll need the previous y position by doing:
py(previous y) = y - yVelocity
increment = velocity/(velocity/height)
for(int i = 0; i < velocity/height; i++){
checkCollision(py + (i*increment));
}

So instead of just doing:
Velocity
|_|Check--------------------------|_|Check
you are doing:

increment increment increment
|_|Check|_|CheckPart|_|CheckPart|_|Check

Sorry if I made any spelling or grammar mistakes (I've hurt my middle finger).
Thanks for the response! Although I'm not entirely sure I understand. Would I be doing this check in the bounds check? Also, once I start putting in tiles I'm a bit confused as to how I should register the already landed boolean? And do you have any thoughts on the other sections?

Thanks again for the help!

"Double Jumping/Flying"


I'm not a programmer but, can you keep jumping and jumping if you keep pressing the "jump" button? Do you have a "state" for when the character is jumping?
I think you can have it like If character is in "X/Jumping" state, not be able to "jump/accept the jump button again". No?

That way you can have a "standing", "walking", "running", and so on "states" and limit or add to the control/abilities that the player can do. I think.
I'm not sure what you are asking in your momentum section but your double jump could be solved by two boolean switches. The first boolean sets to true if the player presses the jump button when on the floor. The second boolean sets to true if the first boolean is true and the player is in the air and if the second boolean is false to start with. The player only jumps if one of the booleans are false. So first jump sets boolean 1 to true then if the player hits the ground it sets back to false. Second boolean sets to true if the player jumps in the air again but if he tries to do it a third time then he can't because both booleans are true.

When you do your collision detection it's only checking at point A and B of it's trajectory meaning that it misses collisions when the velocity is greater than the size. So if a 10*10 pixel square is going 100 pixel velocity then it's missing out 10 potential collisions. To solve this you are finding the previous position then incrementing the position by the size of the object then checking collision at that point all in one game update. This could cause lag when travelling fast but it will stop bugs.
I could draw a diagram if it helps because I'm finding hard to explain.
Here is an example.
Your player is moving downwards at 300 pixels and update. The floor has a y position of 400 and a height of 20 so the floor covers 400 - 420. The player's y position is 300 and the players height is 50. When it checks collision they don't collide because they aren't touching at that point. The game updates and the player moves downwards to y position 600. The collision still brings back nothing because the floor and player aren't touching at that point even though the player has travelled through the floor. To fix this you have to check the collision at each point of the velocity.
So, in one game update, check collision at 350, 400, 450, 500, 550, 600. The player will collide at 400. To do the segmented collision detection all you have to do is divide the velocity by the height of the player (6) which gives you the amount of detections you need to do that update. Then you do:
for(int i = 0; i < amountOfDetections; i++){
checkCollision(previousYPosition + (i*height));
}
this will do the 350, 400, 450... thing and detect all possible collisions.
There may be a faster way to do this but this is what I came up with.
Sorry about the late reply, my internet has been acting pretty strange lately.

Cryo
I think you misunderstand. I don't want my player to double jump. Just one jump, but for whatever reason, he is able to jump twice. That's the bit I want to fix. I understand the collision with the floor now though, thank you for that! The momentum section was just that the player took a while to accelerate to his full speed while moving horizontally. I've decided to take that out though. I think I'll just add a sprint button.

David
The way I have it implemented, it's already checking to see whether the player is jumping or not. For whatever reason though, the player is still able to jump one last time. I believe it has to do with at which point I flip the booleans. Thanks for the help anyway though :]
Didn't have time to read the other replies so you may have your answer already. But...

When I was working on a platformer I found the double jump occurred because I was allowing jumps to happen when the player pressed the jump button and the vertical velocity was zero. Turns out that when you reach the peak of your jump and you're about to start descending, your velocity can be zero. So if you're still pressing the button you initiate another jump.

The collision issue took me some time to work out too. What I was doing was having it so that I was traveling at a speed in one direction, I'd check for an obstacle at current position + speed. If there's something there, then I collide with it. This doesn't catch situation where there's something something between current position + speed. So you need a way to check that and collide appropriately. And you need to do this for both horizontal and vertical collisions.
Hi SharkBait,

Here's a snippet of code I've used in this forums to describe how I would handle movement and jumping. NOTE, it doesn't include the momentum you have; rather, when the user stops pressing left or right, he stops moving that direction.

The main points are:
#1, you can jump if you're on the ground and hit the jump key
#2, Gravity is ALWAYS applied to the player; You check if the player has collided with anything on his feet to determine OnGround, and, if a collision happens, move the player back to location before collision to avoid overlapping.
#3, if you hit anything in the Y direction (your head or your feet), reset the location, and set Y velocity to 0.
Good Luck!
(#define values are just guessed examples, you'll have to change them to fit your game)

// Speed player moves left or right
#define MOVEMENT_SPEED 10.0f
// initial velocity given to player when he jumps
#define JUMP_VELOCITY 20.0f
void Player::HandleInput()
{
if (LeftIsPressed()) {
this.xVelocity = -MOVEMENT_SPEED;
}
else if (RightIsPressed()) {
this.xVelocity = MOVEMENT_SPEED;
else {
this.xVelocity = 0.0f;
}
// Only jump if we're not already jumping or falling
if (JumpIsPressed() && this.OnGround) {
this.yVelocity = -JUMP_VELOCITY;
}
}
// defines amount to increase downward velocity every frame
#define GRAVITY_FORCE 4.0f
void Player::Update()
{
// Apply downward force to player
this.yVelocity += GRAVITY_FORCE;
// Move the Player
this.xLocation += this.xVelocity;
this.yLocation += this.yVelocity;
// Check we've collide with something above or below us
bool CollideBelow;
if (CheckCollisionY(CollideBelow)) {
// move us back to previous location and Stop Y Velocity
this.yLocation -= this.yVelocity;
this.yVelocity = 0.0f;
if (CollideBelow) {
this.OnGround = true;
}
}
else {
this.OnGround = false;
}
// Check if we've collided with anything on our left or right
if (CheckCollisionX()) {
// move us back to previous location and Stop X Velocity
this.xLocation -= this.xVelocity;
this.xVelocity = 0.0f;
}
}

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

Thanks for the help guys! I ended up breaking out into fever and taking an 8 hour long nap and my brain seems to be working again and I seem to have to sweated out whatever sickness I caught.

BeerNutts:
Yes, that is indeed how I have my program implemented :] I always keep the gravity on the player and simply overcome it by adding a large upward velocity. I solved the double jump solution by removing the jumping boolean and working only with the alreadyLanded boolean. I'm guessing somewhere in there one boolean was true when it shouldn't have been. I shouldn't have been using a boolean to set another boolean in the first place so I kind of deserved that one...

The collision I have yet to implement, but I believe I understand it. Both you and Cryo suggested the same way so I'm going to go ahead and try that one out. What I understand is that right now I'm only checking the current position, so instead I should check the next position? That makes a lot of sense. However I'm not sure I understand the "top" collision. I haven't implemented any tiles yet so I haven't had the opportunity to bash my head into the ceiling yet.

The momentum I decided I'm going to remove so no worries about that. I'm simply going to add a sprint button.

kseh
Oh no worries about that :] I know that the velocity at the peak will always be zero, I defined the ability to jump only when the player has already landed.

Thanks for the help guys! It really helped out :]

One last thing though. I decided to draw the hitbox so I could see it moving around with the player and I noticed that although it does move with the player, it lags along a bit. I'm worried that this will present difficulties when I start checking for collisions against enemy attacks or projectiles....

Oh and I forgot to mention as well that there seems to be some issues with the keyboard input. If I'm moving along I can press and hold the spacebar and the character will bounce along. However, if I hold the spacebar, jump in place, and then start moving without letting go of the spacebar, the character doesn't continue bouncing,

This topic is closed to new replies.

Advertisement