Custom AppState strange behaviour?

Hey everyone, I recently discovered JME and am trying to create a simple ‘test’ project for myself, to familiarize myself with both JME and Java. (Note: I do have some Java and other programming experience)



However, I feel that the custom AppState I’ve written doesn’t behave like I thought it would. I create a simple box in my appstate’s initialize, and on enabling it is added to the app’s rootNode. When I try rotating the box in the app’s simpleUpdate, it rotates just fine. When I try to rotate it in the AppState’s update, nothing happens - and it is in fact executed, so that’s not the issue. Furthermore, if I use the same code I used in simpleUpdate, it works too. Trying to rotate the box via a direct reference (as opposed to rootNode.getChild(0).rotate) simply doesn’t seem to work.



So, is there something I’ve missed? Did I misunderstand how to rotate spatials, or did I misunderstand something about AppStates? Thanks for your help!



Code example:



App:

[java]

import com.jme3.app.SimpleApplication;



public class MyApp extends SimpleApplication {



public static void main(String[] args) {

MyApp app = new MyApp();

app.start();

}



@Override

public void simpleInitApp() {

TestState t = new TestState();

t.initialize(stateManager, this);

stateManager.attach(t);

t.setEnabled(true);

}



@Override

public void simpleUpdate(float tpf) {

//this works

rootNode.getChild(0).rotate(tpf, 0, 0);

}

}

[/java]



TestState:



[java]

import com.jme3.app.Application;

import com.jme3.app.state.AbstractAppState;

import com.jme3.app.state.AppStateManager;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.scene.Geometry;

import com.jme3.scene.Mesh.Mode;

import com.jme3.scene.Node;

import com.jme3.scene.shape.Box;



public class TestState extends AbstractAppState {



private MyApp app;

private Node rootNode;

private Geometry boxGeom;



@Override

public void initialize(AppStateManager stateManager, Application app) {

super.initialize(stateManager, app);

this.app = (MyApp) app;

this.rootNode = this.app.getRootNode();



Box b = new Box(Vector3f.ZERO, 1, 1, 1);

b.setMode(Mode.Lines);

boxGeom = new Geometry(“Box”, b);

Material mat = new Material(this.app.getAssetManager(), “Common/MatDefs/Misc/Unshaded.j3md”);

mat.setColor(“Color”, ColorRGBA.Blue);

boxGeom.setMaterial(mat);

}



@Override

public void setEnabled(boolean enabled) {

super.setEnabled(enabled);

if (enabled) {

rootNode.attachChild(boxGeom);

} else {

rootNode.detachChild(boxGeom);

}

}



@Override

public void update(float tpf) {

super.update(tpf);

// this works too

rootNode.getChild(0).rotate(tpf, 0, 0);



// this doesn’t work

boxGeom.rotate(tpf, 0, 0);

}

}

[/java]

The good news is that your understanding of app states etc seems mostly correct, your architecture is fine.



Your mistake is that you are calling initialize() in the app state yourself. The app state manager does that for you.



This means that initialize gets called twice, so the geometry gets created twice.



The one that gets added to the scene is then not always the one stored in the app state.

Thanks for your reply; that was indeed the case. Removing that line solved the issue, although I did have to add a line calling stateManager.update before calling enable.

I think that was the main reason I added the initialize() itself, because I called enable() and the state threw a couple of errors because it hadn’t been initialized yet. Problem solved though, thanks to you!

That’s because you are enabling it before it has been attached. If you read the AppState tutorial and javadoc it goes through what happens when in detail.



Your best bet will be to call setEnabled(true) at the end of initialize() if you want it to start enabled and that enabled code to be run.

You can run setEnabled() whenever you want but you should not put logic directly in setEnabled() like that since you don’t pay attention to whether the app state has been initialized or not.



If you want to do logic on enable/disable properly then the pattern is slightly more complicated. I want to check in a new base class for users to use but I haven’t come up with a name yet.



Here is an example of an AppState base class that properly takes care of the delegation to enable/disable.



[java]

public abstract class BaseAppState implements AppState {

private Application app;

private boolean initialized;

private boolean enabled;



protected abstract void initialize( Application app );

protected abstract void cleanup( Application app );

protected abstract void enable();

protected abstract void disable();



public final void initialize( AppStateManager stateManager, Application app ) {

this.app = app;

initialized = true;

initialize(app);

if( isEnabled() )

enable();

}



public final boolean isInitialized() {

return initialized;

}



public final Application getApplication() {

return app;

}



public final void setEnabled(boolean enabled) {

this.enabled = enabled;

if( !isInitialized() )

return;

if( enabled )

enable();

else

disable();

}



public final boolean isEnabled() {

return enabled;

}



public void stateAttached(AppStateManager stateManager) {

}



public void stateDetached(AppStateManager stateManager) {

}



public void update(float tpf) {

}



public void render(RenderManager rm) {

}



public void postRender() {

}



public final void cleanup() {

if( isEnabled() )

disable();

cleanup(app);

initialized = false;

}

}

[/java]



This is the one I use in some of my own code. It could probably be modified to have default implementations initialize/cleanup/enabled/disabled but I wanted to make sure I implemented them in my subclasses.

After reading some more in the source code (especially of stateManager) I have to agree. The code you pasted looks really clean and well-rounded, thanks a lot for that! This should be easy to use, and while for now I simply wanted to create a default appstate and have it start enabled, later on I’m pretty sure I’ll need more appstates.



This does however raise another issue, as I almost exactly mirrored the code that was given as example in the AppState tutorial… Perhaps that page should be modified then, maybe changing the code example’s setEnabled?

But won’t that still have enable/disable potentially called off the lwjgl thread?

@pixelsmash said:
This does however raise another issue, as I almost exactly mirrored the code that was given as example in the AppState tutorial... Perhaps that page should be modified then, maybe changing the code example's setEnabled?


Yes, the app state tutorial is wrong.
@zarch said:
But won't that still have enable/disable potentially called off the lwjgl thread?


Was that question for me?

It assumes that setEnabled() is called from the rendering thread or before the app state is attached. setEnabled() is not thread safe anyway and so should only be called from the render thread (or prior to attachment) anyway.

Once you attach an app state, typically the only thing you can do with it from another thread is detach it unless you've taken specific care to make your app state thread-safe. And generally I think it's not worth it just to avoid an enqueue(Callable).

It was more a rhetorical question to make the threading issues clear since they hadn’t really been mentioned yet :slight_smile:



This pattern handles the initialize/enabled thing but it doesn’t handle other potential threading issues.