Nifty gui android life cycle issues

Hi I’m using Nifty Gui in my JME3 game and I’m running it on android 4.4.2 on galaxy note 2. Everything is working fine. I have gui with 3 buttons to control the character in my tile-based game.
But for whatever reason when I turn the screen off with the power button and turn it on again the gui stops working. The game is still working, coz my character is moving (animating), but GUI buttons are not responding. I can’t even see color change when I click them. Please help me if you know the reason of this strange behaviour.

Should I reset the gui in onResume() method which is part of the Android Activity? How should I reset the gui? Remove it and add again? Should I read the XML again?

Some code from my application:

In simpleInitApp() I call loadNifty method:

public void loadNifty() {
	NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, guiViewPort);
	Nifty nifty = niftyDisplay.getNifty();
	nifty.fromXml("Interface/nifty.xml", "start", new MyScreenController(p));
	
	guiViewPort.addProcessor(niftyDisplay);

}

I pass the character reference to MyScreenController to call actions on the character when the user clicks a button.

In my opinion somehow GUI gets frozen after onPause() method. I read the console output very carefully and I found nothing suspicious. Only a warning about failing link of class Lde/lessvoid/nifty/ClipboardAWT and unable to access class java.awt.datatransfer.Clipboard.
I have also a warning about missing element control, but Im using a screen control class in my project, so I think this is not a big deal.

Thanks in advance for your help.

Does your MyScreenController extend AbstractAppState or is it stored in a private field?

one thing that happened to me before was that my screen controller was not stored anywhere and on resuming, the reference was gone. I solved it by either storing it as a private variable or another class or make it an app state and attach it to the state manager. That worked for me.

Hi. I implemented MyScreenController as AbstractAppState and it is stored as a private field in MyGame class (main game class extending SimpleApplication). But somehow I can’t get it working :frowning: Even the easiest scenario where I:

  1. turn on the game
  2. switch off smarphone screen
  3. turn screen on again

is not working. The previous touched button is highlighted and I can’t press any other button.

Have you reattached the state to the state manager when android is resuming the application?

can you provide a sample code of the problem you have at the moment?

OK.

My main game class

public class MyGame extends SimpleApplication {

private AbstractAppState currentState = null;
private StartScreenState startState = null;
private GameRunningState gameState = null;

private NiftyJmeDisplay niftyDisplay;
private Nifty nifty;

public Nifty getNifty() {
	return nifty;
}

public static void main(String[] args) {
	MyGame app = new MyGame();
	app.start();
}

@Override
public void simpleInitApp() {
	flyCam.setEnabled(false);
	flyCam.setMoveSpeed(10f);

	inputManager.setSimulateMouse(false);

	setDisplayStatView(false);

	startState = new StartScreenState(this);
	gameState = new GameRunningState(this);

	niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, guiViewPort);
	nifty = niftyDisplay.getNifty();
	nifty.registerScreenController(startState, gameState);
	nifty.fromXmlWithoutStartScreen("Interface/nifty.xml");

	guiViewPort.addProcessor(niftyDisplay);

	stateManager.attach(startState);
	currentState = startState;
}

@Override
public void simpleUpdate(float tpf) {

}

@Override
public void simpleRender(RenderManager rm) {

}

public void loadGameState() {
	stateManager.detach(currentState);
	stateManager.attach(gameState);
	currentState = gameState;
}

public void loadMenuState() {
	stateManager.detach(currentState);
	stateManager.attach(startState);
	currentState = startState;
}

StartScreen state class

public class StartScreenState extends AbstractAppState implements ScreenController {

private ViewPort viewPort;
private Node rootNode;
private Node guiNode;
private AssetManager assetManager;
private ViewPort guiViewPort;
private InputManager inputManager;
private AudioRenderer audioRenderer;
private MyGame app;


private Node localRootNode = new Node("Start Screen RootNode");
private Node localGuiNode = new Node("Start Screen GuiNode");

public StartScreenState(SimpleApplication app) {
	this.rootNode = app.getRootNode();
	this.viewPort = app.getViewPort();
	this.guiNode = app.getGuiNode();
	this.assetManager = app.getAssetManager();
	this.guiViewPort = app.getGuiViewPort();
	this.inputManager = app.getInputManager();
	this.audioRenderer = app.getAudioRenderer();
	this.app = (MyGame) app;

}

@Override
public void initialize(AppStateManager stateManager, Application app) {
	super.initialize(stateManager, app);

	/**
	 * Init this scene
	 */

	viewPort.setBackgroundColor(ColorRGBA.Gray);

	/** Load a HUD */
	BitmapFont guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
	BitmapText displaytext = new BitmapText(guiFont);
	displaytext.setSize(guiFont.getCharSet().getRenderedSize());
	displaytext.move(1000, displaytext.getLineHeight() + 20, 0);
	displaytext.setText("Start screen. ");
	localGuiNode.attachChild(displaytext);
}

@Override
public void update(float tpf) {
	
}

@Override
public void stateAttached(AppStateManager stateManager) {
	System.out.println("Start screen state - state attached()");
	rootNode.attachChild(localRootNode);
	guiNode.attachChild(localGuiNode);
	((MyGame) app).getNifty().gotoScreen("start");
}

@Override
public void stateDetached(AppStateManager stateManager) {
	System.out.println("Start screen state - state detached()");
	rootNode.detachChild(localRootNode);
	guiNode.detachChild(localGuiNode);
}

//from ScreenController
public void bind(Nifty nifty, Screen screen) {
	System.out.println("bind(" + screen.getScreenId() + ")");
}

public void onStartScreen() {
	System.out.println("onStartScreen");
}

public void onEndScreen() {
	System.out.println("onEndScreen");
}

public void buttonStart() {
	System.out.println("Button start. Starting the game!");
	app.loadGameState();
}

public void buttonQuit() {
	System.out.println("Button quit.");
	app.stop();
}

}

My nifty.xml

<screen id="game" controller="com.jme3.core.GameRunningState">
	<layer id="foreground" childLayout="vertical">
		<panel id="panel_bottom" height="100%" width="70%" align="center"
			valign="bottom" childLayout="horizontal">

			<control name="button" label="Left" id="LeftButton" align="center"
				valign="bottom" height="20%" width="20%">
				<interact onClick="buttonLeft()" />
			</control>

			<control name="button" label="Forward" id="ForwardButton"
				align="center" valign="bottom" height="20%" width="20%">
				<interact onClick="buttonForward()" />
			</control>

			<control name="button" label="Right" id="RightButton" align="center"
				valign="bottom" height="20%" width="20%">
				<interact onClick="buttonRight()" />
			</control>

			<control name="button" label="Back" id="BackButton" align="center"
				valign="bottom" height="20%" width="20%">
				<interact onClick="buttonBack()" />
			</control>
			
			<control name="button" label="Photo" id="PhotoButton" align="center"
				valign="bottom" height="20%" width="20%">
				<interact onClick="buttonPhoto()" />
			</control>

	      </panel>
	</layer>
</screen>

<screen id="start" controller="com.jme3.core.StartScreenState">
	<layer id="foreground" childLayout="vertical">
		<panel id="panel_bottom" height="100%" width="70%" align="center"
			valign="bottom" childLayout="horizontal">

			<control name="button" label="Start the game!" id="startButton"
				align="center" valign="bottom" height="20%" width="25%">
				<interact onClick="buttonStart()" />
			</control>

			<control name="button" label="Quit" id="quitButton" align="center"
				valign="bottom" height="20%" width="25%">
				<interact onClick="buttonQuit()" />
			</control>

		</panel>
	</layer>
</screen>

My android MainActivity class

public class MainActivity extends com.jme3.app.AndroidHarness {

private static final int MY_REQUEST_CODE = 123;

@Override
public void onCreate(Bundle arg0) {
	Log.i("jme3", "onCreate");
	super.onCreate(arg0);
	this.addContentView(b, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}



@Override
protected void onResume() {
	Log.i("jme3", "onResume");
	super.onResume();
}  

@Override
protected void onPause() {
	Log.i("jme3", "onPause");
	super.onPause();
}

@Override
protected void onStart() {
	Log.i("jme3", "onStart");
	super.onStart();
}



public MainActivity() {

	Log.i("jme3", "Constructor");

	// Set the application class to run
	appClass = "com.jme3.core.MyGame";

	// Try ConfigType.FASTEST; or ConfigType.LEGACY if you have problems
	eglConfigType = ConfigType.BEST;

	// Exit Dialog title & message
	exitDialogTitle = "Quit game?";
	exitDialogMessage = "Do you really want to quit the game?";

	// Choose screen orientation
	screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;

	// Invert the MouseEvents X (default = true)
	mouseEventsInvertX = true;

	// Invert the MouseEvents Y (default = true)
	mouseEventsInvertY = true;

}

}

Even with the simple start screen state i have problems with not working nifty gui after onPause and onResume. I have also a GameRunninState class which is very similar to StartScreen state class and a Player class used by the game.
Thanks for help

I think the problem is when you are detaching when you are using loadGameState and loadMenuState, Detaching an app state will also clean it up and remove it. even if you have a private variable that points to it.

You can do either of these:

  • Keep both always attached and have one “Active” at a time, you have one nifty scene controlled by an unique controller so this will be easy. If the game is of small scale, this solution should not be a problem. and with that active flag you can control what runs and whatnot. Here’s an example:

      public void loadGameState() {
              startState.setAvtive(false);
      	gameState.setActive(true);
      	currentState = gameState;
      }
      public void loadMenuState() {
          gameState.setActive(true);
          startState.setAvtive(false);
          currentState = gameState;
      }
    
  • on loadGameState and loadMenuState, you should initialize the new state to attach always and make null the one that is detached. This means you need to implement the cleanup method on StartScreenState and GameRunningState to avoid any bugs or crashes.

      public void loadGameState() {
          stateManager.detach(currentState);
          currentState = new GameRunningState(this);
          stateManager.attach(currentState);
      }
    
      public void loadMenuState() {
          stateManager.detach(currentState);
          currentState = new StartScreenState(this);
          stateManager.attach(currentState);
      }

But when turning the screen off no states are detached. In the simplest scenario I am not calling any of these methods. Just sitting on the start screen state and turning off then on the screen causes nifty gui to freeze. Game is running fine, I have already tested it. There are just problems with nifty gui.

I have tried already many things in android’s onResume method, such as reloading the state, recreating and reloading the state, reloading nifty etc, but nothing worked. :frowning:

After onResume all my buttons do are warnings in console:

WARNING missing element/control with id [startButton#text] for requested control class [de.lessvoid.nifty.controls.TextField], but I’m getting them even when everything is working fine after the app is launched from start completely.

Ok, I will take a look at it later this afternoon to replicate your problem and I will get back at you after that

OK so after over a month I came to something. And quickly I’ve found similar topic on this forum:

The problem is that the setSimulateMouse boolean is resetting to true after android activity is resumed. I don’t know what is this happening, maybe InputManager is recreating or something. I don’t have much time to investigate that.

I am setting setSimulateMouse again to false after resuming activity and it’s working as expected, at least for the scenarios I’ve tested.