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.