Help With OptionsMenu NiftyGUI

Hello,

I’ve been all over the place figuring things out about Nifty. Sorry about my recent posts over petty errors people. It’s just me trying to get used to jmonkey. But they all have been very helpful and informative.

This post doesn’t have errors but I have a serious question. I’ve used the HelloNifty Tutorial code from jmonkey over the loading_screen.xml file and LoadingScreenTest class. I have the code here. The only question I have is related to the jmonkey code not the GUI. I have an options menu xml and the tutorials loading_screen.xml. Now…

How do I set opening the options menu after the game loads into the terrain scene. And other code I need for finishing the project.
REMEMBER! I’m trying to open this options menu after the game loads…Not after I press Load Game

package mygame;

import com.jme3.niftygui.NiftyJmeDisplay;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.elements.Element;
import de.lessvoid.nifty.input.NiftyInputEvent;
import de.lessvoid.nifty.screen.Screen;
import de.lessvoid.nifty.screen.ScreenController;
import de.lessvoid.nifty.tools.SizeValue;
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import de.lessvoid.nifty.controls.Parameters;
import com.jme3.renderer.Camera;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import de.lessvoid.nifty.controls.Controller;
import de.lessvoid.nifty.elements.render.TextRenderer;
import de.lessvoid.xml.xpp3.Attributes;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import jme3tools.converters.ImageToAwt;

public class Test extends SimpleApplication implements ScreenController, Controller {

boolean optionsMenuVisible = false;
private NiftyJmeDisplay niftyDisplay;
private Nifty nifty;
private Screen screen;
private Element progressBarElement;
private TerrainQuad terrain;
private Material mat_terrain;
private boolean load = false;
private ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(2);
private Future loadFuture = null;
private TextRenderer textRenderer;

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


public void simpleInitApp() {
    flyCam.setEnabled(false);
    niftyDisplay = new NiftyJmeDisplay(assetManager,
            inputManager,
            audioRenderer,
            guiViewPort);
    nifty = niftyDisplay.getNifty();

    nifty.fromXml("Interface/loading_screen.xml", "start", this);

    guiViewPort.addProcessor(niftyDisplay);
}

@Override
public void simpleUpdate(float tpf) {
    if (load) {
        if (loadFuture == null) {
            //if we have not started loading yet, submit the Callable to the executor
            loadFuture = exec.submit(loadingCallable);
        }
        //check if the execution on the other thread is done
        if (loadFuture.isDone()) {
            //these calls have to be done on the update loop thread,
            //especially attaching the terrain to the rootNode
            //after it is attached, it's managed by the update loop thread
            // and may not be modified from any other thread anymore!
            nifty.gotoScreen("end");
            nifty.exit();
            guiViewPort.removeProcessor(niftyDisplay);
            flyCam.setEnabled(true);
            flyCam.setMoveSpeed(50);
            rootNode.attachChild(terrain);
            load = false;
        }
    }
}
//this is the callable that contains the code that is run on the other thread.
//since the assetmananger is threadsafe, it can be used to load data from any thread
//we do *not* attach the objects to the rootNode here!
Callable<Void> loadingCallable = new Callable<Void>() {

    public Void call() {

        Element element = nifty.getScreen("loadlevel").findElementById("loadingtext");
        textRenderer = element.getRenderer(TextRenderer.class);

        mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
        mat_terrain.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
        //setProgress is thread safe (see below)
        setProgress(0.2f, "Loading grass");

        Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
        grass.setWrap(WrapMode.Repeat);
        mat_terrain.setTexture("Tex1", grass);
        mat_terrain.setFloat("Tex1Scale", 64f);
        setProgress(0.4f, "Loading dirt");

        Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");

        dirt.setWrap(WrapMode.Repeat);
        mat_terrain.setTexture("Tex2", dirt);
        mat_terrain.setFloat("Tex2Scale", 32f);
        setProgress(0.5f, "Loading rocks");

        Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");

        rock.setWrap(WrapMode.Repeat);

        mat_terrain.setTexture("Tex3", rock);
        mat_terrain.setFloat("Tex3Scale", 128f);
        setProgress(0.6f, "Creating terrain");

        AbstractHeightMap heightmap = null;
        Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
        heightmap = new ImageBasedHeightMap(heightMapImage.getImage());

        heightmap.load();
        terrain = new TerrainQuad("my terrain", 65, 513, heightmap.getHeightMap());
        setProgress(0.8f, "Positioning terrain");

        terrain.setMaterial(mat_terrain);

        terrain.setLocalTranslation(0, -100, 0);
        terrain.setLocalScale(2f, 1f, 2f);
        setProgress(0.9f, "Loading cameras");

        List<Camera> cameras = new ArrayList<Camera>();
        cameras.add(getCamera());
        TerrainLodControl control = new TerrainLodControl(terrain, cameras);
        terrain.addControl(control);
        setProgress(1f, "Loading complete");

        return null;
    }
};

public void setProgress(final float progress, final String loadingText) {
    //since this method is called from another thread, we enqueue the changes to the progressbar to the update loop thread
    enqueue(new Callable() {

        public Object call() throws Exception {
            final int MIN_WIDTH = 32;
            int pixelWidth = (int) (MIN_WIDTH + (progressBarElement.getParent().getWidth() - MIN_WIDTH) * progress);
            progressBarElement.setConstraintWidth(new SizeValue(pixelWidth + "px"));
            progressBarElement.getParent().layoutElements();
                
            textRenderer.setText(loadingText);
            return null;
        }
    });

}

public void showLoadingMenu() {
    nifty.gotoScreen("loadlevel");
    load = true;
}
public void toggleOptionsMenu(){
    nifty.gotoScreen("options");
    optionsMenuVisible = true;
    
}

@Override
public void onStartScreen() {
}

@Override
public void onEndScreen() {
}

@Override
public void bind(Nifty nifty, Screen screen) {
    progressBarElement = nifty.getScreen("loadlevel").findElementById("progressbar");
    nifty.getCurrentScreen().findElementByName("options");
    
    
}

// methods for Controller
@Override
public boolean inputEvent(final NiftyInputEvent inputEvent) {
    return false;
}

@Override
public void bind(Nifty nifty, Screen screen, Element elmnt, Parameters prmtrs) {
    progressBarElement = elmnt.findElementById("progressbar");
}

@Override
public void init(Parameters prmtrs) {
}

public void onFocus(boolean getFocus) {
}


@Override
public void stop() {
    super.stop();
    //the pool executor needs to be shut down so the application properly exits.
    exec.shutdown();
}

}

I would suggest making a second app state that creates a Nifty menu for the in-game settings and display (the HUD), and also move the code you posted above to another app state and use that as your loading screen and main menu app state. Then you can use the main class to control which is attached, that’s how I’m handling this for my game right now.

Each AppState then handles the initiation and cleanup for something like your menus, so this makes for a simple way of swapping menu interfaces in and out by attaching/detaching them from the State Manager and enabling/disabling them like I’m doing below

private MainMenuState ms;
private GameState gs;
private HUDInterface hud;

public void toGame(ArrayList spells, Weapon weapon, HashMap kb){
       hud = new HUDInterface(gs, app, this);
       hud.setEnabled(true);
       app.getStateManager().attach(hud);
       gs = null;
       gs = new GameState(app, hud, this, spells, weapon, kb, keys);
       gs.setEnabled(true);
       app.getStateManager().attach(gs);
       app.getFlyByCamera().setEnabled(true);
       app.getFlyByCamera().setDragToRotate(false);
       System.gc();
    }

    public void stopGame(){
       hud.setEnabled(false);
       app.getStateManager().detach(hud);
       gs.setEnabled(false);
       app.getStateManager().detach(gs);
       
       toMainMenu();
       app.getFlyByCamera().setDragToRotate(true);

    }
    
    public void stopMainMenu(){
       ms.setEnabled(false); 
       app.getStateManager().detach(ms);
       ms = null;
    }
     
    public void toMainMenu(){
        if(ms == null){
            ms =  new MainMenuState(app, this, ownedSpells, ownedWeapons, height, width);
        }
        ms.setEnabled(true);
        app.getStateManager().attach(ms);
    }

Then you can write a method in your HUD app state to show and hide your settings interface, something like hud.showSettings(boolean b); that gets called when the player presses a key to open up the settings menu.

I’ll show you the AppState the cookbook has shown me for the optionsMenu.xml. And the above code would be used as a basis for all xml oriented menus or messages FOR the AppState correct?

This is my AppState:
public class NiftyAppState extends AbstractAppState implements ActionListener {

private NiftyJmeDisplay niftyDisplay;
private Nifty nifty;
@Override
public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);
    NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(
    app.getAssetManager(), app.getInputManager(), app.getAudioRenderer(),
    app.getRenderManager().getPostView("Gui Default"));
    
    app.getRenderManager().getPostView("Gui Default")
            .addProcessor(niftyDisplay);
    app.getInputManager().deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT);
    app.getInputManager().addMapping("TOGGLE_OPTIONS", new KeyTrigger
    (KeyInput.KEY_ESCAPE));
    app.getInputManager().addListener(this, "TOGGLE_OPTIONS");
    
    //TODO: initialize your AppState, e.g. attach spatials to rootNode
    //this is called on the OpenGL thread after the AppState has been attached
}
public void onAction(String name, boolean isPressed, float tpf){
    if(name.equals("TOGGLE_OPTIONS") && isPressed){
        ((Test)nifty.getCurrentScreen().getScreenController())
                .toggleOptionsMenu();
    }
}

}

The optionsMenu shows up just fine. I’m just letting you see the AppState before I get to researching. I’m glad you are helping me. It sounds like an interface is what I need to use for the xmls

I’m not too familiar with the example your referencing myself (i do things slightly different in my menus), but yes each menu app state will be the basis for interacting with the XML layout associated with that AppState.

In regards to initiation, you may need to load the default controls and styles still as well:

        nifty = niftyDisplay.getNifty();
        nifty.loadStyleFile("nifty-default-styles.xml");       // <-- you may also need to call this line
        nifty.loadControlFile("nifty-default-controls.xml");   // <-- and this one
        nifty.fromXml("Interface/HUD.xml", "start", this);
        guiViewPort.addProcessor(niftyDisplay);

You can also use this code to find a specific elements or control on your screen to show/hide them, that’s how I display and hide my settings menu

    Element settingsPanel = nifty.getScreen("start").findElementById("settingsPanel");
    settingsPanel.setVisible(false);

Here is my final code before I get to studying. If anyone has any suggestions for making a main class to run these two classes for my menu to pop up(AppState Class and Controller Class)…that would be nice. Feast your eyes and let me know if I have any errors :).

public class NiftyAppState extends AbstractAppState implements ActionListener{

private NiftyJmeDisplay niftyDisplay;
private Nifty nifty;
private SimpleApplication simpApp;
private AppStateManager stateManager;

public static void main(String[]args){
   
}
@Override
public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);
    
    NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(
    app.getAssetManager(), app.getInputManager(), app.getAudioRenderer(),
    app.getRenderManager().getPostView("Gui Default"));
    
    app.getRenderManager().getPostView("Gui Default")
            .addProcessor(niftyDisplay);
    app.getInputManager().deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT);
    app.getInputManager().addMapping("TOGGLE_OPTIONS", new KeyTrigger
    (KeyInput.KEY_ESCAPE));
    app.getInputManager().addListener(this, "TOGGLE_OPTIONS");
    
    //TODO: initialize your AppState, e.g. attach spatials to rootNode
    //this is called on the OpenGL thread after the AppState has been attached
}

public void onAction(String name, boolean isPressed, float tpf){
    if(name.equals("TOGGLE_OPTIONS") && isPressed){
        ((NiftyController)nifty.getCurrentScreen().getScreenController())
                .toggleOptionsMenu();
    }
}

}

abstract public class NiftyController extends SimpleApplication
implements ScreenController {

protected NiftyJmeDisplay niftyJme;
protected Nifty nifty;
protected Element settingsPanel;
protected Screen screen;
boolean optionsMenuVisible;
public static void main(String[]args){
   
}
public void simpleInitApp(){
    niftyJme = new NiftyJmeDisplay(assetManager,
            inputManager,
            audioRenderer,
            guiViewPort);
    nifty = niftyJme.getNifty();
    nifty.loadStyleFile("nifty-default-styles.xml");
    nifty.loadControlFile("nifty-default-controls.xml");
    nifty.fromXml("Interface/optionsmenu.xml","start", this);
    guiViewPort.addProcessor(niftyJme);
}

public void toggleOptionsMenu() {
nifty.gotoScreen(“options”);
optionsMenuVisible=true;
}
public void bind(Screen screen, Nifty nifty, Element element){
settingsPanel=nifty.getScreen(“start”).findElementById(“optionsPanel”);
settingsPanel.setVisible(false);
}

}

I got it now

1 Like