Separating code to classes (beginner's question)


#1

Hi,



this line:    MaterialState light = display.getRenderer().createMaterialState();

gives me this error:    Non-static variable display cannot be referenced from a static context.



public class Main extends SimpleGame {
    .....
    public static void main(String[] args) {
        Main app = new Main();
        app.setConfigShowMode(ConfigShowMode.ShowIfNoConfig);
        app.start();
    }
    .....
    protected void simpleInitGame() {
        display.setTitle("Chess");
        Load.loadChessboard();
        .....
    }
}




public class Load extends Main{

    public static void loadChessboard() {
        .....
        //!!Following line prints error: Non-static variable display cannot be referenced from a static context!!
        MaterialState light = display.getRenderer().createMaterialState();
        light.setDiffuse(ColorRGBA.red);
        .....
    }
    .....
}



When I had code from loadChessBoard() directly in simpleInitGame(), I wasn't gettin' that error.

Do you know what's going on and how to solve it?

Thank you.

#2

Since you are coding in java I suppose you know about the concept of "objects". Now static methods in a class are available without creating an object (its just one "global" method and can be called w/o creating an object). Because of that the code that is executed in the static method cannot access variables that are only available in an object instance of that class, as for example the display variable.

So in your example you would have to create an object from your load class and make the loadChessboard() method non-static.



Hope this helps,

Normen


#3

Thank you for response.



I did what you adviced. Now I'm getting this error:


SEVERE: Exception in game loop
java.lang.NullPointerException
        at chess.test.Load.loadChessboard(Load.java:49)
        at chess.test.Main.simpleInitGame(Main.java:103)
        at com.jme.app.BaseSimpleGame.initGame(BaseSimpleGame.java:545)
        at com.jme.app.BaseGame.start(BaseGame.java:74)
        at chess.test.Main.main(Main.java:77)
1.4.2010 13:03:13 com.jme.app.BaseSimpleGame cleanup



on ths line:

MaterialState light = display.getRenderer().createMaterialState();


#4

Its because you extend the Main class and then create an object from that. So you create two jme applications from which only the first one is started. When you try to access the local display variable in the object you create from the Load class it is uninitialized because that jme app is not started. Now don't try to start it, you only want one main jme2 application here.



I dont know what programming languages you used before but it seems you dont understand the concept of objects and classes correctly, its different than in pascal or c where you just call functions in one global namespace.

I suggest you try and get more accustomed to the object oriented design of Java and look at some tutorials or get a book about Java, otherwise you will run into many other problems while using jme.



Hope this helps,

Normen


#5

Hello,
We have a similar Problem, so I thought it’s best to revive this thread instead of opening a new one. How should we create a new class? We tried different ways but we always get one or another error. We tried to separate the code block from the tutorial:

https://wiki.jmonkeyengine.org/jme3/beginner/hello_terrain.html

Could anyone please give us an example how to do it right? I know it’s a beginner question.


#6

What code do you currently have and what are the errors it produces?

I can only assume you want to make a new class that can interact with the SimpleApplication of the main class. To do this you must pass your app variable in the constructor of the new class


#7

My Code is a mess at the moment. Our first problem is: we’re completely clueless how the Non-Mainclass should look like. What should follow to “… extends”? One Version (not this one) opened up, but also did end up in NullPointerException at the first bit of code of the Non-Main-Class. With the current code, Main starts and afterwards Proto would like to start alike.
By the way; when the Terrain-Code is not separated, it works; so all materials are known.
We have a Main class and a Proto class that looks like this (plus the bunch of imports):

/** Code begins here*/
//public class Proto extends Main{
public class Proto extends SimpleApplication{
private TerrainQuad terrain;
Material mat_terrain;

@Override
public void simpleInitApp() {

/** 1. Create terrain material and load four textures into it. */
mat_terrain = new Material(assetManager,
        "Common/MatDefs/Terrain/Terrain.j3md");

/** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */
mat_terrain.setTexture("Alpha", assetManager.loadTexture(
        "Textures/Terrain/splat/alphamap.png"));

/** 1.2) Add GRASS texture into the red layer (Tex1). */
Texture grass = assetManager.loadTexture(
        "Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex1", grass);
mat_terrain.setFloat("Tex1Scale", 64f);

/** 1.3) Add DIRT texture into the green layer (Tex2) */
Texture dirt = assetManager.loadTexture(
        "Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex2", dirt);
mat_terrain.setFloat("Tex2Scale", 32f);

/** 1.4) Add ROAD texture into the blue layer (Tex3) */
Texture rock = assetManager.loadTexture(
        "Textures/Terrain/splat/road.jpg");
rock.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex3", rock);
mat_terrain.setFloat("Tex3Scale", 128f);

/** 2. Create the height map */
AbstractHeightMap heightmap = null;
Texture heightMapImage = assetManager.loadTexture(
        "Textures/Terrain/splat/mountains512.png");
heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
heightmap.load();

/** 3. We have prepared material and heightmap.
 * Now we create the actual terrain:
 * 3.1) Create a TerrainQuad and name it "my terrain".
 * 3.2) A good value for terrain tiles is 64x64 -- so we supply 64+1=65.
 * 3.3) We prepared a heightmap of size 512x512 -- so we supply 512+1=513.
 * 3.4) As LOD step scale we supply Vector3f(1,1,1).
 * 3.5) We supply the prepared heightmap itself.
 */
int patchSize = 65;
terrain = new TerrainQuad("my terrain", patchSize, 513, heightmap.getHeightMap());

/** 4. We give the terrain its material, position & scale it, and attach it. */
terrain.setMaterial(mat_terrain);
terrain.setLocalTranslation(0, -100, 0);
terrain.setLocalScale(2f, 1f, 2f);
rootNode.attachChild(terrain);

/** 5. The LOD (level of detail) depends on were the camera is: */
TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
terrain.addControl(control);  
    flyCam.setMoveSpeed(50);
}

@Override
public void simpleUpdate(float tpf) {

}

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

}

And here is the Main class:

/*Code starts here/
public class Main extends SimpleApplication {
Proto protoMap = new Proto();

public Main() {
  start();

}

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

}

@Override
public void simpleInitApp() {
}

@Override
public void simpleUpdate(float tpf) {

}

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

}


#8

I can post the “better-working code” later the day, that would be reasonable.


#10

… or just create a class that extends BaseAppState and add it to the stateManager.


#11

I guess there is a basic thing that you missunderstood:
your Proto class extends SimpleApplication just as your Main class does.
So question: How many applications do you want to program?
Answer: one :smiley:

Thus only one class should extend SimpleApplication (as the wiki mentions, the name might be missleading, in that it has nothing to do with “simple”, it just means this class already is a simple form of an application, by adding components you make it “non-simple”)

Now since games tend to become complex, youre on the right path not to put all code into your Main class, but separate the code instead.

Not sure how familar you are with programming, especially object oriented programming (you even tried to extend Main in your Proto class), but you can add custom behaviour to your application via “composition over inheritance”, thats what appStates are for. (just as controls are for spatials) Think about the Terrain and the LodControl: you basically add the level-of-detail-ability to your terrain without creating a new class that extends QuadTerrain, instead you add a control to the already existing class to “extend” its capabilities. AppStates are the same for your Application, you dont extend it further and further, instead you add capabilities using AppStates

Now youre on the right track here too, putting your terrain-related code into an appState is reasonable. Just it would have to look something like:

public Main() {
    //do not call start() here, as you are starting the application in the main(String[] args)-Method
    //doesnt make a difference in this case but looks cleaner (and its not good to call methods before an object has been fully instanciated but its more complex)
}

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

@Override
public void simpleInitApp() {
    //here you create an instance of your custom "game component" that 
    //you want to add to your game, ie the terrain
    ProtoTerrainState terrainState = new ProtoTerrainState();
    stateManager.attach(terrainState);

    //and do other setup like flyCam (but in general you will find less 
    //and less code to actually be here in this method)
    flyCam.setMoveSpeed(50);
}

@Override
public void simpleUpdate(float tpf) {

}

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

and your ProtoTerrainState.java:

public class ProtoTerrainState extends BaseAppState  {

    private Node terrainNode = new Node("terrain");

    private Material mat_terrain = null;
    // and so on...

    @Override
    protected void initialize(Application app) {
        //get a reference to the assetManager of the Application
        //remember, you are no longer inside this Application where you have 
        //access to assetManager, rootNode etc.., but instead youre inside another object 
        //and thus need to use the reference that was passed to the method, it will be a 
        //reference to that one single Main-class that you created 
        //in your main(String[] args) Method
        AssetManager assetManager = app.getAssetManager();

        //now initialize everything you want...
        mat_terrain = new Material(assetManager,
                      "Common/MatDefs/Terrain/Terrain.j3md");

        mat_terrain.setTexture("Alpha", assetManager.loadTexture(
                     "Textures/Terrain/splat/alphamap.png"));

        //... and so on...

        //until here:
        //rootNode.attachChild(terrain);
        //this wont work because you dont have access to the rootNode instance
        //instead you add it to the node that you created at the top of this class
        terrainNode.attachChild(terrain);

        TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
        terrain.addControl(control);  

        //just as the rootNode, you cannot use the flyCam here, instead i moved this line into Main-class
        //also because it is not related to the terrain, so no point to put it here
    }

    @Override
    protected void cleanup(Application app) {
    }

    @Override
    protected void onEnable() {
        //now the last important thing is: you have to attach the node that is now holding 
        //the terrain to the rootNode of you application, a good place to do that is here 
        //because it means you can add and remove your terrain simply by enabling / disabling 
        //this AppState
        
        ((SimpleApplication)getApplication()).getRootNode().attachChild(terrainNode);
    }

    @Override
    protected void onDisable() {
        //same as above, just we want to remove it upon disabling
        ((SimpleApplication)getApplication()).getRootNode().detachChild(terrainNode);
    }
    
}

I have not tested this code so i hope i didnt forget anything, but i hope it makes things a little clearer

greetings from the shire,
samwise