Hi guys, a complete n00b in game dev here. I just tried making a simple game using swing. I made the game loop (the one that iterates over pressed buttons) in a separate thread. But I have a serious problem, I really lack skills in multithread programming. In the end of the loop I used Thread.currentThread().wait(20) so the picture would have time to get repainted… but something tells me that it is not a good way at all. Could you guys tell me how to make it the right way or how to improve my knowledge in multithread programming?
What are you doing when iterating over the buttons? Can we see some code?
Also, most Swing objects are not thread safe and all code modifying these objects should be sent to run on the event dispatch thread. If you are modifying them in any way on a thread other than the Swing thread itself, then you shouldn’t be.
The only thing I use Swing for is JFrame.
for (Integer currentKey:pressedKeys){
switch (currentKey){
case KeyEvent.VK_D: changePosX(3);break;
case KeyEvent.VK_A: changePosX(-3);break;
}
}
The changePosX method changes the X position of the object and repaints the JFrame. I just have no idea on how to “connect” two threads (the one that iterates and the one that repaints) so that iteration would begin again right after the repainting is complete.
Use a KeyListener instead.
Here’s how to write a KeyListener, and here’s the official documentation.
In the tutorial they seem to create a stand-alone class for it, but it may be simpler to do it using anonymous classes. So I have written a short example:
JPanel panel = new JPanel();
panel.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_0) {
// Do this code
}
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
});
I’ve been already using it, it’s not the issue. The question I asked is how to improve the performance of my code, i.e. how should I replace the .wait(n) method?
Well if you are altering anything Swing related in a thread that is not the Swing thread, use the provided utilities to send your code to run on the event dispatch/swing thread.
SwingUtilities.invokeLater
Send code to the Swing thread, and continue in the current thread.
or…
SwingUtilities.invokeAndWait
Send code to the Swing thread, and wait until the code has run on the Swing thread, and then resume the execution of the code in the current thread.
An example of this is the following:
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
// run code
}
});
// Code here wouldn't run until whatever "//run code" had executed on the Swing thread
You really shouldn’t be doing any multithreading at all. It will just cause problems for you, so don’t use it until you really need it.
I remember about a month ago I tried to multithread my game loop, and ended up just causing more headaches than it was worth. Don’t multithread until you have something that takes too much time and can be split into multiple threads.
Having a game loop that takes the same amount of time every iteration is probably more valuable than having the fastest possible throughput.
A common practice is the following: at the start of the game loop, store the current time in a long. Then, at the end of the game loop, read the time again and calculate the delta. Then, sleep as much as is needed to reach your chosen target loop time.
For 60fps, you will want a loop that consumes about 16 millis. That seems to be a good practical target, as there are quite a few screens that refresh at 60 fps, so going faster than that can be kind of pointless. You are currently consuming the elapsed time for the code + 20 millis, so you can probably expect to see some improvement if you implement this plan.
Re Swing and multi-threading: I try to avoid the EDT for the game loop, as it is already going to be busy with the repaint calls (rendering). There are numerous strategies for preventing multi-threading related crashes, including using synchronization. But it is also possible to do quite a lot, without problems, if you make sure you store changes to game state in a “model” layer, and not in the Swing control itself (which would be considered part of the “presentation” layer). For example, if you have a button that is part of your game, store the state of the button in a separate volatile variable, and if you need to consult the button state, do it by consulting that variable, not the button itself.
“Loose coupling” goes a long way to circumventing potential multi-threading problems.