I’m having trouble getting started with jme3. I’ve been going through the wiki:
… and some questions came up. I’d appreciate it if someone can help add some information to help get me out of my ignorance ditch
(1) The talk about SimpleApplication. In particular, the future of it.
I like what this is saying because one of my frustrations is the SimpleApplication “as is” seems to turn everything on (flycam…)
this(new StatsAppState(), new FlyCamAppState(), new AudioListenerState(), new DebugKeysAppState());}
super(new MyCustomState(), new AnotherState(), ....);
I don’t really see the examples I see online using this but I like the syntax as it lets me pick what I want. But I also don’t want to go down a path at a right angle to what the engine is designed to do. Is this still an acceptable way to do things?
(2) AppStates. I admit that I’m having trouble wrapping my head around these. I’ve seen some people say EVERYTHING in my app should be in an AppState (or many AppStates). But the start up examples (other than the (1) was to init) seem confusing to me as the don’t see many uses of custom AppStates. Just that I should use them.
But it’s usually in general hand waving ideas of using them. Does anyone know of a tut or maybe you can explain better for me how one would concretely use custom AppStates in a real example?
Conversely, is there ANYTHING in the app that shouldn’t be in an AppState?
“It’s been a while since we’ve seen dsobiera — their last post was 13 years ago.”
“[dsobiera] I’m having trouble getting started with jme3.”
If everyone had as much endurance in getting started as you have, the world would be a better place
Jokes aside, i know its not been jme3 13 years ago
As to your questions, examples dont neccessarily show the most efficient or clean way of doing things, they rather tend to show easy / compact versions of code that try to focus on the point the example is actually about. So its totally fine to override the AppStates added by default in the way you want to do.
I cannot tell the sources you looked at for example code, but i personally found @pspeed s examples helpful:
EDIT you might want to check the states added by default though and make sure you really dont want them, especially the ConstantVerifierState
About the AppStates: It’s along the lines of “composition over inheritance”, so you can easily and at any time toggle specific parts of the app on or off and helps to encapsulate your code in a meaningful way making it more reusable.
Imagine the player starts in the main menu, can select a map they want to play on, and upon selection you want to disable the mainmenu, show a loadingscreen instead, also load the actual scene in the background and once loaded, disable the loadingscreen again so the player can see the map. The AppStateManager and AppStates help you do exactly that.
About “is there anything in the app that shouldn’t be an appstate”, looking at the example linked above, it looks quite clean to do global initializations in you application and use appstates for actual behaviour
The code examples in the Wiki and “jme3-examples” have mostly been shoehorned into a single class for clarity, so they don’t provide good examples of how appstates are actually used.
It’s fine to pick and choose which of the standard appstates you want. I can point you to examples of this if you want.
Appstates provide a good basis for many game functions, such as processing user input and controlling cameras. There are also game functions that don’t benefit from being in appstates. Some functions work better as custom scene-graph controls. Some functions need to be scene processors. Data that isn’t updated regularly can derive from Object. And most JME apps can make good use of utility classes, factory classes, and other O-O paradigms.
Appstates provide a good basis for many game functions, such as processing user input and controlling cameras. There are also game functions that don’t benefit from being in appstates. Some functions work better as custom scene-graph controls. Some functions need to be scene processors. Data that isn’t updated regularly can derive from Object. And most JME apps can make good use of utility classes, factory classes, and other O-O paradigms.
@dsobiera just a small description to this in case you cannot get the full idea, appStates are some game tasks that are called synchronously (in a chain like manner) to do some async tasks inside the SimpleApplication update(); method that updates the game frames by the AppStateManager, so in brief its a design pattern to help people build large maintained games, you can choose not to use appStates & do things on the main update of the SImpleApplication, you will have same results, but you will disturb the pattern & your game will be no longer maintained & well designed & so do the custom controls, they are updated in a ThreadSafeList per spatial/node.
As for super(params);super().callFunction(); & this(params); & this.callFunction();, it depends on your situation, sometimes its mandatory to call the super class constructor or super class methods when instantiating a method/function/constructor for a subclass :
=> Example 1 : (Activity Class from Android onCreate()), notice the @CallSuper Annotation interface, mandates to call this method inside the descendant classes method version:
Other than this, you call the super methods/functions/constructors inside descendants for optimizations or pre-initialize some configurations as in jme :
=> Example 2 :
in super class LegacyApplication :
public LegacyApplication( AppState... initialStates ) {
initStateManager();
if (initialStates != null) {
for (AppState a : initialStates) {
if (a != null) {
stateManager.attach(a);
}
}
}
}
So a call to super(appStates:AppState[]) inside subclasses constructors would pre-initialize some appStates & schedule them to be firstly updated with game rendering.
Another usage for this is to inject actions inside loops :
=> Example 3 :
in super class, where updateBlankUiStates() is called within an update manager utility :
/**
* Override this & call its overloaded version : #{@link DataStateImpl#updateBlankUiStates(UiPager, PageDataModel[], ActionInjector)}.
* @param uiPager the uiPager that holds the data.
* @param models the dataModels.
*/
protected abstract void updateBlankUiStates(UiPager uiPager, T[] models);
/**
* Overloaded version to be called inside the invoked version inside the overriden solution.
*/
protected void updateBlankUiStates(@NonNull UiPager uiPager, T[] models, @Nullable ActionInjector<T> actionInjector){
if(isUpdateBlankUiStates()) {
//creating an async single tasked thread
Executors.newSingleThreadExecutor().execute(() -> {
//communicate back with looper & enqueue some actions to the main thread using a Handler
new Handler(Looper.getMainLooper(), null).post(() -> {
//switch data application on Ui off
setUpdateDataOnUi(false);
setUpdateDataModel(false);
uiPager.detachAllUiStates();
//prepare the Ui for data filling
for (int i = 0; i < models.length; i++) {
if (actionInjector != null) {
actionInjector.execute(models[i], i);
}
}
//disable the blank ui states update
setUpdateBlankUiStates(false);
//start again collecting data
setUpdateDataModel(true);
//switch data application on Ui on
setUpdateDataOnUi(true);
});
});
}
}
In subclass :
Overriding the abstract method & calling the action injector overloaded version inside it to be scheduled by the update of DataStateManager :