Reputation: 968
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
Reputation: 26
Try calling right before repaint() inside your repaint loop.
Toolkit.getDefaultToolkit().sync()
Upvotes: 0
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:
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