Menu toggles

Hi

Is there any fancy way to bind a button to toggle a menu? So that a menu is both activated and deactivated by same button ?

This is my implementation of an ingame menu:

[java]/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.
    */
    package alpineterra.appstates;

import alpineterra.AlpineTerraStyles;
import alpineterra.Main;
import com.jme3.app.Application;
import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioNode;
import com.jme3.audio.AudioSource;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Command;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.event.BaseAppState;
import com.simsilica.lemur.style.ElementId;

/**
*

  • @author asf
    */
    public class InGameMenuState extends BaseAppState{

    private Container menu;

    private AudioNode selectUp;
    private AudioNode selectDown;
    private AudioNode selectNeutral;

    private AudioNode music;

    public InGameMenuState() {
    }

    @Override
    protected void initialize( Application app ) {
    menu = new Container(new SpringGridLayout(), new ElementId(AlpineTerraStyles.MENU_ID), “retro”);
    menu.addChild(new Label(“Alpine Terra”, new ElementId(AlpineTerraStyles.MENU_TITLE_ID), “retro”));

     Button multipliers = menu.addChild(new Button("Multipliers", "retro"));
     multipliers.addClickCommands(new Multipliers());
     multipliers.addCommands(Button.ButtonAction.HighlightOn, new Highlight());
    
     Button exitToMainMenu = menu.addChild(new Button("Exit to main menu", "retro"));
     exitToMainMenu.addClickCommands(new ExitToMainMenu());
     exitToMainMenu.addCommands(Button.ButtonAction.HighlightOn, new Highlight());
    
     Button returnToGame = menu.addChild(new Button("Return to game", "retro"));
     returnToGame.addClickCommands(new ReturnToGame());
     returnToGame.addCommands(Button.ButtonAction.HighlightOn, new Highlight());
     
     Camera cam = app.getCamera();
     float menuScale = cam.getHeight()/720f;
    
     Vector3f pref = menu.getPreferredSize();
     menu.setLocalTranslation(cam.getWidth() * 0.5f - pref.x * 0.5f * menuScale,
                              cam.getHeight() * 0.75f + pref.y * 0.5f * menuScale,
                              10);
     menu.setLocalScale(menuScale);
    
     AssetManager assets = app.getAssetManager();
     selectUp = new AudioNode(assets, "Sounds/select-up.ogg", false);
     selectUp.setReverbEnabled(false);
     selectUp.setPositional(false);
     selectDown = new AudioNode(assets, "Sounds/select-down.ogg", false);
     selectDown.setReverbEnabled(false);
     selectDown.setPositional(false);
     selectNeutral = new AudioNode(assets, "Sounds/select-neutral.ogg", false);
     selectNeutral.setReverbEnabled(false);
     selectNeutral.setPositional(false);
    

    }

    @Override
    protected void cleanup(Application app) {
    menu.removeFromParent();
    }

    protected void startMusic() {
    if( music == null || music.getStatus() == AudioSource.Status.Stopped ) {
    AssetManager assets = getApplication().getAssetManager();
    music = new AudioNode(assets, “Sounds/panic-menu-theme.ogg”, true);
    music.setReverbEnabled(false);
    music.setPositional(false);
    music.play();
    }
    }

    protected void stopMusic() {
    if( music != null ) {
    music.stop();
    music = null;
    }
    }

    @Override
    public void update( float tpf ) {
    startMusic();
    }

    @Override
    protected void enable() {
    Main main = (Main)getApplication();
    main.getGuiNode().attachChild(menu);
    getStateManager().getState(GamePlayState.class).setPaused(true);
    startMusic();
    }

    @Override
    protected void disable() {
    menu.removeFromParent();
    stopMusic();
    }

    private class Multipliers implements Command<Button> {
    @Override
    public void execute( Button source ) {
    selectUp.playInstance();
    //getStateManager().attach(new MultiPlayerState());
    getStateManager().getState(GameControlState.class).setEnabled(true);
    setEnabled(false);
    }
    }

    private class ExitToMainMenu implements Command<Button> {
    @Override
    public void execute( Button source ) {
    selectDown.playInstance();
    if (getStateManager().getState(GamePlayState.class) != null) {
    getStateManager().getState(GamePlayState.class).setEnabled(false);
    }
    if (getStateManager().getState(MainMenuState.class) != null) {
    getStateManager().getState(MainMenuState.class).setEnabled(true);
    }
    setEnabled(false);
    }
    }

    private class ReturnToGame implements Command<Button> {
    @Override
    public void execute( Button source ) {
    selectDown.playInstance();
    getStateManager().getState(GamePlayState.class).setPaused(false);
    setEnabled(false);
    }
    }

    private class Highlight implements Command<Button> {
    @Override
    public void execute( Button source ) {
    selectNeutral.playInstance();
    }
    }
    }
    [/java]

Return to game does what I want, basically. But I want the button that activated this menu, to also deactivate it (if it is activated and vice versa)

This is my GameControlState that activates the menu:

[java]package alpineterra.appstates;

import alpineterra.GameFunctions;
import com.jme3.app.Application;
import com.jme3.audio.AudioNode;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.simsilica.es.EntityData;
import com.simsilica.es.EntityId;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.event.BaseAppState;
import com.simsilica.lemur.input.AnalogFunctionListener;
import com.simsilica.lemur.input.FunctionId;
import com.simsilica.lemur.input.InputMapper;
import com.simsilica.lemur.input.InputState;
import com.simsilica.lemur.input.StateFunctionListener;

/**

  • Maps player input into game control.

  • @author Asser Fahrenholz
    */
    public class GameControlState extends BaseAppState
    implements AnalogFunctionListener, StateFunctionListener {

    private long lastFrame;

    private AudioNode shoot;
    private AudioNode thrust;

    Application app;

    public GameControlState( ) {
    }

    @Override
    protected void initialize( Application app ) {
    this.app = app;
    InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
    inputMapper.addStateListener(this,
    GameFunctions.F_PAUSE);

    shoot = new AudioNode(app.getAssetManager(), "Sounds/shoot.ogg", false);
    shoot.setReverbEnabled(false);
    thrust = new AudioNode(app.getAssetManager(), "Sounds/thrust.ogg", false);
    thrust.setReverbEnabled(false);
    thrust.setLooping(true);
    

    }

    @Override
    protected void cleanup( Application app ) {
    InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
    inputMapper.removeStateListener(this,
    GameFunctions.F_PAUSE);
    }

    @Override
    protected void enable() {
    lastFrame = System.nanoTime();

    InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
    inputMapper.activateGroup(GameFunctions.GAMEGROUP);
    

    }

    @Override
    protected void disable() {
    InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
    inputMapper.deactivateGroup(GameFunctions.GAMEGROUP);
    }
    /*
    public void valueActive( FunctionId func, double value, double tpf ) {
    if( func == ShipFunctions.F_TURN ) {

        Velocity vel = ed.getComponent(ship, Velocity.class);
        float rotate = (float)(value * rotateSpeed);
        ed.setComponent(ship, new Velocity(vel.getLinear(), new Vector3f(0,0,rotate)));
    } else if( func == ShipFunctions.F_THRUST ) {
    
        Position pos = ed.getComponent(ship, Position.class);
        accel.set(0,(float)(speed * value),0);
        pos.getFacing().multLocal(accel);
    
        lastThrustTime += tpf;
        if( value != 0 &amp;&amp; lastThrustTime &gt;= thrustInterval ) {
    
            lastThrustTime = 0;
    
            // Create a thrust entity
            EntityId thrust = ed.createEntity();
            Vector3f thrustVel = accel.mult(-1);
            Vector3f thrustPos = pos.getLocation().add(thrustVel.normalize().multLocal(0.1f));
            ed.setComponents(thrust,
                             new Position(thrustPos, new Quaternion()),
                             new Velocity(thrustVel),
                             new ModelType(PanicModelFactory.MODEL_THRUST),
                             new Decay(1000));
    
        } else if( value == 0 ) {
            lastThrustTime = thrustInterval;
        }
    }
    

    }
    */
    @Override
    public void valueChanged( FunctionId func, InputState value, double tpf ) {
    if( func == GameFunctions.F_PAUSE && value == InputState.Positive ) {
    //Pause game
    app.getStateManager().getState(GamePlayState.class).setPaused(true);
    //app.getStateManager().getState(null)
    //Show in game menu
    if(app.getStateManager().getState(InGameMenuState.class) == null){
    app.getStateManager().attach(new InGameMenuState());
    }
    else{
    app.getStateManager().getState(InGameMenuState.class).setEnabled(true);
    }
    //Disable this state
    this.setEnabled(false);
    }
    }

    @Override
    public void update( float tpf ) {

    // Use our own tpf calculation in case frame rate is
    // running away making this tpf unstable
    long time = System.nanoTime();
    long delta = time - lastFrame;
    lastFrame = time;
    if( delta == 0 ) {
        return; // no update to perform
    }
    

    }

    @Override
    public void valueActive(FunctionId func, double value, double tpf) {
    throw new UnsupportedOperationException(“Not supported yet.”); //To change body of generated methods, choose Tools | Templates.
    }

}
[/java]

Off the top of my head, the easiest way might be to use a Checkbox instead of a button. (There is no visual difference unless you give it an icon, as long as you aren’t using the default style which sets up a default icon.)

… then just add a click command that checks the button state to open/close your menu.

You could do the same thing with a regular button but you just have to keep track of the state yourself inside of the click command.

Hope that makes sense. Ask if you need clarification on some point.