GameState / GameController Questions

I have gotten past the compiling and tools setup stage and I am now trying to grasp some basic concepts.  Darkfrog had a great example in some code I got from a .zip file somewhere on the subject that I have questions on.  I don't know the location to link to anymore but it is pretty brief so I will try to list it here.



Main Class: starts up our game states and the main loop


public class Main
{
public static void main(String [] args) throws InterruptedException
{
        // Enable jME's statistics gathering
        // this is needed for the statisticsGamestate to work
        System.setProperty("jme.stats", "set");
        StandardGame stdGame = new StandardGame("game");
        stdGame.setConfigShowMode(AbstractGame.ConfigShowMode.AlwaysShow);

        // show the GameSettingsPanel
        if (!GameSettingsPanel.prompt(stdGame.getSettings()))
        {
            // user pressed Cancel
            return;
        }
        stdGame.start();

        // create the three gameStates, attach them to the GameState Manager and activate them all
        GameStateManager.getInstance().attachChild(new TextGameState("text"));
        GameStateManager.getInstance().attachChild(new ThreeDGameState("3d"));
        GameStateManager.getInstance().attachChild(new InputGameState("input"));
        GameStateManager.getInstance().activateAllChildren();

        // add a Statistics GameState which is disabled by default
        GameTaskQueueManager.getManager().update(new Callable<Object>()
        {
            public Object call() throws Exception
            {
                GameStateManager.getInstance().attachChild(new StatisticsGameState("statistics", 1f, 0.2f, 0.3f, true));
                return null;
            }
        });
    }
}


3dGameState: simulates the playing of our game with a set of boxes that rotate.  this would have all our real game code

public class ThreeDGameState extends BasicGameState
{
    public ThreeDGameState(String name)
    {
        super(name);
        // shortcut reference
        Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer();

        // make sure the rootNode is rendered in the Transparent Queue
        rootNode.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);

        // Add a light to the Scene
        PointLight light = new PointLight();
        light.setDiffuse( new ColorRGBA( 0.75f, 0.75f, 0.75f, 1f ) );
        light.setAmbient( new ColorRGBA( 0.5f, 0.5f, 0.5f, 1f ) );
        light.setLocation( new Vector3f( 0, 2, 0 ) );
        light.setEnabled( true );
        LightState lightState = renderer.createLightState();
        lightState.setEnabled( true );
        lightState.attach(light);
        rootNode.setRenderState(lightState);

        // we need to cull the backfaces, or Transparent objects will look wrong
        CullState cs = renderer.createCullState();
        cs.setCullFace(Face.Back);
        rootNode.setRenderState(cs);

        // The BlendState is needed to make the Boxes appear transparent
        BlendState bs = renderer.createBlendState();
        bs.setBlendEnabled(true);
        bs.setSourceFunction(SourceFunction.SourceAlpha);
        bs.setDestinationFunction(DestinationFunction.OneMinusSourceColor);
        rootNode.setRenderState(bs);

        // create 5 colorful and spinning boxes
        for (int i = 0; i < 5; i++) {
            Box b = new Box("b", new Vector3f(-1,-1,-1), new Vector3f(1, 1, 1));
            b.setModelBound(new BoundingSphere());
            b.updateModelBound();

            MaterialState ms = renderer.createMaterialState();
            b.setRenderState(ms);
            ms.setAmbient(new ColorRGBA(FastMath.rand.nextFloat(),FastMath.rand.nextFloat(),FastMath.rand.nextFloat(),0.5f));
            ms.setDiffuse(new ColorRGBA(FastMath.rand.nextFloat(),FastMath.rand.nextFloat(),FastMath.rand.nextFloat(),0.5f));
            ms.setSpecular(ColorRGBA.white.clone());
            ms.setShininess(128);
            b.setRenderState(ms);

            // Add a spatial Transformer to let the Boxes Spin wildly
            SpatialTransformer trans = new SpatialTransformer(1);

            Quaternion x0 = new Quaternion();
            x0.fromAngleAxis(0, Vector3f.UNIT_Y);
            Quaternion x180 = new Quaternion();
            x180.fromAngleAxis(FastMath.DEG_TO_RAD*180, Vector3f.UNIT_Y);
            Quaternion x360 = new Quaternion();
            x360.fromAngleAxis(FastMath.DEG_TO_RAD*360, Vector3f.UNIT_Y);
            trans.setRotation(0, 0, x0);
            trans.setRotation(0, 5, x180);
            trans.setRotation(0, 10, x360);
            trans.setObject(b, 0, 0);
            trans.interpolateMissing();
            trans.setRepeatType(Controller.RT_WRAP);
            trans.setPosition(0, 0, new Vector3f(i, i, i));
            trans.setPosition(0, 5, new Vector3f(i, FastMath.rand.nextFloat()*-2, i));
            trans.setPosition(0, 10, new Vector3f(i, i, i));
            b.addController(trans);

            // attach the Box to the root Node
            rootNode.attachChild(b);
        }

        rootNode.updateRenderState();
        rootNode.updateGeometricState(0, true);
    }
}

TextGameState: I think this simulates an option like screen, however, its just text and we would probably want some controls using something like desktop or something

public class TextGameState extends BasicGameState
{
    private float locX = 20;
    private float locY = DisplaySystem.getDisplaySystem().getHeight()-20;
    private float lastLocY = locY;
    private float offsetY = 0;
    private TextureState font = null;
    private BlendState textBlendState = null;

    /**
    * Construct the GameState.
    * @param name the GameStates name.
    */
    public TextGameState(String name)
    {
        super(name);

        // we want the text always pass the ZBuffer Test,, so it gets drawn over everything else.
        ZBufferState zs = DisplaySystem.getDisplaySystem().getRenderer().createZBufferState();
        zs.setEnabled(true);
        zs.setFunction(com.jme.scene.state.ZBufferState.TestFunction.Always);
        rootNode.setRenderState(zs);

        // A BlendState to display the Text correctly
        textBlendState = DisplaySystem.getDisplaySystem().getRenderer().createBlendState();
        textBlendState.setBlendEnabled(true);
        textBlendState.setSourceFunction(SourceFunction.SourceAlpha);
        textBlendState.setDestinationFunction(DestinationFunction.One);
        textBlendState.setTestEnabled(true);
        textBlendState.setTestFunction(TestFunction.GreaterThan);
        textBlendState.setEnabled(true);
        rootNode.setRenderState(textBlendState);

        // add a reasourcelocator, this is needed to find the defaultfont.tga texture
        try
        {
            ResourceLocatorTool.addResourceLocator(ResourceLocatorTool.TYPE_TEXTURE, new SimpleResourceLocator(Ziblit.class.getResource("/com/jme/app/")));
        }
        catch (URISyntaxException e) {e.printStackTrace();}

        // load the font texture for the Ortho GameState
        font = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        font.setTexture(TextureManager.loadTexture(ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_TEXTURE, "defaultfont.tga")));
        font.setEnabled(true);

        // add the text to display
        addText("Press ESC to quit");
        addText("Press F1 to enable/disable the Ortho GameState");
        addText("Press F2 to enable/disable the Main GameState");
        addText("Press F3 to enable/disable the Statistic GameState");

        // make sure to draw this node in the Ortho queue
        rootNode.setRenderQueueMode(Renderer.QUEUE_ORTHO);

        rootNode.updateGeometricState(0.0f, true);
        rootNode.updateRenderState();
    }

    /**
    * Add a Text line.
    * @param text text to add
    */
    private void addText(String text)
    {
        Text txtObj = new Text("text", text);
        txtObj.setLocalTranslation(locX, lastLocY, 0);
        txtObj.setRenderState(font);
        lastLocY = lastLocY - txtObj.getHeight();
        lastLocY -= offsetY;
        rootNode.attachChild(txtObj);
    }

    /**
    * we don't need to update anything, since nothing is moving around.
    */
    public void update(float tpf) { }

    /**
    * not much  to clean up here.
    */
    public void cleanup() { }
}


InputGameState: currently this has all the logic for keyboard inputs in a gamestate

public class InputGameState extends BasicGameState
{
    public InputGameState(String name)
    {
        super(name);

        // the GameControl System:
        // 1. create GameControlManager, needed to create controls from
        GameControlManager manager = new GameControlManager();
        // 2. create a GameControl as input for the Controller later
        GameControl textTogglecontrol = manager.addControl("toggle_text");
        // 3. add a Key Binding to the gameControl (F1)
        textTogglecontrol.addBinding(new KeyboardBinding(KeyInput.KEY_F1));
        // 4. create a Controller, which gets the InputValues from the GameControl
        ActionChangeController textToggleAction = new ActionChangeController(textTogglecontrol, new ControlChangeListener()
        {

            public void changed(GameControl control, float oldValue, float newValue, float time)
            {
                // toggle TextGameState active/inactive
                // get the GameState
                GameState textGS = GameStateManager.getInstance().getChild("text");
                // invert the GameStates active flag
                textGS.setActive(!textGS.isActive());
            }
        });
        // 5. add the controller to a node, so that it gets updated every update cycle.
        rootNode.addController(textToggleAction);

        // adding new actions to the KeybindingManager
        KeyBindingManager.getKeyBindingManager().add("toggle_3d", KeyInput.KEY_F2);
        KeyBindingManager.getKeyBindingManager().add("toggle_stat", KeyInput.KEY_F3);
        KeyBindingManager.getKeyBindingManager().set("exit", KeyInput.KEY_ESCAPE);
    }

    /**
    * Check if the a Key that we defined previously, has been pressed and execute the according action
    */
    public void update(float tpf)
    {
        super.update(tpf);
    /*
    if (KeyBindingManager.getKeyBindingManager().isValidCommand("toggle_text", false)) {
        // invert the active flag of the 3D GameState
        GameStateManager.getInstance().getChild("text").setActive(
                !GameStateManager.getInstance().getChild("text").isActive());
    }
        */
   
        // check if F2 has been pressed and toggle the 3D Gamestate
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("toggle_3d", false)) {
            // invert the active flag of the 3D GameState
            GameStateManager.getInstance().getChild("3d").setActive(
                    !GameStateManager.getInstance().getChild("3d").isActive());
        }

        if (KeyBindingManager.getKeyBindingManager().isValidCommand("toggle_stat", false)) {
            // invert the active flag of the 3D GameState
            GameStateManager.getInstance().getChild("statistics").setActive(
                    !GameStateManager.getInstance().getChild("statistics").isActive());
        }

        // check if ESC has been pressed, exit the application if needed
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit", false)) {
            System.out.println("Good Bye");
            System.exit(0);
        }
    }
}


I have 2 questions; one technical and one conceptual.  First, technically I do not understand why the f1 key does not enable or disable like the f2 or f3 keys.  It would appear that it should launch the controller which calls the inner classes changed method which should disable our example option dialog (textgamestate).  All that happens is it flickers.  I also tried to comment out the body of the changed and it just stops the flickering.  Also, I tried to explicitly set it in the update method like the f2 and f3 keys but that had no effect.  Can anyone tell me why this is happening?

Second, Conceptually we are simulating the trapping of input for a particular screen.  If I am in the game and I hit a specified key I want to display an option dialog.  If I hit a key corresponding to an option like f1 for setting keybindings then it should instead display the keybinding screen and trap those inputs.  Translating that to gamestates I would think we would have a different GameControlManager for each gamestate.  I would have keybindings for esc, f1 etc in the 3dgamestate.  If I hit esc the manager would enable the textgamestate (our option screen) which would now have f1 bound to the keybindings gamestate (not coded here) which would have its own manager for inputs etc.  In that way I could have f1 bound to the fist player in my group in-game but keybindings if the option screen is active.  Am I on the right track?

Thanks in advance for advise.

just saw this now,

it was actually just an example i made, to show how to use GameStates :slight_smile:

http://www.jmonkeyengine.com/jmeforum/index.php?topic=8935.30


First, technically I do not understand why the f1 key does not enable or disable like the f2 or f3 keys

F1 uses the ActionChangeController, as soon as the value of the F1 button changes the action gets executed.
As long as you keep F1 pressed the TextGameState is disabled, as soon as you release it, the action gets executed again and the GameState is activated again :)
Try to use the ActionController instead of ActionChangeController.

The Buttons F2 and F3 use the KeyBindingManager which is another way to catch input.

Regarding the 2nd point i cant recommend a specific way to do things, there are soo many different ways to handle input... its best if you play around with it yourself and see what fits your needs best.