Sam Haito
Sam Haito

Reputation: 113

Game lag when releasing a key

I am making a java 2d side scroller and im having problems with multiple keys at the same time. Such as right + up. Whenever you release the up you stop moving right as you slowly go back to the ground even though right is still being pressed. Here are the keyboard listeners I have setup. dx is my horizontal movement speed and dy is the vertical height. Below is from the character class

public void keyPressed(KeyEvent e) {
    int key = e.getKeyCode();
    if (key == KeyEvent.VK_LEFT) {
        dx = -5;
        mainCharacterImageFacing = l.getImage();
        }

    if (key == KeyEvent.VK_RIGHT) {
        dx = 5;
        mainCharacterImageFacing = r.getImage();
    } 

    if (key == KeyEvent.VK_UP) {
        dy = 1;
        mainCharacterImageFacing = r.getImage();
    }

}

public void keyReleased(KeyEvent e) {
    int key = e.getKeyCode();
    if (key == KeyEvent.VK_LEFT);
    dx = 0;
    if (key == KeyEvent.VK_RIGHT)
        dx = 0;
    if (key == KeyEvent.VK_UP)
        {dy = 0;
        mainCharacterImageFacing = r.getImage();
        }

This is some code from the main game window that deals with calling the key press/release methods as well as dealing with the jump.

private class AL extends KeyAdapter{
    public void keyReleased(KeyEvent e) {
        p.keyReleased(e);
    }

    public void keyPressed(KeyEvent e) {
        p.keyPressed(e);
    }
}

@Override
public void run() 
{
    long beforeTime;
    long timeDiff;
    long sleep;

    beforeTime = System.currentTimeMillis();
    while(done == false)
    {
        cycle();
        timeDiff = System.currentTimeMillis() - beforeTime;
        sleep = 10 - timeDiff;
        if (sleep < 0 )
            sleep = 2;
        try {
            Thread.sleep(sleep);
        } catch (Exception e)
        {}
        beforeTime = System.currentTimeMillis();
    }
    done = false;
    h = false;
    k = false;
}

boolean h = false;
boolean done = false;   
public void cycle() {
    if (h == false)
        v = v - 2; //jump speed falling
    if (v == 350) //max y value of jump.  Lower numbers = higher jumps
        h = true;
    if (h == true && v <= 470) //starting y value
        {
        v = v + 2; //jump speed rising
        if (v == 470)
            done = true;
    }

}

Upvotes: 1

Views: 277

Answers (2)

Troubleshoot
Troubleshoot

Reputation: 1846

Do not handle your movement directly in the AL class. Instead, use booleans and set them to true/false accordingly:

private class AL extends KeyAdapter{
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        if (key == KeyEvent.VK_LEFT) {
            left = true;
        }

        if (key == KeyEvent.VK_RIGHT) {
            right = true
        } 

        if (key == KeyEvent.VK_UP) {
            //...
        }

    }

    public void keyReleased(KeyEvent e) {
        int key = e.getKeyCode();
        if (key == KeyEvent.VK_LEFT)
            left = false
        if (key == KeyEvent.VK_RIGHT)
            right = false
        if (key == KeyEvent.VK_UP)
            //...
        }
    }

The reason for this is because keyPressed and keyReleased are called when the keys are acted upon, though that means it will not be in sync with your main loop.

To move your sprite call a method in the loop which, if your conditions are true, moves your sprite accordingly, else does nothing.

EDIT: After reviewing your code some more I have a few suggestions.

First of all, look up sub-pixel rendering. Sub-pixel rendering is used to make the movement of sprites look smoother.

Another thing to look up is variable timesteps (sometimes referred to as delta timesteps). Delta time can be used to make sure that actions are performed in sync with a players fps. If one player has 60fps and another has 30fps, the player with 30fps will see actions performed at half the speed. With a delta timestep however, actions are always performed at the same speed. Here's a good article.

The third thing is your timing. Currently you're using System.getCurrentTimeMills. This is bad, and not precise. Instead, use System.nanoTime.

A fourth thing I've spotted is your jumping algorithm. A better way is to include acceleration. First you need to declare a constant for acceleration (0.1 or 0.2 are good) and tally a vertical speed. You can then fall when the vertical speed gets to a certain value. Here's a small snippet, though if you have gravity you may want to edit it. First of all set vSpeed to say -8 by default.

public void jump() {
    vSpeed += ACCELERATION;
    if(vSpeed < 8)
        dy += vSpeed;
    else
        vSpeed = -8;
        falling = false;
}

I just wrote this off the top of my head, but basically, since moving upwards is taking away a number, you appear to jump when it adds negative numbers, then you'll start falling when it reaches 0, and land when reaches 8 again.

Upvotes: 2

Cruncher
Cruncher

Reputation: 7812

The problem is the way the OS handles key events and passes them to your application. When you press a key, it fires an event for down. Then based on your settings for repeating, it will start pressing the key again after some amount of time, at some amount of frequency. (you can see this when typing. Press a key, and hold it).

When you press another key, it stops the repeated triggers for the other key. You can see this while typing as well. Hold a key, then start holding another. The original key stops, as you would expect.

This won't even be consistent with every os, and people could cheat in your game as it is, by making the repeat timer on their keyboard faster.

For these reasons, it's a very bad idea to do animations inside of key listeners. You should be settings flags to true/false and reading them in a game loop. (As demonstrated in @Troubleshoot's answer).

Upvotes: 0

Related Questions