Troubles with AppState not being initialized early enough

Hello,
even though I was able to use a little trick as a workaround for that problem, I wonder what the “right” way for this is. Maybe some experienced jME people will understand this text which is on my list of “known bugs” and “TODOs”:

We need some AppState instances ready during init phase of the app, AppState instances need call to initialize before they can be used, we fake that init by calling AppStateManager.update(0f) – but this method has a comment saying “Calls update for attached states, do not call directly.”

This “do not call directly” part is what worries me and now I think that I might be doing something wrong.

The problem is, when I wait one frame until I work with the AppStates, then the code structure will become less readable, more complicated, etc. I let one frame pass for the Bullet physics to let it position the objects correctly, but I’d like to keep that being an exception from the rule.

Any comments? :chimpanzee_smile:

I don’t understand. Attach the app state by passing it to the app’s constructor?

Edit: or what is actually going on. I’ve personally never hit this issue.

Hm,

I do use that feature with the c’tor of the App taking an array of AppState (for some AppStates that are “always on” - for others that need to be attached/detached, I do it in a different place - in simpleInit of the App).

Problem is, that AppState is not initialized right away, not before simpleInit of the App, but at a later point in time.

Maybe I can make a simple test case later that day.
It’s not an urgent topic though - since I do have a working workaround.

Also, when that “tutorial game” (more of a “framework” now) will be posted, people will tell me what I did wrong and could have done better (“more how it was meant to be done with jME3”) - so maybe let’s just wait a couple of time units until it’s out?

But why do you need it to run before simpleInit()? You shouldn’t really be doing anything in simpleInit() but attaching app states anyway.

Actually, the entire point of simpleInit is that it runs before everything else. If things ran before it then we’d need another beforeSimpleInitsInits method.

Just stop doing what you are doing in simpleInit that requires other things to be done first. Done.

Ah, okay, that sounds like a hint to a solution (a different design).
I currently do in simpleInit all kinds of things (initializing game variables, building the game world, etc.).
I also call simpleInit whenever a new game is started to do all those things again to init the next game.
I’m sure that there is room for improvements.
I hit that problem in my 2014 game too by the way, and added an event that notified my code when AppState intialize was called (and after that the AppState is ready to work with).
Since you never ran into these kinds of problems, I must be doing something fundamentally different. Might be because I’m not so very familiar with the internal code of jME.
EDIT: Or because your designs are better in general due to a long career as software designer.

Here is what almost all of my simpleInitApp() methods look like:

public void simpleInitApp() {
    GuiGlobals.initialize(this);
}

That’s it. If I didn’t use Lemur then it would be 100% empty.

Especially this:

…means you are doing lots of work to avoid an app state. When that’s what app states were designed for.

Okay, I will try to wrap all that into the planned ‘PlayingGameState’, ‘IntroState’, ‘MainMenuState’. So my “Big Refactoring #3” will be a little bigger then.

The game is awesome and making it was fun, but the design had to be refactored 2 times now. I’m learning new stuff every week.

Just a little info: That’s how the init and update stuff looks today:

(Don’t cry when you see this and don’t run around screaming “oh my god, oh my god”. It’s not final yet - I’ve been wrapping more and more things into AppStates and am currently refactoring again, creating even more AppStates. :chimpanzee_wink:)

public class PacificMonkey 
    extends 
        AtollGame //PacificMonkey is a variant of the 'AtollGame'
{
    //game settings (render settings, game controls, UI configuration)
    public PacificMonkeySettings theSettings;
    
    //game vars (general game variables - the parts of which the game consists)
    public PacificMonkeyVars theVars;
    
    //variables related to the sky sim are in 'skyVars'
    protected SkyVars skyVars = new SkyVars();

    
    //the creator (c'tor) of the PacificMonkey class
    public PacificMonkey() {
        //calls the c'tor of SimpleApplication with a selection of AppStates
        super( 
            //you could go this way..
//            //register these AppStates (every SimpleApplication does this)
//            new StatsAppState(), //shows frames per second and number of objects
//            new FlyCamAppState(), //a free-flight camera to discover the scene
//            new DebugKeysAppState(), //[C] key and [M] key to log debug info
//            //also registers our own AppState in addition to the default states
//            new CustomAppState(), //modifies the free-flight camera a little bit
//            new FirstPersonCamAppState() //a first-person camera            
                
            //or you could go this way..
            //we register these AppStates right now (others will follow later)
            new StatsAppState(), //shows frames per second and number of objects
            new FlyCam2AppState(), //a free-flight camera to discover the scene
            new FirstPersonCamAppState() //a first-person camera
        );
        
        theSettings = (PacificMonkeySettings) gameSettings;
        theVars = (PacificMonkeyVars) gameVars;
    }
    
    //the main (entry point - the app starts here)
    public static void main(String[] args) 
    {
        //we create an instance of PacificMonkey
        PacificMonkey app = new PacificMonkey();
        
        //this little helper needs access to the app
        Geometries.setApp(app);
        
        //we ask the user to select the render settings
        AppSettings settings = new AppSettings(true);
        
        //and then we select some more settings via code:
        
        //OpenGL 1 is only available in jME 3.0 (jME 3.1 uses advanced shaders)
        //settings.setRenderer(AppSettings.LWJGL_OPENGL1);
        
        //OpenGL 2 is similar to OpenGL 3 but is also supported on old hardware
        settings.setRenderer(AppSettings.LWJGL_OPENGL2);
  
        //OpenGL 3
        //crashes on my computer (but I have OpenGL 4.5) :-(
        //settings.setRenderer(AppSettings.LWJGL_OPENGL3); 
        
        //OpenAL audio renderer for sound and music
        settings.setAudioRenderer(AppSettings.LWJGL_OPENAL);
        
        //we will support joysticks and gamepads
        settings.setUseJoysticks(true);
        
        //we apply the render settings to the PacificMonkey instance
        app.setSettings(settings);
        
        //we start the PacificMonkey instance
        app.start();
    }
    
    //we need to override this in order to load joystick mappings
    @Override
    public void setSettings(AppSettings settings){
        JoystickMappingsHelper.makeSureJoystickMappingPropertiesIsLoaded();
        super.setSettings(settings);
    }
    
    @Override
    public void start() {
        super.start();
    }
    
    @Override
    public void stop() {
        leavingGame();
        super.stop();
    }
    
    //the init routine (called by SimpleApplication just after the app started)
    @Override
    public void simpleInitApp() {
        
        //during first init we try to auto-load a game
        if(enteringGameJustNow) {
            enteringGame(); //auto-load game settings and default savegame
            return;
        }
        
        //we add a SceneProcessor for our post processing effects
        addFilters();
        
        //we init our LightManager (it's a simple light management system)
        initLightManager();
        
        //we make our own AppStates now
        attachAppStates();
        
        //reset game speed (previous game run might have set it to zero)
        speed = 1f;
        
        //add the first stuff to the credits screen
        setupCredits();
        
        //make materials for outer surface of objects
        prepareMaterials();
        
        //start the physics engine
        preparePhysics();

        //define some important positions (e.g. "spawnPoint" and "finishPoint")
        definePositions();

        //create a world with physics and animations
        createWorld();

        //create a player
        createPlayer();
        
        //we create another light for the torch
        createLights();
        
        //we create another shadow for the torch
        createShadows();
        
        //setup keyboard mappings
        setupKeyMouseInput();
        
        //setup and configure cameras
        setupCameras(); 
        
        //create a simple UI
        setupWidgets();
        
        //initialize all pending AppStates now
        stateManager.update(0f);
        
        //increase our counter by 1
        ++numCallsToSimpleInitApp;
    }
    
    //a little helper variable, can be used for sine-waves that change over time
    float accumulatedTime = 0f;
    
    //the update routine (called before a new frame is rendered)
    @Override
    public void simpleUpdate(float tpf) {
        
        //update the 'accumulated time' (not really needed at the moment)
        accumulatedTime += tpf;
        
        //clear the picking result
        theVars.camPickingDone = false;
        theVars.cursorPickingDone = false;
        
        //update light management
        updateLightManager(tpf);
        
        //if player pressed a key, the position of the avatar might have changed
        updatePlayerPosition(tpf);
        
        //if player pressed a key, the rotation of the avatar might have changed
        updatePlayerOrientation(tpf);
        
        //the camera that hangs on the head of the avatar must get an update
        updateCamera(tpf);
        
        //the compass that tells us the direction must get an update
        updateCompass(tpf);
        
        //the little widgets and the credits screen
        updateWidgets(tpf);
        
        //update the water surface
        updateWater(tpf);
        
        //update the filters
        updateFilters(tpf);
        
        //special action (needed after loading a game)
        if(letOneFramePassAfterLoading)
        {
            letOneFramePassAfterLoading = false;
            //recover pause mechanism from temporary suppression
            reactivatePause();
            //restore status of key locks and maybe unpause game
            unpauseGame(GamePausedBy.LOADING);
        }
    }
    
    //another update routine (needed for the 'Pause' key)
    @Override
    public void update() {
        
        //PROBLEM: can not call super.update() for the 'Application' base class
        //=> TODO: must override the whole update() method of both base classes
        
        super.update();
        
        //even when paused, we want to see some things...
        if(pauseVars.isPaused) 
        {
            //we still want to have a frame time
            timer.update();

            //get the tpf value from timer
            float tpf = timer.getTimePerFrame();
            
            //there are some controls that need updates even when game paused
            for(Control updateWhenPauseControl : theVars.updateWhenPauseControls1)
                updateWhenPauseControl.update(tpf);
            for(Control updateWhenPauseControl : theVars.updateWhenPauseControls2)
                updateWhenPauseControl.update(0f);
            
            //hm.. I'm currently experimenting with this:
            //avoided calling 'update()' on 'stateManager' for a good reason
            //but now it becomes more and more obvious that it's needed
            //maybe if we call it with zero (0f) things will be okay...
            stateManager.update(0f);
            
            updateStatesWhenPauseOn(tpf);
            
            //we want input, even when we are in pause mode
            if(inputEnabled){
                inputManager.update(tpf);
            }
            
            //update nodes (only the 'geometric' state, not the game 'logic')
            rootNode.updateGeometricState();
            guiNode.updateGeometricState();

            //render states and nodes
            stateManager.render(renderManager);
            renderManager.render(tpf, context.isRenderable());
            simpleRender(renderManager);
            stateManager.postRender();  
        }
    }
    
    //some AppStates need updates even when game was frozen by pause mode
    @Override
    protected void updateStatesWhenPauseOn(float tpf) {
        super.updateStatesWhenPauseOn(tpf);        
        
        //credits scroll, even when in pause
        //guiVars.creditsWidget.update(tpf);
        ShowCreditsAppState creditsState1 = stateManager.getState(ShowCreditsAppState.class);
        if(creditsState1 != null && creditsState1.isInitialized() && creditsState1.isEnabled()) {
            creditsState1.update(tpf);
        }
        CreditsKeyAppState creditsState2 = stateManager.getState(CreditsKeyAppState.class);
        if(creditsState2 != null && creditsState2.isInitialized() && creditsState2.isEnabled()) {
            creditsState2.update(tpf);
        }
        
        //this state must have an update (to calculate FPS and show stats)
        StatsAppState statsState2 = stateManager.getState(StatsAppState.class);
        if(statsState2 != null && statsState2.isInitialized()) {
            statsState2.update(tpf);
            statsState2.getFpsText().updateLogicalState(tpf);
            statsState2.getStatsView().updateLogicalState(tpf);
        }

        //this state must have an update (to enable [P]hysics debugging)
//            BulletDebugAppState debugPhysicsState = stateManager.getState(BulletDebugAppState.class);
//            if(debugPhysicsState != null && debugPhysicsState.isInitialized())
//                debugPhysicsState.update(tpf);

        //this state must have an update (to follow the player):
        SkyAppState skyState = stateManager.getState(SkyAppState.class);
        if(skyState != null && skyState.isInitialized())
            skyState.update(0f);

        //this state must have an update (move camera around is allowed):
        AppState camState = theVars.camAppState;
        if(camState != null && camState.isInitialized())
            camState.update(tpf);
    }

TBH, I didn’t really read any farther than that. It’s a sign you need to refactor. I skimmed the rest and confirmed it… all of the manually-working-around-app-states stuff later.

Well, I made good progress during the last two refactorings. They mainly were about putting things into AppStates, creating a meaningful class hierarchy instead of extending SimpleApplication only, and creating several interfaces like “GameWithOcean”, “GameWithPhysics” etc. to have some kind of multiple inheritance.

It may be a bad design when compared to your standards, but as long as it’s fun and works, I will continue to refactor and improve that project. No need for me to have negative feelings just because other people are way better and much more experienced software designers. :chimpanzee_closedlaugh:

The game is not intended to be the “ultima ratio” or “winner of best practices award”. Its main goal is good readability (especially for beginners). Don’t worry, I will attach a note like that when publishing that stuff for free. :chimpanzee_smile:

These should be SimpleApplication with ocean app state, SimpleApplication with physics app state… no need for multiple inheritance or special app subclasses.

…that is 10000% the entire point of app states. The other 100 useful things is a side effect of that.

One should always prefer composition over inheritance and that’s what app states are: composition.

Many of the GameWith... interfaces encapsulate one AppState already. Some just provide a wrapping interface and what AppState is actually used is hidden under the hood.

I know that composition over inheritance thing. Not only from “Effective Java” (book) but also from practice. Exploding permutations are a pain. Still I miss C++ multiple inheritance, but know how to work around that via Java objects, interfaces, inner classes etc. Still I’m a better game designer and art designer than software designer, but that’s okay … I’m learning… :chimpanzee_smile:

Nonetheless I have a hierarchy (in addition to composition).

Here’s the layout of my hierarchy:

ApplicationSimpleApplicationSimpleGameStandardGameSmallGamePacificGameAtollGamePacificMonkey

The interfaces ("GameWith...") are too numerous to name them now.

The class hierarchy I could explain a little, but don’t think we need that.
Just two examples:

SimpleGame includes the things that I’m missing from SimpleApplication. It’s designed in such a way that users can just extend SimpleGame instead of SimpleApplication in order to get those things. “Those things” are a sophisticated visual debugger that shows normals, bounds, wireframe, skeletons, spatial names and the other thing is my “simple console”.

PacificMonkey adds the core game mechanic for this specific game. It effectively just orchestrates spawning enemies and friends and counts points after level complete and decides what game objects the player can pick up, how fast the player can move and what sub-AppState the ‘PlayingGameState’ is in.

Might not be the best overall software design, is not finished and polished yet (work in progress), but it’s a lot fun! :chimpanzee_smile:

But, nonetheless, thanks for your input and your oppinion. I guess, I will learn from your software and add the lessons learned into my framework later. Would be a fool if I ignored that expert knowledge. Thanks. :chimpanzee_smile:

Ok. I thought this was meant to be public examples of a good way to make JME games. But if you are just farting around with your own stuff then by all means do it however.

If you ever want advice on how to fix it then just let us know. Because you current approach is 180 degrees from the way we recommend users use JME… and ultimately in the long run it’s harder for them to do it your way.

First of all: You are polite as always.
Don’t get me wrong, I still like your input, and you do almost all the public support here, so I am thankful for every reply. Hopefully others will help you to moderate this forum soon. Some people already do, which is great.

Now, let’s think again:
You proposed that I put those things you saw from the “0.2.5 version” (between 2nd and 3rd refactoring) in a separate AppState - which I said I would, include that in the 3rd refactoring. I wrote that I did not pack everything into AppStates yet, but did so successfully many times during 2nd refactoring. I’ve understood what you wrote, and already gave that thing a name (PlayingGameState) and already had that on the agenda. Now I know that this state will need to be bigger and comprise everything from the simpleInitApp and simpleUpdateApp methods.

About the “extend monkeys” issue … (meaning the following)

I don’t understand why you state that “It’s a sign you need to refactor”. Since you don’t know anything about the hierarchy and what those classes actually do. You assume something and even when I tell you that “composition over inheritance” is understood, and that I’ve already wrapped subsystems into AppStates and am now continuing and am separating the mechanics into several smaller parts and add more and more AppStates. Still you say:

I don’t get the point where this communication here went so far apart that you still assume that this work-in-progress code snippet and the indication that I’m using a class hierarchy (in addition to composition) is all bad and worst practices.

There was another thing that surprised me when reading that, because earlier statements from you on that forum stated that “jME isn’t forcing anything on its users.”

For example I’m using the best practices that visuals are highly decoupled from data, up to the point that the user can select either shaded or unshaded and textured or untextured while game is running together with save games that simply write out positions and types of objects.
I’m not using an ES, which you said, you did never do again after having met the ES paradigm. Seems like it’s now forbidden to use OO.
You assume that a hierarchy of classes (btw. Application and SimpleApplication is a hierarchy too) is something that caused you to skip reading further code (which is … an interim code … please note that it was not meant to demonstrate superior coding but rather code which I’m replacing soon).

Yes, once the game is out, that’s exactly what one of my first questions will be. I guess you (and others) will prefer the packages-version over the one-file-version (which is currently at 1.2 Megabyte and is still working very okay on my machine, but maybe not on other peoples machines + others will prefer packages and multiple files).

You seemed to keep defending having half a dozen application classes. They are completely unnecessary and against JME recommended practice. In fact, we are moving to a point where you wouldn’t have to subclass application at all unless you really wanted to.

So if I misread your defenses of that approach then I apologize. But that’s the approach that is 180 degrees away from Recommended practice. Users can do whatever they want. We don’t force them. But if someone is trying to make tutorials then we’d prefer that they use the recommended way.

When my advice was getting pushback, I decided to clarify why we are even discussing it.

You say that most of your application-level interfaces are just wrappers around an app state anyway… then I see no reason to have the interface. Just let the app states grab the app states they need. If you are extending BaseAppState (also recommended practice) then this is as easy as:
getState(MyOtherState.class)

If you are already planning on making these changes then continue. But then there is no need to argue in favor of them anymore, yes?