Stop a control

Hello,
in my game I’ve got an GameRunningAppState. This is where nearly everything of the game is done. When the player dies, I show a nifty screen and want the game to be paused/finished. So I detach the GameRunningAppState. In the cleanUp method of it, I detach everything from the PhysicsSpace and detach all AppStates and so on, but somehow the loops of the controls are still being executed… That’s my cleanUp:

[java]pM.clear();
app.getRootNode().detachAllChildren();
app.getGuiNode().detachAllChildren();
pM = null;
app.getStateManager().detach(app.getStateManager().getState(MusicState.class));
app.getStateManager().detach(app.getStateManager().getState(ColtState.class));
app.getStateManager().detach(app.getStateManager().getState(ScarState.class));
app.getStateManager().detach(app.getStateManager().getState(RifleState.class));
app.getStateManager().detach(app.getStateManager().getState(SniperState.class));
app.getStateManager().detach(app.getStateManager().getState(AxeState.class));
app.getStateManager().detach(app.getStateManager().getState(ViewPortState.class));

    app.getRootNode().detachAllChildren();
    app.getGuiNode().detachAllChildren();[/java]

where pM is a PhysicsManager:

[java]/*

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

import com.jme3.bullet.BulletAppState;
import java.util.ArrayList;

/**
*

  • @author Mathias
    */
    public class PhysicsManager {

    private BulletAppState bulletAppState = new BulletAppState();
    private ArrayList<Object> physicsSpaceObjects = new ArrayList<>();

    public PhysicsManager(Fearkill app) {
    app.getStateManager().attach(bulletAppState);
    }

    public void add(Object o) {
    bulletAppState.getPhysicsSpace().add(o);
    physicsSpaceObjects.add(o);
    }

    public void remove(Object o) {
    bulletAppState.getPhysicsSpace().remove(o);
    physicsSpaceObjects.remove(o);
    }

    public void clear() {
    for(Object o : physicsSpaceObjects) {
    bulletAppState.getPhysicsSpace().remove(o);
    }
    }

}
[/java]

But as soon as this AppState gets detached, I get a NPE in a control for an enemy where it tries to get a variable of the GameRunningState. But shouldn’t the control be stopped because I detached it from the PhysicsSpace?

EDIT: With a System.out.println() I recognicedthat the method clear() wasn’t even executed before I got the error. But executing it before detaching the AppState didn’t work either…

the call to Control.update(tpf) doesnt come from the bullet PhysicsSpace, it comes from the game loop/scene graph.

You need to remove the control from the spatial it’s attached to, or remove the spatial from the scene graph (in addition to removing the control from the Physics Space as you are already doing)

For all physics controls, setEnabled(false) also removes them from the physics space and disables the transfer of the physics object translation.

Hmm I can’t get it to work…
Here is the code being executed before I detach the game running appstate:
[java]for(Spatial s : app.getStateManager().getState(GameRunningState.class).getEnemies().getChildren()) {
s.getControl(EnemyControl.class).setEnabled(false);
s.removeControl(EnemyControl.class);
}
app.getStateManager().getState(GameRunningState.class).getPhysicsManager().clear();
app.getStateManager().getState(GameRunningState.class).getEnemies().detachAllChildren();
app.getRootNode().detachAllChildren();
System.out.println(“detaching appstate”);[/java]
And this is the content of the cleanUp method:
[java]app.getGuiNode().detachAllChildren();
pM = null;
app.getStateManager().detach(app.getStateManager().getState(MusicState.class));
app.getStateManager().detach(app.getStateManager().getState(ColtState.class));
app.getStateManager().detach(app.getStateManager().getState(ScarState.class));
app.getStateManager().detach(app.getStateManager().getState(RifleState.class));
app.getStateManager().detach(app.getStateManager().getState(SniperState.class));
app.getStateManager().detach(app.getStateManager().getState(AxeState.class));
app.getStateManager().detach(app.getStateManager().getState(ViewPortState.class));[/java]

This is the exception (in the update loop of EnemyControl):

[java]java.lang.NullPointerException
at net.softwarepage.fearkill.EnemyControl.update(EnemyControl.java:223)
at com.jme3.scene.Spatial.runControlUpdate(Spatial.java:570)
at com.jme3.scene.Spatial.updateLogicalState(Spatial.java:688)
at com.jme3.scene.Node.updateLogicalState(Node.java:145)
at com.jme3.scene.Node.updateLogicalState(Node.java:152)
at com.jme3.scene.Node.updateLogicalState(Node.java:152)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:244)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Thread.java:744)[/java]

Maybe some of the entities appear twice in the list and thus have the control already removed, resulting in a NPE when they are worked off the second time?

Do you mean in the list enemies.getChildren() ? Because everything is only once in there. And the exception is not at s.removeControl(EnemyControl.class); but in the update loop of a control. So somehow it is still being executed and because it tries to do something with the GameRunningState I get a NPE.

Well, what is this line of code:
net.softwarepage.fearkill.EnemyControl.update(EnemyControl.java:223)

We can’t see it.

It’s 100% clear from your stack trace that the control is still in attached to a node that is still in the scene graph. Perhaps you added the control more than once?

Either way, there is still stuff in your scene graph at that point. It looks like it might be a Geometry three levels deep.

I’ve now searched thorugh all the files but I’m just adding using this:
[java]private Spatial makeEnemy(Vector3f position, Size size) {
Spatial enemy = app.getAssetManager().loadModel(“Models/skeleton.j3o”);
EnemyControl enemyControl;
boolean small = size == Size.Small ? true : false;
if (small) {
//enemy.scale(0.5f);
enemyControl = new EnemyControl(app, position, size, 1f, 4f, 30f);
//enemyControl = new BetterCharacterControl(1f, 4f, 30f);
enemyControl.setGravity(new Vector3f(0f, -30f, 0f));
// enemy.setUserData(“Health”, 20f);
} else {
enemy.scale(7f);
enemyControl = new EnemyControl(app, position, size, 4f, 30f, 170f);
// enemyControl = new BetterCharacterControl(4f, 30f, 170f);
enemyControl.setGravity(new Vector3f(0f, -50f, 0f));
// enemy.setUserData(“Health”, 100f);
}
enemy.addControl(enemyControl);
pM.add(enemyControl);
enemyControl.warp(position);
getEnemies().attachChild(enemy); //getEnemies() returns enemies, a node I attach to the rootNode
return enemy;
}[/java]

The line is the following:
[java]
float distance = enemy.getLocalTranslation().distance(app.getStateManager().getState(GameRunningState.class).getPlayer().getLocalTranslation());
[/java]
The problem is probably the getState(GameRunningState.class) because I detached it.

EDIT: I removed the .detach(GameRunningState.class) and I saw that the update loop is called 5 times (probably of 5 different enemies) and than it is not called anymore. Could it possibly be the case that the update loop is still being executed and than the control is set disabled or something liek that?

Where are you running the code from that is detaching stuff?

I’m running the code from the playerControl, when the player’s health is below zero.
After detaching the appstate it seems that one or more controls are still mid update loop and execute it to the end… By adding this line of code I don’t get an exception in the control anymore:
[java]if(app.getStateManager().getState(GameRunningState.class) == null) return;[/java]
But I suddenly get a NPE at another AppState:
[java]
getPhysicsManager().clear(); //cleanup of gamerunningstate
pM = null;
app.getStateManager().detach(app.getStateManager().getState(MusicState.class));
System.out.print("GameRunningState: ");
System.out.println(app.getStateManager().getState(ViewPortState.class) == null);
app.getStateManager().detach(app.getStateManager().getState(ColtState.class));
app.getStateManager().detach(app.getStateManager().getState(ViewPortState.class));

    app.getGuiNode().detachAllChildren();
    app.getRootNode().detachAllChildren(); 

[/java]

As you see, I detach the coltState and then detach the ViewPortState. But I get a NPE in ColtState because the ViewPortState is null. Here there is the cleanup of ColtState:

[java]uper.cleanup();
System.out.print("ColtState7: ");
System.out.println(app.getStateManager().getState(ViewPortState.class) == null);
app.getStateManager().getState(ViewPortState.class).getRoot().detachChild(smoke); //NPE
app.getStateManager().getState(ViewPortState.class).getRoot().detachChild(flame);
app.getInputManager().deleteMapping(“Shoot”);
app.getInputManager().removeListener(actionListener);
app.getStateManager().getState(ViewPortState.class).getRoot().detachChild(sound);
app.getStateManager().getState(ViewPortState.class).getRoot().detachChild(colt);[/java]

That’s the stacktrace:
[java]java.lang.NullPointerException
at net.softwarepage.fearkill.ColtState.cleanup(ColtState.java:173)
at com.jme3.app.state.AppStateManager.terminatePending(AppStateManager.java:261)
at com.jme3.app.state.AppStateManager.update(AppStateManager.java:278)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:239)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Thread.java:744) [/java]

The output is: RunningState: false; ColtState: true
So ViewPortState suddenly becomes null!?

And if I keep the ColtState attached and try just to detach the ViewportState, I get the following exception:
[java]
java.lang.IllegalStateException: Scene graph is not properly updated for rendering.
State was changed after rootNode.updateGeometricState() call.
Make sure you do not modify the scene from another thread!
Problem spatial name: Viewport Root[/java]

where Viewport Root is the Root node of the ViewPort of the ViewPortState which code follows:

[java]package net.softwarepage.fearkill;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.Spatial.CullHint;

public class ViewPortState extends AbstractAppState {

private Camera cam;
private ViewPort view;
private Node root;
private Spatial currentGun;
private Application app;

public ViewPortState() {
    root = new Node("root");
}

public Node getRoot() {
    return root;
}

public Camera getCamera() {
    return cam;
}

public void setCurrentGun(Spatial s) {
    currentGun = s;
} 

public Spatial getCurrentGun() {
    return currentGun;
} 

@Override
public void initialize(AppStateManager stateManager, Application application) {
    super.initialize(stateManager, application);
    app = application;
    root = new Node("Viewport Root");
    root.setCullHint(CullHint.Never);

    cam = app.getCamera().clone();
    cam.setViewPort(0.0f, 1.0f, 0.0f, 1f);

    view = app.getRenderManager().createMainView("GunViewPort", cam);
    getView().setEnabled(true);
    getView().setClearFlags(false, true, false);
    getView().attachScene(root);

    initFlashlight();

    root.updateLogicalState(1);
    root.updateGeometricState();
}

private void initFlashlight() {
    Spatial flashlight = app.getAssetManager().loadModel("Models/flashlight.j3o");
    flashlight.setLocalTranslation(-5.5f, -5.8f, 0f);
    flashlight.rotate(20 * FastMath.DEG_TO_RAD, -20 * FastMath.DEG_TO_RAD, 0);
    app.getStateManager().getState(ViewPortState.class).getRoot().attachChild(flashlight);
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
    sun.setColor(ColorRGBA.White);
    flashlight.addLight(sun);
}

@Override
public void render(RenderManager rm) {
    root.updateGeometricState();
}

@Override
public void update(float tpf) {
    root.updateLogicalState(tpf);
}

@Override
public void cleanup() {
    root.detachAllChildren();
}

/**
 * @return the view
 */
public ViewPort getView() {
    return view;
}

}[/java]

I just don’t get anything anymore :frowning: Why is the ViewPortState suddenly null when I detach the ColtState? And what does that other exception mean?

in your own code… you are removing viewport state

app.getStateManager().detach(app.getStateManager().getState(ColtState.class));
app.getStateManager().detach(app.getStateManager().getState(ViewPortState.class));

cleanup() isnt called directly after you call detach(), cleanup happens on the next update() cycle.

easiest way to fix this is to have colt state cleanup its own spatials, and viewport state clean up its own spatials, instead of having colt state try to clean up viewportstate for it.

Also if you are removing this stuff in a control then there still might be other controls ready to run that haven’t gotten to run yet.

Removing app states from a control is very strange. Can you describe at a higher level what you are trying to do? It seems like some part of your design may be backwards.

Ah ok, I thought it’d instantly call the cleanup method, thanks! So, the way I’m handling things right now is that I have a ViewPortState with one root node and a viewport. When I get a new weapon, another WeaponState is attached and I attach the new Model to the root Node of the ViewPortState. Should I rather have a custom ViewPort with rootNode for each WeaponState, so that I can detach only the things of the AppState in the cleanup method?

@pspeed
I’m writing a Singleplayer FPS and have a PlayerControl which controls the movement and the health of the player. For controlling the health, I have a method setHealth(float health) and when it gets a value that’s <= 0, the player dies. So I detach the GameRunningState and attach another state (which handels the GUI and so on). Where should I rather detach the states?

Thanks @all you already helped me a lot!

One approach would be to ditch the control and just handle the player state in a state… since it’s global.

But given that you already went this way, the easiest is to probably have the control simply tell the state that the player is dead and then let it remove itself on next update or something.

Okay thank you all, I now made a global PlayerState and let each WeaponState have its own Viewport. Thanks, it’s working!