Nifty throws NullPointerException every now and then [SOLVED]

In my game, I am able to select geometries to be targeted by right clicking them. When a geometry is right clicked, a menu appears, but sometimes the game is terminated and the error below is thrown in the console:



init:

Deleting: C:UsersArjenDocumentsjMonkeyProjectsEnemyAheadbuildbuilt-jar.properties

deps-jar:

Updating property file: C:UsersArjenDocumentsjMonkeyProjectsEnemyAheadbuildbuilt-jar.properties

Compiling 1 source file to C:UsersArjenDocumentsjMonkeyProjectsEnemyAheadbuildclasses

compile:

run:

Target: Five-Inch_Gun_Turret1 at position (-0.078137845, 0.675029, 68.92513)

31-jul-2012 19:16:19 com.jme3.app.Application handleError

SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

java.lang.NullPointerException

at de.lessvoid.nifty.screen.Screen.forwardMouseEventToLayers(Screen.java:349)

at de.lessvoid.nifty.screen.Screen.mouseEvent(Screen.java:336)

at de.lessvoid.nifty.Nifty.forwardMouseEventToScreen(Nifty.java:266)

at de.lessvoid.nifty.Nifty.access$1400(Nifty.java:73)

at de.lessvoid.nifty.Nifty$NiftyInputConsumerImpl.processEvent(Nifty.java:1370)

at de.lessvoid.nifty.Nifty$NiftyInputConsumerImpl.processMouseEvent(Nifty.java:1329)

at com.jme3.niftygui.InputSystemJme.onMouseMotionEventQueued(InputSystemJme.java:136)

at com.jme3.niftygui.InputSystemJme.forwardEvents(InputSystemJme.java:234)

at de.lessvoid.nifty.Nifty.update(Nifty.java:248)

at com.jme3.niftygui.InputSystemJme.endInput(InputSystemJme.java:92)

at com.jme3.input.InputManager.processQueue(InputManager.java:787)

at com.jme3.input.InputManager.update(InputManager.java:851)

at com.jme3.app.Application.update(Application.java:598)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:233)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:149)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:182)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:223)

at java.lang.Thread.run(Thread.java:662)

AL lib: ReleaseALC: 1 device not closed

BUILD SUCCESSFUL (total time: 1 minute 45 seconds)




Any idea what could possibly cause this error?

Something’s null there: at de.lessvoid.nifty.screen.Screen.forwardMouseEventToLayers(Screen.java:349) ?

I’ve seen this error a few times where I had a “bad” layout. I can’t remember exactly what caused it I think it was a matter of using Builders and forgetting to set something up in one of the builders.



Find a way to reproduce the problem and then use divide and conquer to track down the incorrect bit of the layout.

Trying to reproduce the problem is relatively hard, since there is no consistency in when it occurs. I have tried to look into the ‘de.lessvoid.nifty.screen.Screen.forwardMouseEventToLayers’ but that doesn’t give me a clue:



[java]private boolean forwardMouseEventToLayers(List<Element> layerList, NiftyMouseInputEvent inputEvent) {

//compiled code

throw new RuntimeException(“Compiled Code”);

}[/java]



I have the feeling it has something to do with the position of the mouse on the screen, but when I log the coordinates of the mouse on clicks, they are different everytime the error occurs. But looking in above code and the rest of the error in the log, it seems to be an issue with passing the mouse position to the screen or to the nifty layers.



Well, I am affraid my limited knowledge is not enough here, so if there is anyone who could shine a light or anyone who ccould make something of above mentioned code, that would be really appreciated. Below the XML-file I use (maybe it has to do with that, as @zarch mentioned?):



[xml]<?xml version=“1.0” encoding=“UTF-8”?>

<nifty xmlns=“http://nifty-gui.sourceforge.net/nifty-1.3.xsd” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=“http://nifty-gui.sourceforge.net/nifty-1.3.xsd http://nifty-gui.sourceforge.net/nifty-1.3.xsd”>

<useControls filename=“nifty-default-controls.xml” />

<useStyles filename=“nifty-default-styles.xml” />

<!-- +++++++++++++++++++++++++++++++++++++++ -->

<!-- start screen -->

<!-- +++++++++++++++++++++++++++++++++++++++ -->

<screen id=“start” controller=“enemyahead.MenuController”>

<layer id=“layer” backgroundColor="#0000" childLayout=“horizontal”>

<panel id=“panel_bottom_left” height=“50%” width=“50%” valign=“center” childLayout=“center” backgroundColor="#033f">

<control name=“button” label=“Start” id=“StartButton” align=“center” valign=“center” visibleToMouse=“true”>

<interact onClick=“startGame(hud)”/>

</control>

</panel>

<panel id=“panel_bottom_right” height=“50%” width=“50%” valign=“center” childLayout=“center” backgroundColor="#444f">

<control name=“button” label=“Quit” id=“QuitButton” align=“center” valign=“center” visibleToMouse=“true”>

<interact onClick=“quitGame()”/>

</control>

</panel>

</layer>

</screen>

<screen id=“actionsmenu” controller=“enemyahead.MenuController”>

<layer id=“layer” backgroundColor="#0000" childLayout=“absolute”>

<panel width=“100%” height=“100%” childLayout=“absolute-inside” backgroundColor="#0000">

<panel id=“panel_menu_background” height=“80” width=“120” valign=“center” childLayout=“vertical” backgroundColor="#033f">

<text id=“selection” font=“aurulent-sans-16.fnt” color="#f00f" text="Selected: " align=“center” valign=“center” />

<control name=“button” label=“Attack” id=“AttackButton” align=“center” valign=“center” visibleToMouse=“true”>

<interact onClick=“attackTarget()”/>

</control>

<control name=“button” label=“Cancel Attack” id=“CancelAttackButton” align=“center” valign=“center” visibleToMouse=“true”>

<interact onClick=“cancelAttack()”/>

</control>

<control name=“button” label=“Cancel” id=“CancelButton” align=“center” valign=“center” visibleToMouse=“true”>

<interact onClick=“cancelMenu()”/>

</control>

</panel>

</panel>

</layer>

</screen>

</nifty>

[/xml]

That’s Nifty’s method definition:



[java]

/**

  • forward mouse event to the given layer list.
  • @param layerList layer list
  • @param inputEvent TODO
  • @return TODO

    */

    private boolean forwardMouseEventToLayers(final List < Element > layerList, final NiftyMouseInputEvent inputEvent) {

    mouseOverHandler.reset();



    long eventTime = timeProvider.getMsTime(); // Line 349 is this. THAT is ODD. jME3’s timer going flunky?

    for (int i=0; i<layerList.size(); i++) {

    Element layer = layerList.get(i);

    layer.buildMouseOverElements(inputEvent, eventTime, mouseOverHandler);

    }



    if (log.isLoggable(Level.FINE)) {

    log.fine(mouseOverHandler.getInfoString());

    }



    mouseOverHandler.processMouseOverEvent(rootElement, inputEvent, eventTime);

    mouseOverHandler.processMouseEvent(inputEvent, eventTime);



    return mouseOverHandler.hitsElement();

    }

    [/java]

@madjack: Thanks for pointing that line 349 out. Do you know of a way to somehow test what the JME3 timer is doing? Can I somehow track something to see what happends?



As said before, my programming skills are limited in a way that I know how to get things done (well, it involves a lot of trial and error, reading the forum and google…), but digging into the engine is more then a bridge to far at this moment. I am already happy with the fact that I have my ships moving in the waves of the ocean in my game-to-be :slight_smile:



Given the fact that it happends every now and then, it occurs to me it could be something caused by internal timing? Maybe things I do in my code? Can it be because I both use the prePhysicTicks as well as the simpleUpdate loops? Do they interfere somehow? Again, maybe just jibberish, but these are things which come to my mind. If it is not possible this causes the error, I do not have to dig into it deeper anymore :slight_smile:



I wil be happy to give someone the entire project-folder to test, but I understand this is not something someone is eager to do :slight_smile: :slight_smile:

More likely the version of Nifty is slightly different and it’s actually layer or mouseOverHandler or something being null.



@Husky, I don’t suppose there is any risk that you are accidentally modifying the layout while not on the render thread? For example if you removed a layer while its iterating over that for loop it could potentially cause all sorts of funkiness.

Did a bit of digging and TimeProvider. java is a Nifty interface.



For some reason I don’t have the source for that one. O_o It’s been deprecated on Nifty’s GitHub and I can’t find the other version so I was wrong above. This isn’t jME’s fault.

@zarch: I perform some changes when it comes to layout and text value of the nifty menu in the ActionsListener(). Here I call for a function (when the right mouse button is clicked). In this function, I do the following:



[java]

nifty.getScreen(“actionsmenu”).findElementByName(“panel_menu_background”).setConstraintX(new SizeValue(Integer.toString((int)click2d.x)));

nifty.getScreen(“actionsmenu”).findElementByName(“panel_menu_background”).setConstraintY(new SizeValue(Integer.toString(screenHeight-(int)click2d.y)));

nifty.getScreen(“actionsmenu”).findElementByName(“selection”).getRenderer(TextRenderer.class).setText(target.getName());

nifty.getScreen(“actionsmenu”).findElementByName(“panel_menu_background”).layoutElements();

nifty.gotoScreen(“actionsmenu”);[/java]



Basically, what it does is getting the x, y coordinates of the mouse click and popup a menu on that location. In this menu, the text of the selected target is displayed. The error occurs as soon as I right click on the screen (so when the menu is not visible yet).

Edit: Just before the application terminates due to the error, the nifty menu is shown.

@husky

I do lots of that too and that error never popped up. :confused:

I think this is another one that might need some input from @void256.



Are you using nifty popup menus at all?



The actions listener is a JME3 input one? If so that’s fine as they are called on the render thread.

Well, time for some more insight information, I guess! Please see below for my entire Main function (I hope you can read through the mess… :roll:):



[java]package enemyahead;



import com.bulletphysics.collision.dispatch.UnionFind.Element;

import com.jme3.app.SimpleApplication;

import com.jme3.bullet.BulletAppState;

import com.jme3.bullet.PhysicsSpace;

import com.jme3.bullet.PhysicsTickListener;

import com.jme3.bullet.collision.shapes.BoxCollisionShape;

import com.jme3.bullet.control.RigidBodyControl;

import com.jme3.collision.CollisionResults;

import com.jme3.font.BitmapText;

import com.jme3.input.KeyInput;

import com.jme3.input.MouseInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.AnalogListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.input.controls.MouseAxisTrigger;

import com.jme3.input.controls.MouseButtonTrigger;

import com.jme3.light.AmbientLight;

import com.jme3.light.DirectionalLight;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.FastMath;

import com.jme3.math.Quaternion;

import com.jme3.math.Ray;

import com.jme3.math.Vector2f;

import com.jme3.math.Vector3f;

import com.jme3.niftygui.NiftyJmeDisplay;

import com.jme3.renderer.RenderManager;

import com.jme3.scene.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.Spatial;

import com.jme3.scene.Spatial.CullHint;

import com.jme3.scene.shape.Box;

import com.jme3.system.AppSettings;

import com.jme3.texture.Texture;

import com.jme3.util.SkyFactory;

import de.lessvoid.nifty.Nifty;

import de.lessvoid.nifty.elements.render.TextRenderer;

import de.lessvoid.nifty.tools.SizeValue;

import java.util.logging.Level;

import java.util.logging.Logger;

import javax.vecmath.Point3f;



public class Main extends SimpleApplication implements PhysicsTickListener

{

static int screenWidth = 800;//1024;

static int screenHeight = 480;//768;



// Menu:

Nifty nifty;

ActionsMenu actionsMenu;



Geometry target;



boolean isRunning = false;



Node sceneNode; // Node which holds the entire scene.

BulletAppState bulletAppState;



// Ocean:

MyProjectedGrid grid;

Geometry projectedGridGeometry;

ProjectedWaterProcessorWithRefraction waterProcessor;

WaterHeightGenerator whGEN;

int seaState = 8; // State of the ocean (0 = no waves, 8 is storm).



// Sun:

private Vector3f sunPos = new Vector3f(-0.1f, -1.0f, 1.0f);

private Vector3f lightDir = sunPos;



// Ships:

Ship myShip;

Ship myShip1;



// Camera:

float camFrustrumNear = 0.001f; // Clipping plane near.

boolean mouseLook = false;

public float camYaw, camPitch;

private Quaternion camRotator = new Quaternion();



// HUD:

BitmapText hudTextSpeed; // Text to show on the HUD (ships speed).

BitmapText hudTextHeading; // Text to show on the HUD (ships heading).

BitmapText hudTextRudder; // Text to show on the HUD (rudder angle).

BitmapText hudTextTarget; // Text to show on the HUD (target).



// Target information:

Geometry geoSelectedTarget = null;

String strSelectedTarget = “”;



float gravity = 9.81f;



public static void main(String[] args)

{

Main app = new Main();



// Set the screen:

AppSettings settings = new AppSettings(true);

settings.setResolution(screenWidth, screenHeight);

app.setSettings(settings);

app.setPauseOnLostFocus(false); // Keep application running when it is not the active window.

Logger.getLogger("").setLevel(Level.SEVERE); // Disable most output in the logger.

app.showSettings = false; // Remove the Display Settings Screen.

app.start();

}







@Override

public void simpleInitApp()

{

// Use this line when recording:

//stateManager.attach(new VideoRecorderAppState()); //start recording

////////////////////////////////////////



bulletAppState = new BulletAppState();

stateManager.attach(bulletAppState);

bulletAppState.getPhysicsSpace().addTickListener(this);

bulletAppState.getPhysicsSpace().setMaxSubSteps(2); // Compensate for low framerates by making this value ‘2’. It hen runs on 30fps.

bulletAppState.getPhysicsSpace().setGravity(new Vector3f(0f,-gravity,0f));

bulletAppState.setEnabled(false); // This stops all physics (else the ships will sink during pause).



//bulletAppState.getPhysicsSpace().enableDebug(assetManager); // Make collision shapes visible:



// Create a waterheightgenerator, based on the given sea state:

whGEN = new WaterHeightGenerator(seaState);



// Create the menu:

NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, guiViewPort);

nifty = niftyDisplay.getNifty();

nifty.fromXml(“Interface/screen.xml”, “start”, new MenuController(this));

guiViewPort.addProcessor(niftyDisplay);

// nifty.exit();



createScene();

addSkybox();

initKeys();

createHUD();



// Set the camera:

flyCam.setEnabled(false); // Disable the default flyCam.

cam.setLocation(myShip.geoViewersEye.getWorldTranslation()); // Set the camera to the viewers position.

cam.setRotation(myShip.geoViewersEye.getWorldRotation()); // Set the camera to the viewers rotation.

float aspect = (float)cam.getWidth() / (float)cam.getHeight();

cam.setFrustumPerspective(45f, aspect, camFrustrumNear, cam.getFrustumFar());

// Store the initial position of the camera to prevent the camera moving to 0 when mouselook is performed the firts time:

float[] camAngles = new float[3];

cam.getRotation().toAngles(camAngles);

camPitch = camAngles[0];

camYaw = camAngles[1];

cam.update();

updateCamera();

}



@Override

public void simpleUpdate(float tpf)

{

if (isRunning)

{

myShip.updateStuff();

myShip1.updateStuff();

updateHUD(); // Update the information to be displayed on the HUD

}

}



@Override

public void simpleRender(RenderManager rm)

{

}



public void updateCamera()

{

// Position and rotate the camera to the location and rotation of the viewers eye:

cam.setLocation(myShip.geoViewersEye.getWorldTranslation());

cam.setRotation(myShip.geoViewersEye.getWorldRotation());

}



public void createScene()

{

sceneNode = new Node(“Scene”);



// Create the ships:

myShip = new Ship(this, rootNode, new Point3f(-10f , 0, 0f), 270);

myShip1 = new Ship(this, rootNode, new Point3f(0f , 0f, 0f), 270);



// Create the water:

grid = new MyProjectedGrid(timer, cam, 100, 100, 0.2f, new WaterHeightGenerator(seaState));

grid.switchFreeze();

projectedGridGeometry = new Geometry(“Projected Grid”, grid); // create cube geometry from the shape

projectedGridGeometry.setCullHint(CullHint.Never);

projectedGridGeometry.setMaterial(setWaterProcessor());

projectedGridGeometry.setLocalTranslation(0, 0, 0);

rootNode.attachChild(projectedGridGeometry);



//rootNode.setCullHint(CullHint.Never);



// A white, directional light source

DirectionalLight sun = new DirectionalLight();

sun.setDirection(lightDir.normalizeLocal());

sun.setColor(ColorRGBA.White);

rootNode.addLight(sun);



// Add ambient light so the shadow sides are not that dark:

AmbientLight al = new AmbientLight();

al.setColor(ColorRGBA.White.mult(2.5f));

rootNode.addLight(al);



//Create a box for testing the gun shells detonation:

//Box testBox = new Box(new Vector3f(0f,-1f,00f), 8000f, 0.5f, 8000f);

Box testBox = new Box(new Vector3f(0f,-30f,0f), 6000f, 0.1f, 6000f);

Geometry geoTest = new Geometry(“Box”, testBox);

Material matTest = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

matTest.setColor(“Color”, ColorRGBA.White);

geoTest.setMaterial(matTest);

RigidBodyControl rbcTest = new RigidBodyControl(new BoxCollisionShape(new Vector3f(6000f, 0.1f, 6000f)), 0);

geoTest.addControl(rbcTest);

rootNode.attachChild(geoTest);

bulletAppState.getPhysicsSpace().add(geoTest);

rbcTest.setPhysicsLocation(new Vector3f(0f,-30f,0f));

}



private void addSkybox()

{



String dir = “Textures/Sky/skybox/”;

Texture north = assetManager.loadTexture(dir + “1.jpg”);

Texture south = assetManager.loadTexture(dir + “3.jpg”);

Texture east = assetManager.loadTexture(dir + “2.jpg”);

Texture west = assetManager.loadTexture(dir + “4.jpg”);

Texture up = assetManager.loadTexture(dir + “6.jpg”);

Texture down = assetManager.loadTexture(dir + “5.jpg”);



Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);

sky.scale(100f);



sceneNode.attachChild(sky);

rootNode.attachChild(sceneNode);

}





private Material setWaterProcessor()

{



waterProcessor = new ProjectedWaterProcessorWithRefraction(cam,assetManager, seaState);

waterProcessor.setReflectionScene(sceneNode);

waterProcessor.setDebug(false);

viewPort.addProcessor(waterProcessor);

return waterProcessor.getMaterial();

}



public void prePhysicsTick(PhysicsSpace space, float f)

{

if (isRunning)

{

float[] angles = new float[3];

cam.getRotation().toAngles(angles);

grid.update(cam.getViewMatrix().clone());



myShip.animateShip(f);

myShip1.animateShip(f);



updateCamera();

}

}



public void physicsTick(PhysicsSpace space, float f)

{

if (isRunning)

{

myShip.checkWavePos();

myShip1.checkWavePos();

}

}



private void initKeys()

{

// You can map one or several inputs to one named action

inputManager.addMapping(“Sea State 0”, new KeyTrigger(KeyInput.KEY_NUMPAD0));

inputManager.addMapping(“Sea State 1”, new KeyTrigger(KeyInput.KEY_NUMPAD1));

inputManager.addMapping(“Sea State 2”, new KeyTrigger(KeyInput.KEY_NUMPAD2));

inputManager.addMapping(“Sea State 3”, new KeyTrigger(KeyInput.KEY_NUMPAD3));

inputManager.addMapping(“Sea State 4”, new KeyTrigger(KeyInput.KEY_NUMPAD4));

inputManager.addMapping(“Sea State 5”, new KeyTrigger(KeyInput.KEY_NUMPAD5));

inputManager.addMapping(“Sea State 6”, new KeyTrigger(KeyInput.KEY_NUMPAD6));

inputManager.addMapping(“Sea State 7”, new KeyTrigger(KeyInput.KEY_NUMPAD7));

inputManager.addMapping(“Sea State 8”, new KeyTrigger(KeyInput.KEY_NUMPAD8));

inputManager.addMapping(“Steer Left”, new KeyTrigger(KeyInput.KEY_COMMA));

inputManager.addMapping(“Steer Right”, new KeyTrigger(KeyInput.KEY_PERIOD));

inputManager.addMapping(“Midships”, new KeyTrigger(KeyInput.KEY_SLASH));

inputManager.addMapping(“View Down”, new MouseAxisTrigger(1, false));

inputManager.addMapping(“View Up”, new MouseAxisTrigger(1, true));

inputManager.addMapping(“View Left”, new MouseAxisTrigger(0, true));

inputManager.addMapping(“View Right”, new MouseAxisTrigger(0, false));

inputManager.addMapping(“Mouse Look”, new MouseButtonTrigger(mouseInput.BUTTON_LEFT));

inputManager.addMapping(“Engine Stop”, new KeyTrigger(KeyInput.KEY_0));

inputManager.addMapping(“Engine Ahead Dead Slow”, new KeyTrigger(KeyInput.KEY_1));

inputManager.addMapping(“Engine Ahead Slow”, new KeyTrigger(KeyInput.KEY_2));

inputManager.addMapping(“Engine Ahead Standard”, new KeyTrigger(KeyInput.KEY_3));

inputManager.addMapping(“Engine Ahead Full”, new KeyTrigger(KeyInput.KEY_4));

inputManager.addMapping(“Engine Ahead Flank”, new KeyTrigger(KeyInput.KEY_5));

inputManager.addMapping(“Engine Back Slow”, new KeyTrigger(KeyInput.KEY_6));

inputManager.addMapping(“Engine Back Standard”, new KeyTrigger(KeyInput.KEY_7));

inputManager.addMapping(“Engine Back Full”, new KeyTrigger(KeyInput.KEY_8));

inputManager.addMapping(“Engine Back Emergency”, new KeyTrigger(KeyInput.KEY_9));

inputManager.addMapping(“Actions Menu”, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));

inputManager.addMapping(“View Pos Forwards”, new KeyTrigger(KeyInput.KEY_UP));

inputManager.addMapping(“View Pos Backwards”, new KeyTrigger(KeyInput.KEY_DOWN));

inputManager.addMapping(“View Pos Right”, new KeyTrigger(KeyInput.KEY_RIGHT));

inputManager.addMapping(“View Pos Left”, new KeyTrigger(KeyInput.KEY_LEFT));

inputManager.addMapping(“View Pos Up”, new KeyTrigger(KeyInput.KEY_PGUP));

inputManager.addMapping(“View Pos Down”, new KeyTrigger(KeyInput.KEY_PGDN));

inputManager.addMapping(“Fire”, new KeyTrigger(KeyInput.KEY_SPACE));

inputManager.addMapping(“View To Home”, new KeyTrigger(KeyInput.KEY_HOME));

inputManager.addMapping(“Main Menu”, new KeyTrigger(KeyInput.KEY_F1));





// Add the names to the action listener.



inputManager.addListener(actionListener, new String[]

{

“Sea State 0”,

“Sea State 1”,

“Sea State 2”,

“Sea State 3”,

“Sea State 4”,

“Sea State 5”,

“Sea State 6”,

“Sea State 7”,

“Sea State 8”,

“Steer Left”,

“Steer Right”,

“Midships”,

“Mouse Look”,

“Engine Stop”,

“Engine Ahead Dead Slow”,

“Engine Ahead Slow”,

“Engine Ahead Standard”,

“Engine Ahead Full”,

“Engine Ahead Flank”,

“Engine Back Slow”,

“Engine Back Standard”,

“Engine Back Full”,

“Engine Back Emergency”,

“Actions Menu”,

“Fire”,

“View To Home”,

“Main Menu”

});



inputManager.addListener(analogListener, new String[]

{

“View Up”,

“View Down”,

“View Left”,

“View Right”,

“Mouse Look”,

“View Pos Forwards”,

“View Pos Backwards”,

“View Pos Right”,

“View Pos Left”,

“View Pos Up”,

“View Pos Down”

});

}



private ActionListener actionListener = new ActionListener()

{

public void onAction(String name, boolean keyPressed, float tpf)

{

for (int i=0; i<9; i++)

{

if (name.equals("Sea State " + i) && !keyPressed && isRunning)

{

// Change the state of the ocean:

whGEN.setSeaState(i);

grid.myHeightGenerator = new WaterHeightGenerator(i);

}

}

if (name.equals(“Steer Left”) && !keyPressed && isRunning)

{

myShip.setRudder(-5);

}

if (name.equals(“Steer Right”) && !keyPressed && isRunning)

{

myShip.setRudder(5);

}

if (name.equals(“Midships”) && !keyPressed && isRunning)

{

myShip.setRudder(0);

}

if (name.equals(“Mouse Look”) && isRunning)

{

mouseLook = true;

}

if (name.equals(“Mouse Look”) && !keyPressed && isRunning)

{

mouseLook = false;

}

if (name.equals(“Engine Stop”) && !keyPressed && isRunning)

{

myShip.setTrust(0);

}

if (name.equals(“Engine Ahead Dead Slow”) && !keyPressed && isRunning)

{

myShip.setTrust(1);

}

if (name.equals(“Engine Ahead Slow”) && !keyPressed && isRunning)

{

myShip.setTrust(2);

}

if (name.equals(“Engine Ahead Standard”) && !keyPressed && isRunning)

{

myShip.setTrust(3);

}

if (name.equals(“Engine Ahead Full”) && !keyPressed && isRunning)

{

myShip.setTrust(4);

}

if (name.equals(“Engine Ahead Flank”) && !keyPressed && isRunning)

{

myShip.setTrust(5);

}

if (name.equals(“Engine Back Slow”) && !keyPressed && isRunning)

{

myShip.setTrust(6);

}

if (name.equals(“Engine Back Standard”) && !keyPressed && isRunning)

{

myShip.setTrust(7);

}

if (name.equals(“Engine Back Full”) && !keyPressed && isRunning)

{

myShip.setTrust(8);

}

if (name.equals(“Engine Back Emergency”) && !keyPressed && isRunning)

{

myShip.setTrust(9);

}

if (name.equals(“Actions Menu”) && !keyPressed && isRunning)

{

handleSelect();

}

if (name.equals(“Fire”) && !keyPressed && isRunning)

{

myShip.fireGun();

}

if (name.equals(“View To Home”) && !keyPressed && isRunning)

{

myShip.geoViewersEye.setLocalTranslation(myShip.viewersEyePos);

}

if (name.equals(“Main Menu”) && !keyPressed && isRunning)

{

isRunning = false;

bulletAppState.setEnabled(false);

grid.switchFreeze();

nifty.gotoScreen(“start”);

}

}

};



private AnalogListener analogListener = new AnalogListener()

{

public void onAnalog(String name, float value, float tpf)

{

if (name.equals(“View Up”) && mouseLook == true)

{

RotateCamera(value, true);

}

if (name.equals(“View Down”) && mouseLook == true)

{

RotateCamera(-value, true);

}

if (name.equals(“View Left”) && mouseLook == true)

{

RotateCamera(value, false);

}

if (name.equals(“View Right”) && mouseLook == true)

{

RotateCamera(-value, false);

}

if (name.equals(“View Pos Forwards”))

{

myShip.geoViewersEye.setLocalTranslation(myShip.geoViewersEye.getLocalTranslation().x+tpf/10f, myShip.geoViewersEye.getLocalTranslation().y, myShip.geoViewersEye.getLocalTranslation().z);

}

if (name.equals(“View Pos Backwards”))

{

myShip.geoViewersEye.setLocalTranslation(myShip.geoViewersEye.getLocalTranslation().x-tpf/10, myShip.geoViewersEye.getLocalTranslation().y, myShip.geoViewersEye.getLocalTranslation().z);

}

if (name.equals(“View Pos Right”))

{

myShip.geoViewersEye.setLocalTranslation(myShip.geoViewersEye.getLocalTranslation().x, myShip.geoViewersEye.getLocalTranslation().y, myShip.geoViewersEye.getLocalTranslation().z+tpf/10f);

}

if (name.equals(“View Pos Left”))

{

myShip.geoViewersEye.setLocalTranslation(myShip.geoViewersEye.getLocalTranslation().x, myShip.geoViewersEye.getLocalTranslation().y, myShip.geoViewersEye.getLocalTranslation().z-tpf/10);

}

if (name.equals(“View Pos Up”))

{

myShip.geoViewersEye.setLocalTranslation(myShip.geoViewersEye.getLocalTranslation().x, myShip.geoViewersEye.getLocalTranslation().y+tpf/10f, myShip.geoViewersEye.getLocalTranslation().z);

}

if (name.equals(“View Pos Down”))

{

myShip.geoViewersEye.setLocalTranslation(myShip.geoViewersEye.getLocalTranslation().x, myShip.geoViewersEye.getLocalTranslation().y-tpf/10, myShip.geoViewersEye.getLocalTranslation().z);

}

}

};



public void RotateCamera(float value, boolean pitch)

{

if( pitch )

{

camPitch += value;

}

else

{

camYaw += value;

}

camRotator.fromAngles(camPitch, camYaw, 0);

myShip.geoViewersEye.setLocalRotation(camRotator);

}





public void createHUD()

{

hudTextSpeed = new BitmapText(guiFont, false);

hudTextSpeed.setSize(guiFont.getCharSet().getRenderedSize()); // font size

hudTextSpeed.setColor(ColorRGBA.Blue); // font color

hudTextSpeed.setText(“Speed: " + myShip.shipSpeed + " kts”); // the text

hudTextSpeed.setLocalTranslation(0.0f, settings.getHeight(), 0); // position

hudTextHeading = new BitmapText(guiFont, false);

hudTextHeading.setSize(guiFont.getCharSet().getRenderedSize()); // font size

hudTextHeading.setColor(ColorRGBA.Blue); // font color

hudTextHeading.setText("Heading: " + truncate(myShip.getHeadingDegrees(myShip.shipRealHeading), 1) + “°”);

hudTextHeading.setLocalTranslation(0.0f, settings.getHeight()-hudTextSpeed.getLineHeight(), 0); // position

hudTextRudder = new BitmapText(guiFont, false);

hudTextRudder.setSize(guiFont.getCharSet().getRenderedSize()); // font size

hudTextRudder.setColor(ColorRGBA.Blue); // font color

hudTextRudder.setText("Rudder: " + myShip.rudderAngle + “°”);

hudTextRudder.setLocalTranslation(0.0f, settings.getHeight()-hudTextSpeed.getLineHeight()-hudTextHeading.getLineHeight(), 0); // position

hudTextTarget = new BitmapText(guiFont, false);

hudTextTarget.setSize(guiFont.getCharSet().getRenderedSize()); // font size

hudTextTarget.setColor(ColorRGBA.Blue); // font color

hudTextTarget.setText("Target: " + strSelectedTarget);

hudTextTarget.setLocalTranslation(0.0f, settings.getHeight()-hudTextSpeed.getLineHeight()-hudTextHeading.getLineHeight()-hudTextRudder.getLineHeight(), 0); // position

guiNode.attachChild(hudTextSpeed);

guiNode.attachChild(hudTextHeading);

guiNode.attachChild(hudTextRudder);

guiNode.attachChild(hudTextTarget);

}



public void updateHUD()

{

hudTextSpeed.setText(“Speed: " + truncate(myShip.shipSpeed, 1) + " kts”);

hudTextHeading.setText("Heading: " + truncate(myShip.getHeadingDegrees(myShip.shipRealHeading), 1) + “°”);

hudTextRudder.setText("Rudder: " + truncate(myShip.rudderAngleFastMath.RAD_TO_DEG, 1) + “°”);

hudTextTarget.setText("Target: " + strSelectedTarget);

}



private float truncate(float x, int dec)

{

long y =(long)(x
(dec10));

return (float)y/(dec
10);

}





public void handleSelect()

{

// Reset results list.

CollisionResults results = new CollisionResults();

// Convert screen click to 3d position

Vector2f click2d = inputManager.getCursorPosition();

Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();

Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d);

// Aim the ray from the clicked spot forwards.

Ray ray = new Ray(click3d, dir);

// Collect intersections between ray and all nodes in results list.

rootNode.collideWith(ray, results);



if (results.size() > 0)

{

//System.out.println(click2d);

// The closest result is the target that the player picked:

target = results.getClosestCollision().getGeometry();

// Show the actions menu:

nifty.getScreen(“actionsmenu”).findElementByName(“panel_menu_background”).setConstraintX(new SizeValue(Integer.toString((int)click2d.x)));

nifty.getScreen(“actionsmenu”).findElementByName(“panel_menu_background”).setConstraintY(new SizeValue(Integer.toString(screenHeight-(int)click2d.y)));

nifty.getScreen(“actionsmenu”).findElementByName(“selection”).getRenderer(TextRenderer.class).setText(target.getName());

nifty.getScreen(“actionsmenu”).findElementByName(“panel_menu_background”).layoutElements();

nifty.gotoScreen(“actionsmenu”);

}

}

}[/java]



And this is the controller for the menu:

[java]/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.

    */

    package enemyahead;



    import com.jme3.app.state.AbstractAppState;

    import de.lessvoid.nifty.Nifty;

    import de.lessvoid.nifty.screen.Screen;

    import de.lessvoid.nifty.screen.ScreenController;



    /**

    *
  • @author Arjen

    /

    public class MenuController extends AbstractAppState implements ScreenController

    {

    //private Nifty nifty;

    //private Screen screen;

    private Main myGame;



    /
    * custom methods /



    public MenuController(Main mygame)

    {

    myGame = mygame;

    }



    /
    * Nifty GUI ScreenControl methods */



    public void bind(Nifty nifty, Screen screen)

    {

    //this.nifty = nifty;

    //this.screen = screen;

    }



    public void startGame()

    {

    myGame.nifty.exit();

    myGame.isRunning = true;

    myGame.bulletAppState.setEnabled(true);

    myGame.grid.switchFreeze();

    }

    public void quitGame()

    {

    myGame.stop();

    }

    public void cancelMenu()

    {

    System.out.println("Cancel");

    myGame.nifty.exit();

    }

    public void attackTarget()

    {

    System.out.println("Attacking!");

    myGame.myShip.selectedTarget = myGame.target;

    myGame.myShip.doTrack = true;

    myGame.nifty.exit();

    }

    public void cancelAttack()

    {

    System.out.println("Cancel attack!");

    myGame.myShip.selectedTarget = null;

    myGame.myShip.doTrack = false;

    myGame.nifty.exit();

    }



    public void onStartScreen()

    {

    //throw new UnsupportedOperationException("Not supported yet.");

    }



    public void onEndScreen()

    {

    //throw new UnsupportedOperationException("Not supported yet.");

    }

    }[/java]

Sorry, I don’t have time to dig through that. Try and come up with a reliable reproduction of the problem and/or a test case.

@zarch: Sorry, you are right! Here it is, together with the instructions to reproduce the error:



Package name = mygame



Main.java:

[java]package mygame;



import com.jme3.app.SimpleApplication;

import com.jme3.app.state.AbstractAppState;

import com.jme3.input.MouseInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.MouseButtonTrigger;

import com.jme3.niftygui.NiftyJmeDisplay;

import com.jme3.renderer.RenderManager;

import de.lessvoid.nifty.Nifty;

import de.lessvoid.nifty.screen.Screen;

import de.lessvoid.nifty.screen.ScreenController;



public class Main extends SimpleApplication

{

Nifty nifty;

boolean isRunning = false;



public static void main(String[] args)

{

Main app = new Main();

app.start();

}



@Override

public void simpleInitApp()

{

// Create the menu:

NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, guiViewPort);

nifty = niftyDisplay.getNifty();

nifty.fromXml(“Interface/screen.xml”, “start”, new MenuController(this));

guiViewPort.addProcessor(niftyDisplay);



initKeys();



flyCam.setEnabled(false); // Disable the default flyCam.



}



@Override

public void simpleUpdate(float tpf)

{

}



@Override

public void simpleRender(RenderManager rm)

{

}



private void initKeys()

{

inputManager.addMapping(“RMB”, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));



inputManager.addListener(actionListener, new String[]

{

“RMB”,

});

}



private ActionListener actionListener = new ActionListener()

{

public void onAction(String name, boolean keyPressed, float tpf)

{

if (name.equals(“RMB”) && !keyPressed && isRunning)

{

nifty.gotoScreen(“actionsmenu”);

}

}

};

}[/java]



MenuController.java:

[java]/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.

    */

    package mygame;



    import com.jme3.app.state.AbstractAppState;

    import de.lessvoid.nifty.Nifty;

    import de.lessvoid.nifty.screen.Screen;

    import de.lessvoid.nifty.screen.ScreenController;



    /**

    *
  • @author Arjen

    */

    public class MenuController extends AbstractAppState implements ScreenController

    {

    private Main myGame;





    public MenuController(Main mygame)

    {

    myGame = mygame;

    }



    public void bind(Nifty nifty, Screen screen)

    {

    }



    public void startGame()

    {

    myGame.nifty.exit();

    myGame.isRunning = true;

    }

    public void quitGame()

    {

    myGame.stop();

    }

    public void cancelMenu()

    {

    myGame.nifty.exit();

    }

    public void attackTarget()

    {

    myGame.nifty.exit();

    }

    public void cancelAttack()

    {

    myGame.nifty.exit();

    }



    public void onStartScreen()

    {

    //throw new UnsupportedOperationException("Not supported yet.");

    }



    public void onEndScreen()

    {

    //throw new UnsupportedOperationException("Not supported yet.");

    }

    }

    [/java]



    screen.xml (goes in the Project Assets/Interface folder):

    [xml]<?xml version="1.0" encoding="UTF-8"?>

    <nifty xmlns="http://nifty-gui.sourceforge.net/nifty-1.3.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://nifty-gui.sourceforge.net/nifty-1.3.xsd http://nifty-gui.sourceforge.net/nifty-1.3.xsd">

    <useControls filename="nifty-default-controls.xml" />

    <useStyles filename="nifty-default-styles.xml" />

    <!-- +++++++++++++++++++++++++++++++++++++++ -->

    <!-- start screen -->

    <!-- +++++++++++++++++++++++++++++++++++++++ -->

    <screen id="start" controller="mygame.MenuController">

    <layer id="layer" backgroundColor="#0000" childLayout="horizontal">

    <panel id="panel_bottom_left" height="50%" width="50%" valign="center" childLayout="center" backgroundColor="#033f">

    <control name="button" label="Start" id="StartButton" align="center" valign="center" visibleToMouse="true">

    <interact onClick="startGame(hud)"/>

    </control>

    </panel>

    <panel id="panel_bottom_right" height="50%" width="50%" valign="center" childLayout="center" backgroundColor="#444f">

    <control name="button" label="Quit" id="QuitButton" align="center" valign="center" visibleToMouse="true">

    <interact onClick="quitGame()"/>

    </control>

    </panel>

    </layer>

    </screen>

    <screen id="actionsmenu" controller="mygame.MenuController">

    <layer id="layer" backgroundColor="#0000" childLayout="absolute">

    <panel width="100%" height="100%" childLayout="absolute-inside" backgroundColor="#0000">

    <panel id="panel_menu_background" height="80" width="120" valign="center" childLayout="vertical" backgroundColor="#033f">

    <text id="selection" font="aurulent-sans-16.fnt" color="#f00f" text="Selected: " align="center" valign="center" />

    <control name="button" label="Attack" id="AttackButton" align="center" valign="center" visibleToMouse="true">

    <interact onClick="attackTarget()"/>

    </control>

    <control name="button" label="Cancel Attack" id="CancelAttackButton" align="center" valign="center" visibleToMouse="true">

    <interact onClick="cancelAttack()"/>

    </control>

    <control name="button" label="Cancel" id="CancelButton" align="center" valign="center" visibleToMouse="true">

    <interact onClick="cancelMenu()"/>

    </control>

    </panel>

    </panel>

    </layer>

    </screen>

    </nifty>

    [/xml]



    To reproduce the error:
  1. Launch the project
  2. Click ‘Start’ in the first nifty menu
  3. LMB in top left corner of the screen
  4. RMB in same corner → ERROR



    The error occurs when a LMB is registered in the same region as where the popup menu will appear after a RMB (RMB opens the ‘popup’ menu).



    Hope this helps!

Are you holding down or releasing the mouse? Maybe it’s something to do with lingering input effects. Does the error happen when the menu opens or when it closes?



Why are you doing nifty.exit() ?

Error occurs on mouse release. Menu starts to open on release of RMB, then the error occurs. I use nifty.exit() to close the popup menu (get it from screen again). I suppose I can use the visible=false as well, but have not implemented it yet (same as that I have not implemented the nifty integrated popup yet).

I really can’t see anything obviously wrong with what you are doing - although nifty exit is something I’ve never used so I wonder if that’s not helping.



I wonder if somehow nifty is picking up the mouse click and sending it to the menu even though the menu hasn’t fully opened yet.



Try doing an app.enqueue (or any other delay) between the mouse click and the screen change.

1 Like

Try printing the current thread in every callback.

Nonreproducible bugs are usually caused by doing concurrent updates to the same data structure from multiple threads. For example, in JME, it could be that the main thread is still initializing things but the update loop is already running and assuming stable data; this could very well unhinge Nifty.

Doing initKeys before addProcessor might be exactly such a case, depending on whether inputManager.addWhatever are threadsafe or not.

Or it might be that doing nifty.exit in startGame is simply a bad idea. Nifty.exit shuts down Nifty, you can’t expect it to work after that. I suspect you’ll have to remove it as a processor before you do that anyway… but “idiomatic” JME code never shuts down Nifty, you just goTo a blank screen (if you really have no screen to display).



Just toying with ideas here.

1 Like

Thanks mates! It turns out that the exit() is indeed the problem here :roll:



Using the following code solved this issue:

To display the menu →

[java] nifty.getScreen("actionsmenu").findElementByName("panel_menu_background").setVisible(true);

nifty.gotoScreen("actionsmenu");[/java]

and to hide it -->[java]myGame.nifty.getScreen("actionsmenu").findElementByName("panel_menu_background").setVisible(false);[/java]



My apologies for being stubborn!

1 Like

Be careful if you switch to nifty popup menus btw, I had a lot of problems with popup menus and screen transitions (as in its horribly broken if you have a popup open and try and change screens).



In the end I ditched nifty popups entirely and rolled my own popup framework.