Refactoring to StandardGame/gamestates

I'm trying to refactor my game to use StandardGame and StandardGameStates. I'm running into some issues, however.



I have an InGameState class that descends from StandardGameState. The StandardGame initializes and gives a blank screen, but my InGameState constructor fails on the super call to StandardGameState. It seems to be failing in initCamera's createCamera call–specifically:



Sep 23, 2006 2:10:45 PM com.jme.system.lwjgl.LWJGLDisplaySystem <init>
INFO: LWJGL Display System created.
Sep 23, 2006 2:10:49 PM com.jme.renderer.lwjgl.LWJGLRenderer <init>
INFO: LWJGLRenderer created. W:  640H: 480
Sep 23, 2006 2:10:49 PM com.jme.renderer.AbstractCamera <init>
INFO: Camera created.
Sep 23, 2006 2:10:49 PM com.jme.scene.Node <init>
INFO: Node created.
Sep 23, 2006 2:10:49 PM com.jme.scene.Node attachChild
INFO: Child (FPS label) attached to this node (FPS node)
Sep 23, 2006 2:10:49 PM com.jmex.game.state.GameStateManager create
INFO: Created GameStateManager
Sep 23, 2006 2:10:49 PM com.jme.util.lwjgl.LWJGLTimer <init>
INFO: Timer resolution: 1000 ticks per second
Sep 23, 2006 2:10:49 PM com.jme.scene.Node <init>
INFO: Node created.
Exception in thread "main" java.lang.NullPointerException
   at org.lwjgl.opengl.GL11.glMatrixMode(GL11.java:1737)
   at com.jme.renderer.lwjgl.LWJGLCamera.onFrustumChange(Unknown Source)
   at com.jme.renderer.AbstractCamera.<init>(Unknown Source)
   at com.jme.renderer.lwjgl.LWJGLCamera.<init>(Unknown Source)
   at com.jme.renderer.lwjgl.LWJGLRenderer.createCamera(Unknown Source)
   at com.jmex.game.state.StandardGameState.initCamera(Unknown Source)
   at com.jmex.game.state.StandardGameState.<init>(Unknown Source)
   at com.starfireinteractive.torrent.InGameState.<init>(InGameState.java:24)
   at com.starfireinteractive.torrent.Game.main(Game.java:36)



No, I've not built JME in debug mode, so there aren't line numbers for some of those.

Could the problem be that StandardGame has already created a camera, and now StandardGameState is trying to create another? I'd used StandardGameState because it seems logical to me that each state should have its own root node, but perhaps I'm not quite understanding the state system? Or am I on the right track in my understanding but the wrong one with my debugging, and the fact that multiple cameras are being created isn't the issue here? It's all very confusing. :)

My first guess would be a threading issue - do you use threads? Be sure to access gl only from one thread.



And a note on the line numbers: you should really build with all debug stuff on while in development/testing phase. It's a lot easier to find problems then (for you and for others). You can then build an optimized version for release/deployment.

Try using BasicGameState instead.  I think that's more what you're looking for.  StandardGameState existed before I created StandardGame and there is no connection between the two besides sharing "Standard" in their name. ;)  In fact, I'd love to kill that class as it threw me off when I first tried using it myself.



I bet it is a threading issue because the typical way you deal with StandardGame is to instantiate everything outside of the GL thread.  If you need to instantiate something inside it you can use the GameTaskQueueManager and pass a Callable.

Ok, more information on this.



No, my code isn't using threads, or anything terribly complicated for that matter. I've since refactored to remove the need to create a StandardGame subclass for my application, and moved my state code to a subclass of BasicGameState. The problem migrated. It seems like when I try doing anything with the renderer–setting a background color or moving the camera–I get the same basic exception pattern, albeit in different lines of GL11.java. If I comment out the following lines:



      //DisplaySystem.getDisplaySystem().getRenderer().setBackgroundColor(ColorRGBA.black);
      Vector3f loc = new Vector3f(0.0f, 50f, 0.0f);
      Vector3f left = new Vector3f(-1.0f, 0.0f, 0.0f);
      Vector3f up = new Vector3f(0.0f, 0.0f, -1.0f);
      Vector3f dir = new Vector3f(0.0f, -1.0f, 0.0f);
      // Move our camera to a correct place and orientation.
      //DisplaySystem.getDisplaySystem().getRenderer().getCamera().setFrame(loc, left, up, dir);
      //DisplaySystem.getDisplaySystem().getRenderer().getCamera().update();



my game works fine, though the camera isn't positioned correctly.

Am I misunderstanding the intent of GameStates? Mine is an arcade game with a top-down perspective. What I'd hoped to do is tweak the camera perspective upon activation of the GameState such that it can be moved for other states that might need an alternate perspective.

I'm all for cleaning up the GameState system. I understand it now, but there are several classes that make trying to comprehend it somewhat more difficult. Perhaps some of the functionality from the StandardGameState classes could be mixed into BasicGameState? I had to reimplement onActivate/onDeactivate calls from setActive() in my own state, for instance.

I also noticed that my state's update method seems to be called even if it isn't active. Is this a bug, and if not, why not? I have an isActive() check at the beginning of my IngameState#update method, but why might I want to have an inactive state updated? Seems like this might be a quick-and-easy way of pausing a game--just set the IngameState to inactive and its update method isn't called. But unless I'm missing something and update() shouldn't be called, and there's no default support for onActivate/onDeactivate, what purpose does activation serve in BasicGameState?

First of all, if you're using StandardGame you're using threads. You need to show the code you're invoking to create StandardGame and your GameStates, I'm assuming it looks something like this:


StandardGame game = new StandardGame("My Game");
game.start();

MyGameState state = new MyGameState();
state.setActive(true);
GameStateManager.getManager().attachChild(state);



This is probably not exactly right as I don't have the code in front of me, but you must realize that StandardGame intentionally hides the OpenGL thread from you.  When you instantiate MyGameState you are starting it in the default thread but StandardGame created a new thread where the OpenGL updates occur.  Like I said above, if you need to interject something into the OpenGL thread you have to push it in there. For example, if MyGameState needs to be instantiated there you need to do this:

Callable<MyGameState> callable = new Callable<MyGameState>() {
public MyGameState call() throws Exception {
    return new MyGameState();
}
);
MyGameState state = GameTaskQueueManager.getManager().update(callable).get();



Looks moderately more complicated, but there's good reason for this.  The more we move outside the OpenGL queue the more smoothly it will be able to run and the better multiprocessor/hyperthreading support we'll have in our games.  It may be more advantageous to add to the GameTaskQueueManager from within your GameState for the invocation that must be run in your OpenGL thread.

Do you plan on writing some tutorials for your Game classes darkfrog?

Yeah, I just haven't gotten around to it yet.  I'll try to do that soon.  That will hopefully clarify the problems that people seem to be having with it.  It is a drastic change in thought from the way other Games work in jME.

Aha! I was misunderstanding what you meant in your discussion of instantiation, thinking that you meant instantiation of StandardGame subclasses/instances and not GameStates. I had to apply your suggestions to activation of the state rather than instantiation, but other than that it compiles and runs now. :slight_smile:



Ok, here's a stupid question–I'm somewhat new to all of this. I wasn't getting anything rendered to the screen, then realized that I hadn't created any lights. Using the code from the DebugGameState, I created/tweaked a light and now things are appearing. But, any clue why I was able to get away with not setting up lights before, merely using BasicLogicrateGame? I don't think that sets up any lighting…



Thanks for all the help thus far.

I think it disables lighting or something in the scene but I'm not sure.  You've got to realize that StandardGame as opposed to every other "Game" in jME is meant for real games.  All the others are simply examples so they may provide functionality to help get you running.  StandardGame tries not to impose anything on you, but rather helps you to more quickly and easily develop games.

Okay, I found a bit of extra time and instead of procrastinating (as I’m really quite good at), I decided to go ahead and write this tutorial:



http://www.jmonkeyengine.com/jmeforum/index.php?topic=3915.0

Darkfrog,



Could you please post an example of the main class with different gamestates using StandardGame.



I'm making a game with an ingameState and a menuState (these are the basic States)

When I start I see my ingameState, but I can't seem to get my menu to show.



Both states extend the basicGameState.

Sure, I'll try to do that before the end of the weekend.  If you don't hear anything back about this feel free to bump me since I'm awfully good at forgetting. :o

No problem man,



I appreciate all the help i get.

If you're subclassing StandardGameState, you need to do something like this:


    protected void initCamera() {
        try {
            GameTaskQueueManager.getManager().update(new Callable<Void>() {
                public Void call() throws Exception {
                    _initCamera(); return null;
                }
            }); // you might want to call get() on this, to wait for the camera to be initialized
        } catch (Exception e) { /* this is fatal */ assert false : e; }
    }
    void _initCamera() { super.initCamera(); }



If my proposed updates to GameTaskQueueManager are accepted (see http://www.jmonkeyengine.com/jmeforum/index.php?topic=3642.60) then this could/should actually be done in StandardGame itself, making it safe to use.  Until then, you need this ugly workaround.

(darkfrog, this is another use case for my proposed functionality.)

My response to this post can be summed up with this post (that is, if my response in the linked post by cananian is not sufficient):



http://www.jmonkeyengine.com/jmeforum/index.php?topic=4136.0