Your code has more than one issue.
Issue: Do not call listener() inside the "while" loop. Because this adds a new key listener each time it is called, resulting in a new listener each time. This produces multiple updates of the map, more updates each 10 seconds because listener() inserts a new listener each time.
Solution: Move the call to listener() function out of the "while" loop.
long startLoopTime = Calendar.getInstance().getTimeInMillis();
long updateLoopTime = Calendar.getInstance().getTimeInMillis();
boolean firstLoop = true;
listener(board, player, map);
while (true) { // keep running
startLoopTime = Calendar.getInstance().getTimeInMillis();
long tickCheck = startLoopTime - updateLoopTime;
if(firstLoop || tickCheck > 10000)
{
firstLoop = false;
updateLoopTime = Calendar.getInstance().getTimeInMillis();
}
}
Issue: Even doing the fix proposed above, you're missing that event listeners callback functions are fired in the event dispatch thread that is not the main Thread. Your game has now at least two threads, I know this because with that code, if the main loop were in the same thread than the UI, then the application would hang, trapped in the main loop without never reaching the line where the key press event is handled.
As the event is being handled, I assume that your main loop is in the main thread (that is not the UI thread if you are using awt/swing, the presence of a JTextArea suggests you are). Because of this is that you get the event processed as soon as you press a key and not once each 10 seconds as you want.
You can confirm my theory by inserting calls to System.out.println("Thread ID = " + Thread.currentThread().getId())
. Insert one right before entering the "while" loop and other in the keyPressed()
function of the key listener. You will see then that the key listener callback functions are called in a different thread than your main loop.
Solution: A classic game programming approach is to move the key handling to the main loop. To do this and at the same time benefit from all UI API goodies as are the listeners system, I suggest you to set some key state variable inside the key listener callback function and then check that state variable in the main loop. Problem solved.
Example, modify to adapt to your needs:
Change listener code to this (I didn't tried to compile):
private boolean keysDown [] = new boolean [KeyEvent.KEY_LAST];
board.addKeyListener(new KeyListener(){
@Override
public void keyPressed(KeyEvent e){
keysDown[e.getKeyCode()] = true;
}
});
Note that I only including the keyPressed
callback. You must add keyTyped
and keyReleased
.
And then, process the key in the main loop and set that key as not pressed, if the player maintains it pressed, or release and press it again, it will get marked as pressed again by the key listener code.
long startLoopTime = Calendar.getInstance().getTimeInMillis();
long updateLoopTime = Calendar.getInstance().getTimeInMillis();
boolean firstLoop = true;
listener(board, player, map);
while (true) { // keep running
startLoopTime = Calendar.getInstance().getTimeInMillis();
long tickCheck = startLoopTime - updateLoopTime;
if(firstLoop || tickCheck > 10000)
{
firstLoop = false;
updateLoopTime = Calendar.getInstance().getTimeInMillis();
}
if (keysDown[KeyEvent.VK_W] == true)
{
// Move to here the code previously in the key listener.
keysDown[KeyEvent.VK_W] = false;
} else
if (keysDown[KeyEvent.VK_S] == true)
{
// Move to here the code previously in the key listener.
keysDown[KeyEvent.VK_S] = false;
}
// And so on...
}
You don't have to use a boolean array, it's only an example. You can use a List, a Map, or what works best for you. You don't have to use booleans, you can collect as many information as you need about the key events. For example you can save the whole KeyEvent object (I think that may be a bit extreme, but you can).