Question

I am making a game in the jMonkey Engine 3 and my world generation code was a bit bulky so I planned to move it to a new class called Generator but when I finally got everything worked out and ran my program, I got a NullPointerException in the place where I tried to use the method from the other class. I have had such bad luck with NullPointerException in the past but this is the worst one yet. The code worked when it was in the main class file. I will give you the code for both classes below as well as the error.

Please note that I am not including the package declaration or the imports to save space.

Class one: Main:

public class Main extends SimpleApplication implements ActionListener {

    private static final Logger logger = Logger.getLogger(Main.class.getName());
    private BulletAppState bulletAppState;
    private CharacterControl playerControl;
    private Vector3f walkDirection = new Vector3f();
    private boolean[] arrowKeys = new boolean[4];
    private CubesSettings cubesSettings;
    private BlockTerrainControl blockTerrain;
    private Node terrainNode = new Node();
    private Generator gen;

    public static void main(String[] args){
        Logger.getLogger("").setLevel(Level.FINE);
        AppSettings s = new AppSettings(true);
        Main app = new Main();
        try {
            s.load("com.bminus");
        } catch(BackingStoreException e) {
            logger.log(Level.SEVERE, "Could not load configuration settings.",e);
        }
        try {
            s.setIcons(new BufferedImage[]{ImageIO.read(new File("assets/Textures/icon.gif"))});
        } catch (IOException e) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Icon file missing",e);
        }
        s.setRenderer(AppSettings.LWJGL_OPENGL2);
        s.setBitsPerPixel(24);
        s.setFrameRate(-1);
        s.setFullscreen(false);
        s.setResolution(640,480);
        s.setSamples(0);
        s.setVSync(true);
        s.setFrequency(60);
        s.setTitle("The Third Power Pre-Alpha 1.1.0");
        try {
            s.save("com.bminus");
        } catch(BackingStoreException e) {
            logger.log(Level.SEVERE, "Could not save configuration settings.",e);
        }
        app.setShowSettings(false);
        app.setSettings(s);
        app.start();
    }

    public Main(){}

    @Override
    public void simpleInitApp(){

        setDisplayFps(false);
        setDisplayStatView(false);
        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        initControls();
        gen.initBlockTerrain(); //THIS LINE IS THE ONE THAT IS NULL!!!
        initGUI();
        initPlayer();
        cam.lookAtDirection(new Vector3f(1, 0, 1), Vector3f.UNIT_Y);
    }

    private void initControls(){
        inputManager.addMapping("fps_show", new KeyTrigger(KeyInput.KEY_F3));
        inputManager.addMapping("fps_hide", new KeyTrigger(KeyInput.KEY_F4));
        inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("forward", new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("backward", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("jump", new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addMapping("place", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
        inputManager.addMapping("break", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
        inputManager.addListener(this, "fps_show");
        inputManager.addListener(this, "fps_hide");
        inputManager.addListener(this, "left");
        inputManager.addListener(this, "right");
        inputManager.addListener(this, "forward");
        inputManager.addListener(this, "backward");
        inputManager.addListener(this, "jump");
        inputManager.addListener(this, "place");
        inputManager.addListener(this, "break");
    }

    private void initGUI(){
        BitmapText crosshair = new BitmapText(guiFont);
        crosshair.setText("+");
        crosshair.setSize(guiFont.getCharSet().getRenderedSize() * 2);
        crosshair.setLocalTranslation(
                (settings.getWidth() / 2) - (guiFont.getCharSet().getRenderedSize() / 3 * 2),
                (settings.getHeight() / 2) + (crosshair.getLineHeight() / 2), 0);
        guiNode.attachChild(crosshair);

        BitmapText instructionsText1 = new BitmapText(guiFont);
        instructionsText1.setText("Left Click: Remove");
        instructionsText1.setLocalTranslation(0, settings.getHeight(), 0);
        guiNode.attachChild(instructionsText1);
        BitmapText instructionsText2 = new BitmapText(guiFont);
        instructionsText2.setText("Right Click: Place");
        instructionsText2.setLocalTranslation(0, settings.getHeight() - instructionsText2.getLineHeight(), 0);
        guiNode.attachChild(instructionsText2);
        BitmapText instructionsText3 = new BitmapText(guiFont);
        instructionsText3.setText("Bottom Layer Cannot Be Broken");
        instructionsText3.setLocalTranslation(0, settings.getHeight() - (2 * instructionsText3.getLineHeight()), 0);
        guiNode.attachChild(instructionsText3);
    }

    private void initPlayer(){
        playerControl = new CharacterControl(new CapsuleCollisionShape((cubesSettings.getBlockSize() / 2), cubesSettings.getBlockSize() * 2), 0.05f);
        playerControl.setJumpSpeed(25);
        playerControl.setFallSpeed(140);
        playerControl.setGravity(100);
        playerControl.setPhysicsLocation(new Vector3f(5, 257, 5).mult(cubesSettings.getBlockSize()));
        bulletAppState.getPhysicsSpace().add(playerControl);
    }

    @Override
    public void simpleUpdate(float lastTimePerFrame) {
        float playerMoveSpeed = ((cubesSettings.getBlockSize() * 2.5f) * lastTimePerFrame);
        Vector3f camDir = cam.getDirection().mult(playerMoveSpeed);
        Vector3f camLeft = cam.getLeft().mult(playerMoveSpeed);
        walkDirection.set(0, 0, 0);
        if(arrowKeys[0]){ walkDirection.addLocal(camDir); }
        if(arrowKeys[1]){ walkDirection.addLocal(camLeft.negate()); }
        if(arrowKeys[2]){ walkDirection.addLocal(camDir.negate()); }
        if(arrowKeys[3]){ walkDirection.addLocal(camLeft); }
        walkDirection.setY(0);
        playerControl.setWalkDirection(walkDirection);
        cam.setLocation(playerControl.getPhysicsLocation());
    }
        @Override
        public void onAction(String actionName, boolean value, float lastTimePerFrame){
        if(actionName.equals("forward")) {
            arrowKeys[0] = value;
        }
        else if(actionName.equals("right")) {
            arrowKeys[1] = value;
        }
        else if(actionName.equals("left")) {
            arrowKeys[3] = value;
        }
        else if(actionName.equals("backward")) {
            arrowKeys[2] = value;
        }
        else if(actionName.equals("jump")) {
            playerControl.jump();
        }
    else if(actionName.equals("fps_show")) {
        setDisplayFps(true);
    }
        else if(actionName.equals("fps_hide")) {
            setDisplayFps(false);
        }
        else if(actionName.equals("place") && value){
            Vector3Int blockLocation = getCurrentPointedBlockLocation(true);
            if(blockLocation != null){
                blockTerrain.setBlock(blockLocation, Block_Wood.class);
            }
        }
        else if(actionName.equals("break") && value){
            Vector3Int blockLocation = getCurrentPointedBlockLocation(false);
            if((blockLocation != null) && (blockLocation.getY() > 0)){
                blockTerrain.removeBlock(blockLocation);
            }
        }
    }

        private Vector3Int getCurrentPointedBlockLocation(boolean getNeighborLocation){
        CollisionResults results = getRayCastingResults(terrainNode);
        if(results.size() > 0){
            Vector3f collisionContactPoint = results.getClosestCollision().getContactPoint();
            return BlockNavigator.getPointedBlockLocation(blockTerrain, collisionContactPoint, getNeighborLocation);
        }
        return null;
    }

    private CollisionResults getRayCastingResults(Node node){
        Vector3f origin = cam.getWorldCoordinates(new Vector2f((settings.getWidth() / 2), (settings.getHeight() / 2)), 0.0f);
        Vector3f direction = cam.getWorldCoordinates(new Vector2f((settings.getWidth() / 2), (settings.getHeight() / 2)), 0.3f);
        direction.subtractLocal(origin).normalizeLocal();
        Ray ray = new Ray(origin, direction);
        CollisionResults results = new CollisionResults();
        node.collideWith(ray, results);
        return results;
    }

    public void attachRootChildTerrain(){ //THIS IS CALLED IN THE GENERATOR CLASS!!
        rootNode.attachChild(gen.terrainNode);
    }
}

Class two: Generator:

public class Generator {

    private CubesSettings cubesSettings;
    private BlockTerrainControl blockTerrain;
    private final Vector3Int terrainSize = new Vector3Int(1000, 256, 1000);
    private final Vector3Int bottomLayer = new Vector3Int(1000,1,1000);
    private BulletAppState bulletAppState;
    public Node terrainNode = new Node();
    private SimpleApplication sa;
    private Main main;

    public void initBlockTerrain(){
        CubesTestAssets.registerBlocks();
        CubesTestAssets.initializeEnvironment(sa);

        cubesSettings = CubesTestAssets.getSettings(sa);
        blockTerrain = new BlockTerrainControl(cubesSettings, new Vector3Int(7, 1, 7));
        blockTerrain.setBlocksFromNoise(new Vector3Int(), terrainSize, 20f, Block_Grass.class);
        blockTerrain.setBlocksFromNoise(new Vector3Int(), bottomLayer, 0.0f, Block_Stone.class);
        blockTerrain.addChunkListener(new BlockChunkListener(){

            @Override
            public void onSpatialUpdated(BlockChunkControl blockChunk){
                Geometry optimizedGeometry = blockChunk.getOptimizedGeometry_Opaque();
                RigidBodyControl rigidBodyControl = optimizedGeometry.getControl(RigidBodyControl.class);
                if(rigidBodyControl == null){
                    rigidBodyControl = new RigidBodyControl(0);
                    optimizedGeometry.addControl(rigidBodyControl);
                    bulletAppState.getPhysicsSpace().add(rigidBodyControl);
                }
                rigidBodyControl.setCollisionShape(new MeshCollisionShape(optimizedGeometry.getMesh()));
            }
        });
        terrainNode.addControl(blockTerrain);
        terrainNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        main.attachRootChildTerrain();
    }
}

The error:

java.lang.NullPointerException
    at com.bminus.Main.simpleInitApp(Main.java:110)
    at com.jme3.app.SimpleApplication.initialize(SimpleApplication.java:226)
    at com.jme3.system.lwjgl.LwjglAbstractDisplay.initInThread(LwjglAbstractDisplay.java:130)
    at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:207)
    at java.lang.Thread.run(Thread.java:744)

This is the first time I have tried to use multiple classes. I am guessing I forgot to call something from the Generator class or make something public instead of private but I'm not sure. If anyone can figure this out, it would be greatly appreciated. Thanks!

EDIT: I did what you guys suggested (I think) and I got this error:

Exception in thread "main" java.lang.StackOverflowError
    at sun.misc.Unsafe.putObject(Native Method)
    at java.util.concurrent.ConcurrentLinkedQueue$Node.<init>(ConcurrentLinkedQueue.java:187)
    at java.util.concurrent.ConcurrentLinkedQueue.<init>(ConcurrentLinkedQueue.java:255)
    at com.jme3.app.Application.<init>(Application.java:94)
    at com.jme3.app.SimpleApplication.<init>(SimpleApplication.java:102)
    at com.jme3.app.SimpleApplication.<init>(SimpleApplication.java:98)
    at com.bminus.Main.<init>(Main.java:88)
    at com.bminus.Generator.<init>(Generator.java:31)
    at com.bminus.Main.<init>(Main.java:47)

and since it is an overflow error the last two error lines go on FOREVER! The lines in my code that are being called as errors are:

    public Main(){}

,

    private Main main = new Main();

and

    private Generator gen = new Generator(this);

I don't know exactly why this is happening but if any of you guys did, it would be great to know how to fix it. Thanks!

Was it helpful?

Solution

Most important when debugging NullPointerException's is to find the line that throws the exception. A variable that you are trying to use on that line is null.


Possible culprits:

In your Main class, your Generator variable, gen, is never assigned an instance, and thus is null. Give it an instance by calling the Generator constructor.

gen = new Generator();

In your Generator class, the Main field, main, is null since you never assign to it a Main instance. I suggest that you give the constructor a Main parameter so you can pass in the currently used Main instance created in your main method (these names are confusing!) into your Generator class and use this parameter to initialize your main field.

i.e.,

public Generator(Main main) {
  //.....

  this.main = main; // give the main field an object
}

So change the above code to create Generator to:

gen = new Generator(this);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top