State Not Changing

Ok, I am trying to switch states from MainMenu to Game by pressing the D key. This is my code:

[java]public class Main extends SimpleApplication implements ActionListener{

MainMenu MainMenu = new MainMenu();
Game Game = new Game();

public static AppSettings cfg = new AppSettings(true);

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

@Override
public void simpleInitApp() {
    
    initControls();
    
    stateManager.attach(MainMenu);
}

@Override
public void simpleUpdate(float tpf) {
}

@Override
public void simpleRender(RenderManager rm) {
    //TODO: add render code
}

public void onAction(String binding, boolean isPressed, float tpf) {
    System.out.println("I am here!");
    
    if (binding.equals("Next")) {
        stateManager.detach(MainMenu);
        stateManager.attach(Game);
    }
}

private void initControls() {
    inputManager.addMapping("Next", new KeyTrigger(KeyInput.KEY_D));
    
    inputManager.addListener(this, "Next");
}

}[/java]
So when I go into the Menu State, I can see my image but when I hid the D key, nothing happens. What is the error here?

Also, while we are on the topic of states, is it possible to change states from another state, or does the changing have to be done from the main class? The help is appreciated.

Hard to tell what is going on without seeing your AppState classes.

As for changing states, it can be called from anywhere, but you need a reference to the state manager.

EDIT: Couple questions:

Are you detaching the state you are expecting to go away?
If so, in the cleanup method of that state are you removing the input mappings and spatials you no longer want visible/active?

Actually I will post all my code:

MainMenu.java:

[java]public class MainMenu extends AbstractAppState {

private SimpleApplication app;

private Node rootNode;
private Node guiNode;
private AssetManager assetManager;
private AppSettings cfg;
private InputManager inputManager;
private AppStateManager stateManager;

@Override
public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);
    this.app = (SimpleApplication) app;
    
    cfg = Main.cfg;
    
    this.rootNode = this.app.getRootNode();
    this.guiNode = this.app.getGuiNode();
    this.assetManager = this.app.getAssetManager();
    this.inputManager = this.app.getInputManager();
    this.stateManager = this.app.getStateManager();

    initBackground();
    
    
}

@Override
public void update(float tpf) {
}

@Override
public void cleanup() {
    super.cleanup();
}

private void initBackground() {
    Picture pic = new Picture("HUD Picture");
    pic.setImage(assetManager, "/Interface/Splash.png", true);
    pic.setWidth(cfg.getWidth());
    pic.setHeight(cfg.getHeight());
    pic.setPosition(0,0);
    guiNode.attachChild(pic);
}

}[/java]

Game.java

[java]public class Game extends AbstractAppState implements ActionListener{

private SimpleApplication app;

private Node rootNode;
private Node guiNode;
private AssetManager assetManager;
private AppSettings cfg;

private Spatial sceneModel;
private Vector3f lightDir = new Vector3f(-4f,-1f,5f);
private AudioNode Suspense;
private WaterFilter water;

private BulletAppState bulletAppState;
private CharacterControl player;
private Vector3f walkDirection = new Vector3f();
private boolean left = false, right = false, up = false, down = false;

private Vector3f camDirection = new Vector3f();
private Vector3f camLeft = new Vector3f();
private Camera cam;
private InputManager inputManager;

@Override
public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);
    this.app = (SimpleApplication) app;
    
    this.rootNode = this.app.getRootNode();
    this.guiNode = this.app.getGuiNode();
    this.assetManager = this.app.getAssetManager();
    this.inputManager = this.app.getInputManager();
    this.cam = this.app.getCamera();
    
    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);
    
    initScene();
    initLight();
    initMusic();
    initControls();
    
    sceneModel.addControl(new RigidBodyControl(0));
    
    // Collision detection for the player by creating a capsule. There are also numerous other settings.
    CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
    player = new CharacterControl(capsuleShape, 0.05f);
    player.setJumpSpeed(20);
    player.setFallSpeed(30);
    player.setGravity(30);
    player.setPhysicsLocation(new Vector3f(-10, 200, 10)); // Starting position of the player in 3D space

    // Add both the player and the scene into the physics node to make it detect collision
    bulletAppState.getPhysicsSpace().add(sceneModel);
    bulletAppState.getPhysicsSpace().add(player);
}

@Override
public void update(float tpf) {
    camDirection.set(cam.getDirection()).multLocal(0.6f);
    camLeft.set(cam.getLeft()).multLocal(0.4f);
    walkDirection.set(0, 0, 0);
    
    // Actually chaning the direction
    if (left == true) {
        walkDirection.addLocal(camLeft);
    }
    if (right == true) {
        walkDirection.addLocal(camLeft.negate());
    }
    if (up == true) {
        walkDirection.addLocal(camDirection);
    }
    if (down == true) {
        walkDirection.addLocal(camDirection.negate());
    }
    
    player.setWalkDirection(walkDirection); // Setting the player on the direction depending on the button pressed
    cam.setLocation(player.getPhysicsLocation()); // Making the camera follow the player
}

@Override
public void cleanup() {
    super.cleanup();
}

public void onAction(String binding, boolean isPressed, float tpf) {
    if (binding.equals("Left")) {
        if (isPressed == true) { 
            left = true; 
        } else { 
            left = false; 
        }
    } else if (binding.equals("Right")) {
        if (isPressed == true) {
            right = true; 
        } else { 
            right = false; 
        }
    } else if (binding.equals("Up")) {
        if (isPressed == true) { 
            up = true; 
        } else { 
            up = false; }
    } else if (binding.equals("Down")) {
        if (isPressed == true) {
            down = true; 
        } else { 
            down = false; 
        }
    } else if (binding.equals("Jump")) {
        player.jump();
    }
}

private void initScene() {
    sceneModel = assetManager.loadModel("Scenes/Scene.j3o");  
    
    rootNode.attachChild(sceneModel);
}

private void initLight() {
    // Sun
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection(lightDir);
    sun.setColor(ColorRGBA.Gray.clone().multLocal(1.7f));
    rootNode.addLight(sun);
    
    // Another Sun
    DirectionalLight l = new DirectionalLight();
    l.setDirection(Vector3f.UNIT_Y.mult(-1));
    l.setColor(ColorRGBA.Gray.clone().multLocal(0.3f));
    rootNode.addLight(l);
}

private void initMusic() {
    Suspense = new AudioNode(assetManager, "Sounds/Suspense.ogg", true);
    Suspense.setPositional(false);
    Suspense.setVolume(10f);
    Suspense.play();
}

private void initControls() {
    inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
    inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
    inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
    inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
    
    inputManager.addListener(this, "Left");
    inputManager.addListener(this, "Right");
    inputManager.addListener(this, "Up");
    inputManager.addListener(this, "Down");
    inputManager.addListener(this, "Jump");
}

}[/java]

This:
Main main = new Main();

ā€¦inside your app state looks VERY strange. Presumably Main is your SimpleApplication subclass and you should have exactly ONE of those per application. (Get it: one application per application)

Other than that, you should litter your code with printlns and/or run through a debugger to see what is really happening. Does your ā€œI am here!ā€ even show up?

Yea, ā€œI am hereā€ shows up every time I hit D key

EDIT: ā€œI am hereā€ does not work. It was printing before, now it doesnā€™t show up.

When you say nothing happens

What is your expected results as apposed to what doesnā€™t happen?

Is the scene spatial not getting loaded?
Are your physics controls not getting loaded?
Is your camera just not moving?
Or?

In initialize of Game, if you println(ā€œHalloā€); do you see the the output?
In each of the init methods of Game, if you println(ā€œHalloā€); do you see the the output?

I think what @pspeed said is your best bet for finding the issue. Itā€™s hard to tell what is going on without some form of output from different states of your app.

I have println(ā€œHelloā€) in all of the methods of the game class but nothing shows up after I hit the D key. I think the state is not getting called properly. I am not sure what I am doing wrong. Is this the right way to initialize states?

Global Variables of the Main Class:

[java]MainMenu MainMenu = new MainMenu();
Game Game = new Game();[/java]

Hmmmā€¦ one thing I noticed from the code above that will cause confusion for you later:

[java]
Game Game = new Game();
[/java]

Perhaps use lowercase on the first letter of the field? It makes the code much easier to read at a glance when using things like:

[java]
if (Game instanceof Game) { } // Wouldnā€™t know what to think if I saw this
[/java]

Other things you can try:

[java]
Game game;
MainMenu mainMenu;

public void simpleInitApp() {
initControls();
mainMenu = new MainMenu(); // Add a println throughout the constructor to ensure all is happening as expected
game = new Game(); // Add a println throughout the constructor to ensure all is happening as expected
stateManager.attach(MainMenu); // Add a println to the initialize method of mainMenu to ensure all is happening as expected
}
[/java]

Also, in Game.java you are binding KEY_D again. This would overwrite the previous binding.

@akshaypathak1011 said: Yea, "I am here" shows up every time I hit D key

EDIT: ā€œI am hereā€ does not work. It was printing before, now it doesnā€™t show up.

Are you going back and editing the original code as this thread progresses? Please donā€™t do that as it makes it impossible to follow what is happeningā€¦ especially when done without comment. One line I commented on is already gone now. A moving target will be impossible to hit unless we know why/how it is moving.

There is certainly something wrong with how you register. I suspect that something was failing before and causing something else to work.

Anyway, if you ever start seeing your ā€œI am hereā€ show up again then try actually printing the values passed to that method and then you can see why your if statements arenā€™t hitting.

Ok, so after considering all the helpful comments people left for me, I decided to recreate it and just try it with two different images. This is my code:

Main Class:
[java]public class Main extends SimpleApplication implements ActionListener{

MainMenu mainMenu = new MainMenu();
Game game = new Game();

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

@Override
public void simpleInitApp() {
    initControls();
    
    stateManager.attach(mainMenu);
}

@Override
public void simpleUpdate(float tpf) {
    //TODO: add update code
}

@Override
public void simpleRender(RenderManager rm) {
    //TODO: add render code
}

private void initControls() {
    inputManager.addMapping("Next", new KeyTrigger(KeyInput.KEY_N));
    
    inputManager.addListener(this, "Next");
}

public void onAction(String binding, boolean isPressed, float tpf) {
    if (binding.equals("Next")) {
        if (isPressed) {
            stateManager.detach(mainMenu);
            stateManager.attach(game);
        }
    }
}

}[/java]

Main Menu Class:
[java]public class MainMenu extends AbstractAppState{

private SimpleApplication app;

private Node rootNode;
private Node guiNode;
private AssetManager assetManager;

@Override
public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);
    this.app = (SimpleApplication) app;
    
    this.rootNode = this.app.getRootNode();
    this.guiNode = this.app.getGuiNode();
    this.assetManager = this.app.getAssetManager();
    
    initBackground();
}

@Override
public void update(float tpf) {
}

@Override
public void cleanup() {
    super.cleanup();
}

private void initBackground() {
    Picture pic = new Picture("HUD Picture");
    pic.setImage(assetManager, "/Interface/Splash1.png", true);
    pic.setWidth(1920);
    pic.setHeight(1080);
    pic.setPosition(0,0);
    guiNode.attachChild(pic);
}

}[/java]

Game Class:
[java]public class Game extends AbstractAppState {

private SimpleApplication app;

private Node rootNode;
private Node guiNode;
private AssetManager assetManager;

@Override
public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);
    this.app = (SimpleApplication) app;
    
    this.rootNode = this.app.getRootNode();
    this.guiNode = this.app.getGuiNode();
    this.assetManager = this.app.getAssetManager();
    
    initBackground();
}

@Override
public void update(float tpf) {
}

@Override
public void cleanup() {
    super.cleanup();
}

private void initBackground() {
    Picture pic = new Picture("HUD Picture");
    pic.setImage(assetManager, "/Interface/Splash2.jpg", true);
    pic.setWidth(1920);
    pic.setHeight(1080);
    pic.setPosition(0,0);
    guiNode.attachChild(pic);
}

}[/java]

This seams to be working fine. When I hit the N key (I decided to change it) the image changes. Now what I donā€™t understand is why the same is code in not working with the actual game code.

UPDATE: So I retried doing it with the actual game code, and it does not work. So I now know that the problem is not with the way I am making my state. The problem is within the game code that I have. I am not exactly sure whats causing it.

When you added it back to your real game code did you still use ā€˜nā€™ or did you go back to using ā€˜dā€™?

InputManager doesnā€™t like when you map two actions to the same key and so you would have to manage that very carefully.

@pspeed said: When you added it back to your real game code did you still use 'n' or did you go back to using 'd'?

InputManager doesnā€™t like when you map two actions to the same key and so you would have to manage that very carefully.

No I still used N

One other thing to note that may eventually cause you problems. Partially since you do the switch on the press instead of the release and totally because you never unregister the listener/mapping, it is highly likely that you will end up attaching multiple game states.

I suspect if you hold down the ā€˜nā€™ key then it will keep adding statesā€¦ but certainly if you press it twice then it will.

Probably you want your main menu to register for the key event on its init and then remove that registration on cleanup. It can call back to the game to say ā€œstartGameā€ or simply attach the game app state itself before committing suicide (self-removing from state manager)

@pspeed said: One other thing to note that may eventually cause you problems. Partially since you do the switch on the press instead of the release and totally because you never unregister the listener/mapping, it is highly likely that you will end up attaching multiple game states.

I suspect if you hold down the ā€˜nā€™ key then it will keep adding statesā€¦ but certainly if you press it twice then it will.

Probably you want your main menu to register for the key event on its init and then remove that registration on cleanup. It can call back to the game to say ā€œstartGameā€ or simply attach the game app state itself before committing suicide (self-removing from state manager)

Actually, if I add println under the ā€œisPressed()ā€ only one line gets printed instead of two.

Infact, if I change the default state(line 15 in my new code) and make it go to Main Menu state after pressing the N key, it works. That means the program works backwards. I donā€™t understand why this is happening.

Why does it work fine when I go from the Game State to the Main Menu State and not work when I go the other way around. I am not sure whats causing this problem.

@akshaypathak1011 said: Why does it work fine when I go from the Game State to the Main Menu State and not work when I go the other way around. I am not sure whats causing this problem.

Out of curiosity, did you resolve the multiple mapping for KEY_D that I pointed out earlier?

1 Like

Yea, I did. I changed to KEY_N

@akshaypathak1011 said: Yea, I did. I changed to KEY_N

Okay, so this section of code from Main.java

[java]
public void onAction(String binding, boolean isPressed, float tpf) {
System.out.println(ā€œTest 1ā€);
if (binding.equals(ā€œNextā€)) {
System.out.println(ā€œTest 2ā€);
if (isPressed) {
System.out.println(ā€œTest 3ā€);
stateManager.detach(mainMenu);
stateManager.attach(game);
}
}
}
[/java]

if you add the above printlnā€™sā€¦ which actually print?
Next question is, is this mapping being removed once you load game? If not, it would just try and remove a state that doesnā€™t exist and then reinitialize the game.
You may want to move these specific bindings to the AppState they apply too, so in initialize of MainMenu these bindings would be added and on cleanup of MainMenu they would be removed. That way you donā€™t have code lingering around that applies to a specific AppState unintentionally