So, I\'m working on a game engine, and I\'ve made pretty good progress. However, my engine is single-threaded, and the advantages of splitting updating and rendering into separa
I would suggest going pipeline with this architecture, meaning that the render stage will render all the elements updated on the previous frame, it would go like this:
Update 0
Update 1 Render 0
Update 2 Render 1
Update 3 Render 2
....
it would mean that your game will use more memory and all the objects will have to have per frame states / data
if you introduce more layers in this pipeline your game will suffer from input lag (meaning the user will see his action on the screen later then normal), so I suggest to using just this 2 stage pipeline
I've made my program using three threads (though more could be used).
Update logic (Does the data collecting and pre-processing)
Helper thread (Calculates time consuming cache pre calculations etc. at infinite sleep 1ms loop ... So this thread does not care where Update logic is going, or how fast. It just checks where it is going and calculates iF it needs to cache new items)
Render thread (Does ONLY rendering, everything it needs to render is pre-processed so it does only draw functions and calculates screen positions)
Doing this is super easy if you just have "thread safe" items you are drawing. But in game, I personally think that it is not bad thing if you render player 1 one tick ahead than player 2... Because you still want to draw as fast you can on games. Game logic thread makes sure there is no logical exceptions... So usually I think it does not matter what you draw and when, you just do it as fast as you can without thinking any "synchronizations".
I presonally prefer public static volatile item
to share data between threads. And AtomicIntegerArray
is a useful class for that too.
Also note that your draw thread should never run faster then your update thread. Since if your update thread is not done with the current step yet, you will draw the same thing as before. While doing this you might miss the finishing of the update step, which in the end causes lower than optimal framerate.
(Remember drawing the exact same picture as before doesn't benefit anyone).
I would say add a field that specifies the thread needed to run and render, and number the threads, if the thread number == the thread required, then it is allowed to run and render, and increment the required thread field, until it hits max, then loop back to 0. Alternatively, you could, use one thread for tick and another for render, this might be easier. Here is an example code:
public Game() {
this.tickThread=new Thread(this::tickLoop());
this.renderThread=new Thread(this::renderLoop());
}
public void tickLoop() {
//code for timing...
while(running) {
//more code for timing...
tick();
}
}
public void renderLoop() {
//code for timing or syncing frames...
while(running) {
//more code for timing...
render();
}
}
Alternatively, you could say:
|MyRunnable.java|
public interface MyRunnable
{
public abstract void run(boolean toRender);
}
|MyThread.java|
public class MyThread extends Thread
{
private boolean isRender;
private MyRunnable runnable
public MyThread(boolean isRender,MyRunnable runnable)
{
this.isRender=isRender;
this.runnable=runnable;
}
public void run()
{
this.runnable.run(this.isRender);
}
}
|Game.java|
public class Game extends /*JPanel/Canvas/JFrame/Some other component*/
{
private MyThread tickThread;
private MyThread renderThread;
private boolean running;
public Game()
{
super();
tickThread=new MyThread(this::run);
renderThread=new MyThread(this::run);
//other constructor code
}
public void tick()
{
//tick code here
}
public void render()
{
//render code here
}
public void run(boolean isRender)
{
//timing variables
while(running)
{
//timing code
if(isRender)
{
this.render();
}
else
{
this.tick();
}
}
}
}
Place your update logic in some kind of Updater
worker class (implementing Runnable
), and put renderer into separate worker class. When you need to update data, let Updater put that update into queue shared by both Updater and Producer. Most convenient would be to use queue which already have built-in multi-threaded support, like subclass of BlockingQueue
. For example code, see javadoc for BlockingQueue
.
Using queue is natural if you need to render all changes, even obsolete ones. If you wish to render only the latest change, use ConcurrentHashMap
instead of queue.
Don't forget to make your updates immutable objects, so there's no chance update can change while you render it.
As Nirmal pointed out, you could use some kind of thread pool to limit number of threads and to simplify starting/stopping of threads. Refer to Executor
interface and Executors
utility class in JDK to see available options here.
A decent implementation is one where you'll need little to no synchronization between the update thread and render thread. The difficulty lies in the situation where one thread runs at a different speed (which is very likely). Check out
http://blog.slapware.eu/game-engine/programming/multithreaded-renderloop-part1/ http://blog.slapware.eu/game-engine/programming/multithreaded-renderloop-part2/ http://blog.slapware.eu/game-engine/programming/multithreaded-renderloop-part3/ http://blog.slapware.eu/game-engine/programming/multithreaded-renderloop-part4/
how this can be achieved. The site gives an explanation and implementation (source + binaries).