I would expect it to show the first update spatial, then sleep 1s, then the second spatial, etc…
Instead what I observe is that it sleeps 3s and then shows up all the spatials at the same time.
Can somebody explain to me why is happening?
There’s rendering code that needs to run in order for anything to be rendered to the display. If that code is run on the thread that does the rendering, then the sleep() call is preventing the rendering code from running.
But if the rendering code is on the same thread, then everything should be sequential right? Unless we can imagine spatial.rotate as an asynchronous call that will be rendered later by the same thread, at which point I have some questions:
Why is the rendering happening in the same thread?
Where exactly is the code rendering all the spatials invoked?
Is it normal practice to fork a new thread to move your spatials?
First I assume that you have the basic application and put your code into the update method.
Rendering happens in the same thread because update is run by the render thread.
Rendering happens between update calls.
No, usually you don’t use Thread.sleep to progress game logic. You check the elapsed time each update/frame and trigger your code when the desired time has elapsed. You can run your game logic in a separate thread, but spatials should be moved in the render thread.
Hi, welcome to your first game and probably your first UI-related project. And welcome to the very deep end of that giant ocean.
As others have stated, games work on the concept that each update you prepare a new frame of graphics.
update
render
update
render
update
render
If you want to do something over some period of time then you have to track that and do pieces of it on each update. You want to keep updates as small as possible to not break frame.
This is the way game engines work. This is also similar to the way that any user interface works (though those will be interaction based instead of frame based).
TLDR; You need to think of everything in terms of small slices of time call a frame.
Edit: you should also do the jMonkeyEngine tutorials on the wiki. Especially as a brand new game-dev beginner, these will be extremely vital.
True, but in this case it’s not so much that you “can” use the thread for other things at the same time as it is that you’ve just completely locked jME out of doing any of its many critical duties as long as the thread is asleep.
More than that actually, if you leave it blocked for longer the OS might think the app hung and will show it as not responding, possibly crashing it if you click on it.
This is why loading needs to be done on a separate thread and why the asset manager is thread safe.
Once I used to load it in a separate thread like below and it was working fine.
/**
* This is a multi thread model loader which loads model in loader
* thread and after loading it attaches model to scene in render
* thread.
*/
private class ModelLoader implements Supplier<Spatial> {
private String modelName;
private Node modelNode;
/**
* @param modelName This is model name used when loading model
* by assetManager
* @param modelNode This is the node where model will be attached
* to after loading
*/
public ModelLoader(String modelName, Node modelNode) {
this.modelName = modelName;
this.modelNode = modelNode;
}
@Override
public Spatial get() {
ModelKey modelKey = new ModelKey(modelName.concat(AssetUtils.getEnabledModelFormat().getType()));
Spatial model = getApplication().getAssetManager().loadModel(modelKey);
try {
int numberOfChildren = getApplication().enqueue(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return modelNode.attachChild(model);
}
}).get();
} catch (InterruptedException ex) {
log.error("Problem occurred while loading model" + modelKey.getName(), ex);
}
return modelNode;
}
}
and then using a CompletableFuture to load it and attach it to resultModelNode .
boolean loaded = false; // don't run main loop until this is true
Geometry model;
@Override
public void simpleInitApp() {
//attach loading screen to gui
Thread load = new Thread("Loading"){
@Override public void run(){
try{
//async load assets
model = (Geometry)app.getAssetManager().loadModel("model.obj");
TangentBinormalGenerator.generate(model);
app.enqueue(() -> {
//rendering thread again
//remove loading screen, setup viewports and attach processors to them, unblock input, launch main menu, etc.
rootNode.attachChild(model);
loaded = true;
});
}catch (Exception e) {
e.printStackTrace();
}
}
};
load.setPriority(Thread.MAX_PRIORITY);
load.setDaemon(true); //otherwise the thread will keep your game running if it crashes during loading
load.start();
}