NiftyAppState Contribution

I wrote this little app state for one of my games and decided to share it I think it would make a great addition to the com.jme3.niftygui package.

public class NiftyAppState implements AppState {
	
	private AppStateManager stateManager;
	private ViewPort viewPort;
	
	private NiftyJmeDisplay display;
	private Nifty nifty;
	
	private Screen screen;
	
	private String startFilePath;
	private String startScreenId;
	
	private void enabled;
	
	public void NiftyAppState() { }
	
	public void NiftyAppState(String filePath) {
		this(filePath, null);
	}
	
	public void NiftyAppState(String filePath, String screenId) {
		startFilePath = filePath;
		startScreenId = screenId;
	}
	
	public Nifty getNifty() {
		return nifty;
	}
	
	public NiftyJmeDisplay getDisplay() {
		return display;
	}
	
	@Override
	public boolean isEnabled() {
		return enabled;
	}
	
	@Override
	public boolean setEnabled(boolean enabled) {
		if(this.enabled == enabled) {
			return;
		}
		
		if(enabled) {
			attachDisplay();
		} else {
			detachDisplay();
		}
		
		setControlEnabled(enabled);
		
		this.enabled = enabled;
	}
	
	@Override
	public boolean isInitialized() {
		if(display == null) {
			return false;
		}
		return display.isInitialized();
	}
	
	@Override
	public void initialize(AppStateManager stateManager, Application app) {
		this.stateManager = stateManager;
		this.viewPort = app.getGuiViewPort();
		
		AssetManager assetManager = app.getAssetManager();
		InputManager inputManager = app.getInputManager();
		AudioRenderer audioRenderer = app.getAudioRenderer();
		
		display = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, viewPort);
		nifty = display.getNifty();
		
		if(startFilePath != null) {
			nifty.addXML(startFilePath);
			if(startScreenId != null) {
				nifty.gotoScreen(startScreenId);
			}
		}
		
		if(enabled) {
			attachDisplay();
		}
	}
	
	@Override
	public void update(float tpf) {
		Screen screen = nifty.getCurrentScreen();
		if(this.screen != screen) {
			detachController();
			this.screen = screen;
			attachController();
		}
	}
	
	@Override
	public void render(RenderManager rm) { }
	
	
	@Override
	public void postRender() { }
	
	@Override
	public void stateAttached(AppStateManager stateManager) { }
	
	@Override
	public void stateDetached(AppStateManager stateManager) { }
	
	@Override
	public void cleanup() {
		detachController();
		detachDisplay();
	}
	
	private void attachDisplay() {
		if(viewPort == null || display == null) {
			return;
		}
		
		if(viewPort.getProcessors().contains(display)) {
			viewPort.addProcessor(display);
		}
	}
	
	private void detachDisplay() {
		if(viewPort == null || display == null) {
			return;
		}
		
		if(viewPort.getProcessors().contains(display)) {
			viewPort.removeProcessor(display);
		}
	}
	
	private void attachController() {
		if(screen == null || stateManager == null) {
			return;
		}
		
		ScreenController controller screen.getScreenController();
		
		if(controller != null) {
			if(controller instanceof AppState) {
				AppState appState = (AppState)controller;
				if(!stateManager.hasState(appState)) {
					stateManager.attach(appState);
				}
			}
		}
	}
	
	private void detachController() {
		if(screen == null || stateManager == null) {
			return;
		}
		
		ScreenController controller screen.getScreenController();
		
		if(controller != null) {
			if(controller instanceof AppState) {
				AppState appState = (AppState)controller;
				if(stateManager.hasState(appState)) {
					stateManager.detach(appState);
				}
			}
		}
	}
	
	public void setControllerEnabled(boolean enabled) {
		if(screen == null) {
			return;
		}
		
		ScreenController controller screen.getScreenController();
		
		if(controller != null) {
			if(controller instanceof AppState) {
				((AppState)controller).setEnabled(enabled);
			}
		}
	}
}

It is actually very simple to use attach the state in your application.

public class MyGame extends SimpleApplication {
	
	@Override
	public void sipleinitApp() {
		stateManager.attach(new NiftyAppState("Interface/nifty.xml", "start");
	}
	
}

Create a screen with a controller Nifty GUI Java Interaction

the state will automatically load your AppState ScreenController and start updating it if the NiftyAppState is disabled or destroyed it will do the same to your Controller. this means if your game is screen driven like mos where GUI elements allow the user to navigate between states you only need to call nifty.gotoScreen(“newScreenId”) from your controller and let the NifyAppState handle attaching the state for you.

so in short your main application could consist of only the code above and your UI could be done only in xml with AppState Controllers. It really cleaned up some of my code I hope this helps others.

You don’t want to do anything in stateAttached or stateDetached as these are called on whatever thread attaches the state. Just do it in initialize() and cleanup(), which are called on the render thread.

I will make the changes thank you

Is there anything else you see that could cause issues?

Note: even for your own use, if you extend BaseAppState it will take care of a lot of the lifecycle for you.

So how does this look to you?

public class NiftyAppState extends BaseAppState {
	
	private AppStateManager stateManger;
	private ViewPort viewPort;
	
	private NiftyJmeDisplay display;
	private Nifty nifty;
	
	private ScreenController controller
	private String startFilePath;
	private String startScreenId;
	
	public void NiftyAppState() { }
	
	public void NiftyAppState(String filePath) {
		this(filePath, null);
	}
	
	public void NiftyAppState(String filePath, String screenId) {
		startFilePath = filePath;
		startScreenId = screenId;
	}
	
	public Nifty getNifty() {
		return nifty;
	}
	
	public NiftyJmeDisplay getDisplay() {
		return display;
	}
	
	@Override
	protected void initialize(Application app) {
		stateManager = app.getStateManager();
		viewPort = app.getGuiViewPort();
		
		AssetManager assetManager = app.getAssetManager();
		InputManager inputManager = app.getInputManager();
		AudioRenderer audioRenderer = app.getAudioRenderer();
		
		display = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, viewPort);
		nifty = display.getNifty();
		
		if(startFilePath != null) {
			nifty.addXML(startFilePath);
			if(startScreenId != null) {
				nifty.gotoScreen(startScreenId);
			}
		}
	}
	
	@Override
	protected void cleanup(Application app) {
		detachController();
		detachDisplay();
	}
	
	@Override
	protected void onEnable() {
		attachDisplay();
		setControllerEnabled(true);
	}
	
	@Override
	protected void onDisable() {
		setControllerEnabled(false);
		detachDisplay();
	}
	
	@Override
	public void update(float tpf) {
		if(!isEnabled()) {
			return;
		}
		ScreenController currentController = nifty.getCurrentScreen().getScreenController();
		if(controller != currentController) {
			detachController();
			controller = currentController;
			attachController();
		}
	}
	
	private void attachDisplay() {
		if(!viewPort.getProcessors().contains(display)) {
			viewPort.addProcessor(display);
		}
	}
	
	private void detachDisplay() {
		if(viewPort.getProcessors().contains(display)) {
			viewPort.removeProcessor(display);
		}
	}
	
	private void attachController() {
		if(controller == null) {
			return;
		}
		if(controller instanceof AppState) {
			AppState appState = (AppState)controller;
			if(!stateManager.hasState(appState)) {
				stateManager.attach(appState);
			}
		}
	}
	
	private void detachController() {
		if(controller == null) {
			return;
		}
		
		if(controller instanceof AppState) {
			AppState appState = (AppState)controller;
			if(stateManager.hasState(appState)) {
				stateManager.detach(appState);
			}
		}
	}
	
	public void setControllerEnabled(boolean enabled) {
		if(controller == null) {
			return;
		}
		
		if(controller instanceof AppState) {
			((AppState)controller).setEnabled(enabled);
		}
	}	
}

This code looks strange.

I can’t really comment though as I haven’t used nifty in like 4 years or something.

Basically it is like this…

NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, guiViewPort);
Nifty nifty = niftyDisplay.getNifty();
nifty.fromXml("Interface/screen.xml", "start");
guiViewPort.addProcessor(niftyDisplay);

you can find these lines HERE

this is becouse NiftyJmeDisplay is a SceneProcessor… BTW I updated my code again there is no need to hold the screen if I only care about the ScreenController.

But I don’t know why you try to add the processor only if the viewport already contains the processor. I suspect a typo.

1 Like

your right i ran the code after i posted what i had written and got nothing but a black screen…LOL

Nice save I had the answer before I even had to look for the issue. :yum:

Issues are now resolved and working perfectly on my test case

@pspeed resolves your bugs before they show up. This is the man :smile:

And this even without a neat little test case or a full stack dump provided after an exception :stuck_out_tongue:
I always smile when I see “I followed the Tutorial x, but it doesn’t work.”
“Why doesn’t it work?”
“It comes up with a NullpointerExceptin!”

Simple typo during port from interface to abstract class. dev compile and execute from aide on android so please allow for some bugs and typos. Not to mention this is more like the creation of a tutorial not following instructions.

But for the record @pspeed is a boss…lol