Sh4d0wsPlyr
Sh4d0wsPlyr

Reputation: 968

Moving objects on a screen without delays/lag

I am looking to move objects across a canvas or other component(s), but to do so without noticeable latency/delays. Now, to get it out of the way I am using double buffering with multiple layers (most of which only get drawn once). In my case I have a background that gets drawn once when the map generates, a mask layer (things behind the object), and a fringe layer (things in front of the object). The only thing that's actually getting re-drawn is the actual object moving around the screen.

That being said, I have also setup KeyListeners on this, and moved my object movement into a drawing and processing into separate threads (e.g. a thread dedicated to drawing the canvas, and a thread dedicated to processing the movement).

What I notice is that when I press a key to move the object (W,A,S,D) it will move a small amount (a single move action), followed by a slight delay, then move constantly until I stop pressing the keys.

Is there a better way to implement movement from the keyboards to reduce/eliminate delay?

-Edit- I realized I could probably sum this up in a single statement: How to make controls for a game that feel responsive, without awkward delays/latency.

Upvotes: 0

Views: 906

Answers (2)

Kevin Jaureguy
Kevin Jaureguy

Reputation: 26

Try calling right before repaint() inside your repaint loop.

Toolkit.getDefaultToolkit().sync() 

Upvotes: 0

Alexander Guyer
Alexander Guyer

Reputation: 2193

What operating system are you using? On linux / possibly mac, holding a key down performs several cycles which involve calling both the press and release methods constantly (You know how if you hold a key down in a text box, one character appears, then there's a delay, then several more appear rapidly? In linux, the operating system executes press, then release, really really fast.) In windows, however, the release method is never called until the key is actually released. (basically, it just keeps calling the press method really fast.)

A very ugly and hacky answer, but I hope it helps:

It is helpful to know the timing of these cycles. Typically, it goes something like this:

  • User presses and holds a button (say, the 'a' key). The 'a' key's press function is immediately called.
  • There is about a quarter to half second delay (often adjustable in system settings).
  • 'a' key's release function is called
  • Few millisecond delay (between 2 and 10 ms by my testing)
  • Press function is called again
  • Slightly longer delay (Between 20 and 30 ms by my testing).
  • Repeat starting on third bullet point.

With this cycle in mind, it is theoretically possible (actually, completely possible. I've tested it.) to use timing to detect whether or not the user has actually released the key. Basically, if the key is released for more than 10 milliseconds (I'd go 15, just to be safe), then you know that the user has released the key.

This solution involves creating a boolean for each relevant key. Your KeyListeners, in turn, will do nothing more than set these booleans to true / false (maybe when they are pressed, they are set to true, and when they are released, they are set to false.)

Then, you create one more boolean for every key which will represent whether or not the keys are really being pressed. Create a thread which watches the first booleans (the ones controlled by the KeyListeners). Every time the first booleans are set to true, set the corresponding second boolean to true.

However, whenever the first booleans are set to false, wait 15 milliseconds, and check again. If they are still set to false, then set the corresponding second boolean to false. Why? If it's been 15 milliseconds and they are still false, then you know that the user has actually released the key.

KeyListener:

@Override
public void keyPressed(KeyEvent e){
    if(e.getKeyCode == 65){
        aPressed = true;
    }
}

@Override
public void keyReleased(KeyEvent e){
    if(e.getKeyCode == 65){
        aPressed = false;
    }
}

Separate Thread (calling it trueKeyListener)

boolean waiting = false;
long tWaitStart;
Runnable trueKeyListener = new Runnable(){
    @Override
    public void run(){
        while(true){//Instead of true, use some variable, like running
            if(aPressed){
                aTruePressed = true;
            }else if(!waiting){//not yet waiting for the 15 ms
                waiting = true;
                tWaitStart = System.nanoTime();
            }else if(System.nanoTime() - tWaitStart >= 15000000){//waiting is over
                waiting = false;
                if(!aPressed){//a still isn't pressed
                    aTruePressed = false;
                }
            }
        }
    }
};

As far as I know, timing the booleans is the only way to make it work on any operating system. As I said, this shouldn't be (or at least wasn't when I last coded something like this) an issue on a Windows OS.

Edit: Finally, as I forgot to mention, you can create one more thread which watches the truePressed booleans and handles movement accordingly.

Secondly, just some advice, if there are several relevant keys in your application, I recommend using arrays of pressed booleans, truePressed booleans, waiting booleans, and tWaitStart longs for handling multiple keys in one for loop.

Second Edit: I have found this question which seems very similar to yours. I hope it helps.

Upvotes: 1

Related Questions