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
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
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
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.
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/(dec10);
}
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:
- Launch the project
- Click âStartâ in the first nifty menu
- LMB in top left corner of the screen
- 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.
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.
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!
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.