Getting Startet...now with Gamestates

Hi guys,

I know the topic isnt specific, but i think I will use this Thread for different Questions regarding my first steps. (In case you guys dont mind)

I have to say I did read the flagrush tutorial 3-4 months ago, but then I had to do some other stuff, so i couldnt keep learning jME.

Now that I want to continue, I read these things about GameStates and StandandGame and I do want to use this way.

Currently I have some problems how to exactly use StandardGame, especially because the most other tuts, which are older, still use SimpleGame (like the Flagrushtutorial, which is great, btw :D).

I do understande the idea behind States, (maybe from the Idea a little like Slick uses them?)

Well anyway let me come to the point, where I am currenlty struggling:



1)


All the Examples with StandardGame are kinda using just one big main-Method where all the stuff happens, but I dont think its how u will usually do it, right?

My current idea would be to do something like that

public class MainClassOne {
      
   public MainClassOne(String name) {
      StandardGame game = new StandardGame(name);
      game.start();
      Class1 c1 = new Class1("test");
      GameStateManager.getInstance().attachChild(c1);
      c1.setActive(true);
   }
   
   public static void main(String[] args) {
      new MainClassOne("FirstTry");
   }



Whereas Class1 extends from PhysicsGameState

public class Class1 extends PhysicsGameState{


So I would basicly do everything that has to be done in the Class1-State within that class, like creating objects and adding them and updating them, etc.
(For the time beeing I just need one State)

Would that be right the approach?

2)
I want to use a ChaseCamera (like in the flagrushtutorials) and I would do that also in my Class1-State (which is like my IngameState where all the action happens). But there is also a Camera in Standardgame and I actually don't know how set up a chasecamera which should follow an object, which I create within my Class1-State. I dont think I should create a new Camera, because it wouldnt make sense would it?

So, how do I do it?


Hope you guys can help me out.


PS: English is not my native language, so please forgive me for any mistakes in this post and all the posts I will write 

No code in front of me, so bear with me. :slight_smile:



1.) Yes, granting that Class1 is an instance of GameState that is probably the preferred way.  When I wrote the tutorials I was going for simplicity rather than creating lots of different classes.  In a full-scale project you'll likely want to separate things out into GameStates rather than in one massive initialization method that builds everything.



2.) I believe StandardGame has a getCamera() method?  If not, you may have to do something like DisplaySystem.getDisplaySystem.getCamera() or something along those lines.

darkfrog said:

2.) I believe StandardGame has a getCamera() method?  If not, you may have to do something like DisplaySystem.getDisplaySystem.getCamera() or something along those lines.

I did not found anything like "DisplaySystem.getDisplaySystem.getCamera()" :(.
But I used the StandardGame.getCamera() to get the camera, though I had to give a reference to the Construktor of the Class1-State, because otherwise I couldnt get to the game Objects, if I wouldnt make it static.

      Class1 c1 = new Class1("test", game.getCamera());


If you ask me I dont like to get all the necesarry stuff that way, but I guess it be done otherwise, right?

its actually displaysystem.getdisplaysystem.getrenderer.getcamera()

but the standardgame.getcamera gives u the same thing ha~  :smiley:



well, game is just the main game loop. game states are supposed to be seperated from the main game loop.

standardgame does alot of things for u, so u dont have to do inisystem and stuffs like that. but u do need to use gametaskqueuemanager to insert ur gamestates.

neakor said:
but u do need to use gametaskqueuemanager to insert ur gamestates.


Only if you're doing something that MUST be done in the OpenGL thread.  Most scenegraph work can be done without GTQ.

draGy said:

If you ask me I dont like to get all the necesarry stuff that way, but I guess it be done otherwise, right?


You could always make a static reference to StandardGame in your game and have a getStandardGame() method that returns the instance if you don't want to pass around references.

I recently started looking into GameState and StandardGame as well, and was curious as to if most people simply use StandardGame, or use parts of it that they need.



From what I currently have (might be a bit outdated version of jME, but not by much), I have issues with audio (some OpenAL exception) that I didn't see when using the audio tests I created.  I didn't fully look into the issue yet, but my plan after that and seeing the options menu was to create a custom StandardGame implementation.



Any thoughts/comments/beer?

Yes I have thoughts. ;)  If you have to create your own custom implementation of StandardGame it means there's something wrong with StandardGame.  The whole purpose of StandardGame is to be able to instantiate it and then do everything else through GameStates.  Yes you can accomplish your task most likely by subclassing or creating your own custom version of the class, but if you would instead figure out what it is not doing that you need help to fix it so it WILL work for your needs.



I wrote StandardGame, and the intent was to handle any game scenario necessary.  However, (and I know this will be a shock to you all) I'm not perfect and there will be things that it either does wrong or doesn't do that it should.  Help to make StandardGame better and then you aren't just helping yourself, but the community as well. :wink:

Yeah, the issue I ran into was that it uses openAL, and I was using the com.jmex.audio package with Track and such.

Perhaps Audio should either be removed from StandardGame entirely or there should be a configuration option to set how and what it loads at init.

For my testing right now (porting over my stuff from SimpleGame), I currently just removed audio from it altogether…I don't know which would be more efficient, especially since right now I'm fighting with game settings…



Seems like the current GameSettingsPanel doesn't quite work with me, as it has none of the supported display modes for my system…heh (16 bit or 32 bit…I need 24bit with a few funky frequencies, apparently).  I guess I could just go ahead and create something specifically for my app that will call  Display.getAvailableDisplayModes()  and provide choices that take that into account…but I wonder if I can use what was previously used.



Why are the settings done so differently than in SImpleGame?  Seemed much…simpler…to me, and I'm seeing modes that I'd never have expected, instead of what I've been using.

Honestly, that was sort of hacked together with the intent to go back later and revise it.  If you'd like to modify the code to work more like SimpleGame the contribution would be appreciated.  I don't remember what it was, but I ran into some strange problems when I was originally trying to replicate the auto-detection features that SimpleGame has.



Drop a patch or the changes here and I'll apply them for you.

Will work on it later tonight…in the meantime I have a noobish question regarding the whole StandardGame deal:



Currently, I have a quick hackish port to StandardGame, and when I get it running, I get a NullPointerException dealing with org.lwjgl.opengl.GL11.glHint when I try to apply the TextureState on my models.



Does the following quote still apply?  If not, can you point me to the wiki page?  I'm just curious as to if I have to keep doing things until I see no more NullPointerExceptions XD

We definitely need to evaluate how to better inform developers about what has to be done in the OpenGL thread, it just hasn't been done yet.


Thanks for responding so much to my babbling, by the way...it really helps.  I'd be more responsive myself, but I don't get much time during the week to work on this, due to work.

I guess I should also apologize for hijacking this thread...

Yes, it's most likely due to not executing the code in the OpenGL thread.  Anything in TextureStates seem to have to be run in the OpenGL thread because it's doing things directly to the graphics memory.

Here's the patch (hope I generated it in the proper format) for what I've done so far.  You may notice that I didn't deal with the possible exceptions other than to just ignore them and move on.  You may also notice that I still only use a specific set of resolutions: it would be easy to grab all the possible resolutions and display them instead, but from a gamer's perspective, I'd rather not have the option of viewing in a bunch of weird resolutions.  I had the code working by the time I posted last for getting the proper modes and such, but hadn't integrated it with the panel code until now…been busy with skybox issues.



This is VERY useful for me, especially since in Mepis right now using the Nvidia drivers I have weird frequencies displayed on the user end (like 50,53, etc), while it's actually using the proper ones (75, 60, respectively) on the backend.  Can tell this easily by using vsync and checking the fps.


Index: GameSettingsPanel.java
===================================================================
RCS file: /cvs/jme/src/com/jmex/editors/swing/settings/GameSettingsPanel.java,v
retrieving revision 1.7
diff -u -r1.7 GameSettingsPanel.java
--- GameSettingsPanel.java   25 Feb 2007 22:15:18 -0000   1.7
+++ GameSettingsPanel.java   15 Jul 2007 07:56:09 -0000
@@ -40,6 +40,8 @@
 import java.awt.Insets;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 import java.util.*;
 
 import javax.swing.JButton;
@@ -49,6 +51,9 @@
 import javax.swing.JPanel;
 import javax.swing.SwingConstants;
 
+import org.lwjgl.opengl.Display;
+import org.lwjgl.opengl.DisplayMode;
+
 import com.jme.system.GameSettings;
 
 /**
@@ -56,8 +61,12 @@
  */
 public class GameSettingsPanel extends JPanel {
    private static final long serialVersionUID = 1L;
+   public static final int[][] RESOLUTIONS = { {640,480}, {800,600}, {1024,768}, {1280,1024},
+      {1600,1200}, {1440, 900} };
+   public static final int[] DEPTHS = {16, 24, 32};
 
    private GameSettings settings;
+   private DisplayMode[] allModes;
    
    private GridBagLayout layout;
    private GridBagConstraints constraints;
@@ -80,6 +89,11 @@
    
    public GameSettingsPanel(GameSettings settings) {
       this.settings = settings;
+      
+      try{
+         allModes = Display.getAvailableDisplayModes();
+      }catch(Exception e){}
+      
       map = new HashMap<String, JComboBox>();
       defaults = new HashMap<String, Object>();
       init();
@@ -161,21 +175,22 @@
    }
    
    protected Component createResolution() {
-      resolution = new JComboBox(new Object[] {
-                  "640x480",
-                  "800x600",
-                  "1024x768",
-                  "1280x1024",
-                  "1600x1200",
-                  "1440x900"});
+      resolution = new JComboBox(getResolutionArray());
       resolution.setName("Resolution");
+      
+      ItemListener itemListener = new ItemListener() {
+         public void itemStateChanged(ItemEvent event) {
+            // The resolution combobox is all we care about
+            String[] parser = ((String)resolution.getSelectedItem()).split("x");
+            setMenuOptions(Integer.parseInt(parser[0]), Integer.parseInt(parser[1]));
+         }
+      };
+      resolution.addItemListener(itemListener);
       return resolution;
    }
    
    protected Component createDepth() {
-      depth = new JComboBox(new Object[] {
-                  "16",
-                  "32"});
+      depth = new JComboBox(getDepthArray());
       depth.setName("Depth");
       return depth;
    }
@@ -343,4 +358,83 @@
          Thread.sleep(50);
       }
    }
+   
+   /**
+    * Sets the other menu options based on the given width and height parameters.
+    *
+    * @param width
+    * @param height
+    */
+   public void setMenuOptions(int width, int height)
+   {
+      Vector<DisplayMode> availableModes = getAvailableModesRes(allModes, width, height);
+      depth.removeAllItems();
+      frequency.removeAllItems();
+      HashSet<String> depths = new HashSet<String>();
+      HashSet<String> frequencies = new HashSet<String>();
+      
+      for(DisplayMode aMode : availableModes)
+      {
+         depths.add(String.valueOf(aMode.getBitsPerPixel()));
+         frequencies.add(String.valueOf(aMode.getFrequency()));
+      }
+      for(String oneDepth : depths)
+      {
+         depth.addItem(oneDepth);
+      }
+      for(String oneFreq : frequencies)
+      {
+         frequency.addItem(oneFreq);
+      }
+      depth.updateUI();
+      frequency.updateUI();
+   }
+   
+   /**
+    * Gets the available modes based on the set of modes for the system and a resolution.
+    *
+    * @param theModes
+    * @param width
+    * @param height
+    * @return
+    */
+   public static Vector<DisplayMode> getAvailableModesRes(DisplayMode[] theModes, int width, int height)
+   {
+      Vector<DisplayMode> modes = new Vector<DisplayMode>();
+      
+      for(int[] res : RESOLUTIONS)
+      {
+         if(res[0] == width && res[1] == height)
+         {
+            for(DisplayMode aMode : theModes)
+            {
+               if(aMode.getHeight() == height && aMode.getWidth() == width)
+               {
+                  modes.add(aMode);
+               }
+            }
+         }
+      }
+      return modes;
+   }
+   
+   public static Object[] getResolutionArray()
+   {
+      Object[] resolutions = new Object[RESOLUTIONS.length];
+      for(int i=0; i<resolutions.length; i++)
+      {
+         resolutions[i] = RESOLUTIONS[i][0]+"x"+RESOLUTIONS[i][1];
+      }
+      return resolutions;
+   }
+   
+   public static Object[] getDepthArray()
+   {
+      Object[] depths = new Object[DEPTHS.length];
+      for(int i=0; i<depths.length; i++)
+      {
+         depths[i] = String.valueOf(depths[i]);
+      }
+      return depths;
+   }
 }
No newline at end of file

Now that that's out of the way…I need to figure out how to fix my skybox:



  • Still have the same problem I've always had that's been discussed in another thread - skybox "popping" when camera is moved (not updating at the right time or something).

  • Got a new problem when adding the skybox into its own GameState: instead of loading the proper texture like it's always done, it's now loading the font map texture for whatever reason!  I was pretty amused at first, but now it's just annoying, since I didn't change the actual texture loading code.



Anyway, any help would be appreciated...need to move forward with GameStates.  I keep trying to get to a point where I'm making a playable demo, but things keep getting in the way...

I'll try to get that patch in ASAP.



The first problem I'm not sure why it's happening.  I can try to post some example code of how to use Skyboxes and see if there's some differences from your code.



Have you tried calling updateRenderState on the root node after you've applied all your textures and such (if you're issuing calls to GameTaskQueue make sure you call get() so that it blocks until they complete)?

Yeah…stupid me…I moved the code over, but forgot to move over the renderState update…heh.  I guess messing with this at 3am isn't always such a good idea.  So, that fixed the texture issue, but still have the problem with the "popping."  In the other thread it was noted to deal with the input not being processed at the right time in regards to moving the skybox (I'm using an extended DebugGameState as my main state, and then my "SkyboxGameState").



I also just realized as I woke up and hobbled over to the computer: the code I submitted could be more efficient by not parsing the resolution like I mindlessly copied, but instead getting the selected index and grabbing the associated index in the RESOLUTIONS array.  Oh well…not something I would assume people would be using in production anyway.

Okay, this has finally been patched to the repository.