Slick Forums

Discuss the Slick 2D Library
It is currently Fri May 24, 2013 7:02 pm

All times are UTC




Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: TWL and StateBasedGame
PostPosted: Sun Feb 28, 2010 3:14 am 
Offline

Joined: Sun Feb 28, 2010 2:59 am
Posts: 4
What would be the best way to integrate TWL with a StateBasedGame Slick game?

Clearly I could simply have each state maintain it's own GUI and root Widget(), but that seems odd to me...

Would it be better to setup a single GUI/root Widget() and then have each state add widgets to root as needed? Clearly each state would need to call twlWrapper.update() in it's render() method... either way seems a bit cludgy to me.

Any recommendations?


Top
 Profile  
 
 Post subject:
PostPosted: Sun Feb 28, 2010 8:08 pm 
Offline
Game Developer
User avatar

Joined: Wed Feb 17, 2010 12:24 am
Posts: 594
What I have is a GameStateGlobal. In this, I put all "global" functions that need to be ran regardless of game state (debug, twl, global inputs, etc). I call this manually (render/update) from each game state.

For example, if the user hits 'print screen', this should take a screen shot, regardless if they are in the menu or game state. For me, this seems to save a lot of hassle since I know GameStateGloabl is going to be called regardless of the "real" state.

Also this is were I setup TWL. I setup a static root window. Any place in any other game state can add a window (widget) to it since it's static and will be assured the render is going to be called.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2010 3:49 pm 
Offline
Regular
User avatar

Joined: Thu May 21, 2009 10:25 am
Posts: 161
Location: France
I create my own system using StateBasedGame and TWL. I have trouble with the new version of TWLAdapter, the old version is OK.

In both case I have a problem with the ComboBox Component. I can use it in my first state with no problem. But if I use an other ComboBox in the second state and try to click on it the game crash(look like an infinite loop). I didn't have this problem with buttons.

Any idea ? :(

The code of my abstract gamestate version:


Code:
public abstract class View extends BasicGameState {

   private static final String THEME_PATH = "resources/themes/guiTheme.xml";

   private LWJGLRenderer lwjglRenderer;
   private ThemeManager theme;
   private GUI gui;
   private TWLInputWrapper twlWrapper;
   private Widget root;

   protected GameContainer container;
   protected Game game;
   protected boolean onState;

   @Override
   public void enter(GameContainer container, StateBasedGame game) throws SlickException {
      super.enter(container, game);
      onState = true;
   }

   @Override
   public void leave(GameContainer container, StateBasedGame game) throws SlickException {
      super.leave(container, game);
      onState = false;
   }

   @Override
   public void init(GameContainer container, StateBasedGame game) throws SlickException {
      this.container = container;
      this.game = (Game) game;
   }

   @Override
   public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException {
      if (twlWrapper != null)
         twlWrapper.update();
   }

   /**
    * Developer must initialize the state resources here.
    *
    * @param container
    *            The game container associated to the game context.
    * @param game
    *            The Game context.
    */
   public abstract void initResources();

   public abstract void initTwlComponent(GameContainer container, StateBasedGame game, Widget root);

   public void initSlickAndTwlResources() {
      initResources();

      root = new Widget();
      root.setTheme("");

      initTwlComponent(container, game, root);

      // save Slick's GL state while loading the theme
      GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
      try {
         lwjglRenderer = new LWJGLRenderer();
         lwjglRenderer.setUseSWMouseCursors(true);
         theme = ThemeManager.createThemeManager(Thread.currentThread().getContextClassLoader().getResource(THEME_PATH), lwjglRenderer);
         gui = new GUI(root, lwjglRenderer);
         twlWrapper = new TWLInputWrapper(gui, container.getInput());
         container.getInput().addPrimaryListener(twlWrapper);
         gui.setSize();
         gui.applyTheme(theme);
      } catch (LWJGLException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      } finally {
         // restore Slick's GL state
         GL11.glPopAttrib();
      }
   }

   //Old version
   private class TWLInputWrapper extends InputAdapter {
      private Input input;
      private GUI gui;

      public TWLInputWrapper(GUI gui, Input input) {
         this.gui = gui;
         this.input = input;
      }

      @Override
      public void mouseWheelMoved(int i) {
         gui.handleMouseWheel(i);
         consume();
      }

      @Override
      public void mousePressed(int button, int x, int y) {
         gui.handleMouse(x, y, button, true);
         consume();
      }

      @Override
      public void mouseReleased(int button, int x, int y) {
         gui.handleMouse(x, y, button, false);
         consume();
      }

      @Override
      public void mouseMoved(int oldX, int oldY, int newX, int newY) {
         gui.handleMouse(newX, newY, -1, false);
         consume();
      }

      @Override
      public void keyPressed(int key, char c) {
         gui.handleKey(key, c, true);
         consume();
      }

      @Override
      public void keyReleased(int key, char c) {
         gui.handleKey(key, c, false);
         consume();
      }

      @Override
      public void mouseClicked(int button, int x, int y, int clickCount) {
         consume();
      }

      private void consume() {
         if (gui.getRootPane().hasOpenPopups()) {
            input.consumeEvent();
         }
      }

      public void update() {
         gui.handleKeyRepeat(); // must be called before updateTime() so that
         // it uses the same "current time" as the
         // event handlers above
         gui.setSize();
         gui.updateTime();
         gui.handleTooltips();
         gui.updateTimers();
         gui.invokeRunables();
         gui.validateLayout();
         gui.draw();
         gui.setCursor();
      }
   }

}


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2010 4:13 pm 
Offline
Slick Zombie

Joined: Fri Jan 29, 2010 7:02 pm
Posts: 1173
The old version of the wrapper has serious issues. That's why I created the new adapter and it's strongly recommended to use the new version.

_________________
TWL - The Themable Widget Library


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2010 4:16 pm 
Offline
Regular
User avatar

Joined: Thu May 21, 2009 10:25 am
Posts: 161
Location: France
Yeah I know, sorry, but If use the new version with StateBasedGame the mouse sometimes stop on screen and click event are not catch when I clicked on button (maybe I have not the last TWL version).


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2010 4:23 pm 
Offline
Slick Zombie

Joined: Fri Jan 29, 2010 7:02 pm
Posts: 1173
Maybe a good idea would be to subclass StateBasedGame and add all TWL related code there. Then if the state is changed just change the root pane of TWL. You could add an interface to states for that:
Code:
public interface HasUI {
   public Widget getRootWidget();
}
Then if the new state implements this interface use the returned widget as root, otherwise use a empty Widget. You can call gui.setRootPane for that.

The problem is that many methods in StateBasedGame are final which would make this is bit complex to implement. So you would need to override the postRenderState() method and the postUpdateState() and check if the current state has changed.

_________________
TWL - The Themable Widget Library


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2010 4:33 pm 
Offline
Regular
User avatar

Joined: Thu May 21, 2009 10:25 am
Posts: 161
Location: France
I already try a similar idea, I put all things associated to TWL in the abstract class and everything was class instance (static) excepted the widget associated to the GUI object. After what when I enter in a sbg I was doing something like View.setRootPane(root); (View is the class that extends sbg) but problems associated were the same :(.

But if you said it's the better way I will move again my code in this way (and used again the Adapter V2) and try to find why it's not working well.

Sorry for my poor English and thanks for your help


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2010 4:38 pm 
Offline
Slick Zombie

Joined: Fri Jan 29, 2010 7:02 pm
Posts: 1173
Don't use static - there is no reason for it at all. If you make a subclass of StateBasedGame then you can put all data in there.

_________________
TWL - The Themable Widget Library


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2010 4:42 pm 
Offline
Regular
User avatar

Joined: Thu May 21, 2009 10:25 am
Posts: 161
Location: France
I used static method to call this part :

Code:

      // save Slick's GL state while loading the theme
      GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
      try {
         lwjglRenderer = new LWJGLRenderer();
         lwjglRenderer.setUseSWMouseCursors(true);
         theme = ThemeManager.createThemeManager(Thread.currentThread().getContextClassLoader().getResource(THEME_PATH), lwjglRenderer);
         gui = new GUI(root, lwjglRenderer);
         twlWrapper = new TWLInputWrapper(gui, container.getInput());
         container.getInput().addPrimaryListener(twlWrapper);
         gui.setSize();
         gui.applyTheme(theme);
      } catch (LWJGLException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      } finally {
         // restore Slick's GL state
         GL11.glPopAttrib();
      }


only one time because in my version it's called with the init version (so it's called for each sbg in the game and container have a lot of Primary listener)


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2010 5:01 pm 
Offline
Regular
User avatar

Joined: Thu May 21, 2009 10:25 am
Posts: 161
Location: France
Oh sorry !!!! I never means StateBasedGame but BasicGameState !!! Sorry language mistake :?.

So know I see what you mean by said : put TWL logic in StateBasedGame (I put the logic in all BasicGameState ). I will try this with a system to know in which BasicGameState I am.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2010 10:37 pm 
Offline
Regular
User avatar

Joined: Thu May 21, 2009 10:25 am
Posts: 161
Location: France
I do it in a small package and it works :D. So I was happy but only for a short time. When I try on my own project I still have the same bug : when I clicked on button, nothing happens. After some debug test I find why.

In my game I used TWL for the menu GUI. But in game I used MouseOverArea components and it look like when using StateBasedGame + TWL + MouseOverArea the input are cut by MouseOverArea even if the object is just instantiate and not used in a state that it's not used too.

All the code (one interface and three classes (one StateBasedGame and two BasicGameState)), (need an image for MouseOverArea and a TWL Theme) :

To see the problem just comment/uncomment the MouseOverArea line in MyBasicGameState2 (method init)

Code:
public interface ITWLGameState {

   public Widget getRootWidget();
}



Code:
public class MyStateBasedGame extends StateBasedGame {

   private static final String THEME_PATH = "resources/themes/guiTheme.xml";

   private LWJGLRenderer lwjglRenderer;
   private ThemeManager theme;
   private GUI gui;
   private TWLInputAdapter twlWrapper;

   //Main
   public static void main(String[] args) {
      try {
         AppGameContainer container = new AppGameContainer(new MyStateBasedGame());
         container.setDisplayMode(800, 600, false);
         container.setTargetFrameRate(100);
         container.start();
      } catch (SlickException e) {
         e.printStackTrace();
      }
   }
   
   public MyStateBasedGame() {
      super("State Based Game with TWL");
   }

   @Override
   public void initStatesList(GameContainer container) throws SlickException {
      
      addState(new MyBasicGameState1());

      addState(new MyBasicGameState2());

      // save Slick's GL state while loading the theme
      GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
      try {
         lwjglRenderer = new LWJGLRenderer();
         lwjglRenderer.setUseSWMouseCursors(true);
         theme = ThemeManager.createThemeManager(Thread.currentThread().getContextClassLoader().getResource(THEME_PATH), lwjglRenderer);
         gui = new GUI(lwjglRenderer);
         twlWrapper = new TWLInputAdapter(gui, container.getInput());
         container.getInput().addPrimaryListener(twlWrapper);
         gui.setSize();
         gui.applyTheme(theme);
      } catch (LWJGLException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      } finally {
         // restore Slick's GL state
         GL11.glPopAttrib();
      }
   }

   // TWL

   public void updateTWL() {
      twlWrapper.update();
   }

   public void renderTWL() {
      twlWrapper.render();
   }

   public void setRootPane(int id) {
      gui.setRootPane(((ITWLGameState) getState(id)).getRootWidget());
   }

   public class TWLInputAdapter extends InputAdapter {

      private final Input input;
      private final GUI gui;

      private int mouseDown;
      private boolean ignoreMouse;
      private boolean lastPressConsumed;

      public TWLInputAdapter(GUI gui, Input input) {
         if (gui == null) {
            throw new NullPointerException("gui");
         }
         if (input == null) {
            throw new NullPointerException("input");
         }

         this.gui = gui;
         this.input = input;
      }

      @Override
      public void mouseWheelMoved(int change) {
         if (!ignoreMouse) {
            if (gui.handleMouseWheel(change)) {
               consume();
            }
         }
      }

      @Override
      public void mousePressed(int button, int x, int y) {
         if (mouseDown == 0) {
            // only the first button down counts
            lastPressConsumed = false;
         }

         mouseDown |= 1 << button;

         if (!ignoreMouse) {
            if (gui.handleMouse(x, y, button, true)) {
               consume();
               lastPressConsumed = true;
            }
         }
      }

      @Override
      public void mouseReleased(int button, int x, int y) {
         mouseDown &= ~(1 << button);

         if (!ignoreMouse) {
            if (gui.handleMouse(x, y, button, false)) {
               consume();
            }
         } else if (mouseDown == 0) {
            ignoreMouse = false;
         }
      }

      @Override
      public void mouseMoved(int oldX, int oldY, int newX, int newY) {
         if (mouseDown != 0 && !lastPressConsumed) {
            ignoreMouse = true;
            gui.clearMouseState();
         } else if (!ignoreMouse) {
            if (gui.handleMouse(newX, newY, -1, false)) {
               consume();
            }
         }
      }

      @Override
      public void mouseDragged(int oldx, int oldy, int newX, int newY) {
         mouseMoved(oldx, oldy, newX, newY);
      }

      @Override
      public void keyPressed(int key, char c) {
         if (gui.handleKey(key, c, true)) {
            consume();
         }
      }

      @Override
      public void keyReleased(int key, char c) {
         if (gui.handleKey(key, c, false)) {
            consume();
         }
      }

      @Override
      public void mouseClicked(int button, int x, int y, int clickCount) {
         if (!ignoreMouse && lastPressConsumed) {
            consume();
         }
      }

      private void consume() {
         input.consumeEvent();
      }

      @Override
      public void inputStarted() {
         gui.updateTime();
      }

      @Override
      public void inputEnded() {
         gui.handleKeyRepeat();
      }

      /**
       * Call this method from {@code BasicGame.update}
       *
       * @see BasicGame#update(org.newdawn.slick.GameContainer, int)
       */
      public void update() {
         gui.setSize();
         gui.handleTooltips();
         gui.updateTimers();
         gui.invokeRunables();
         gui.validateLayout();
         gui.setCursor();
      }

      /**
       * Call this method from {@code BasicGame.render}
       *
       * @see BasicGame#render(org.newdawn.slick.GameContainer,
       *      org.newdawn.slick.Graphics)
       */
      public void render() {
         gui.draw();
      }
   }

}



Code:
public class MyBasicGameState1 extends BasicGameState implements ITWLGameState {

   public static final int ID = 1;

   private MyStateBasedGame myGame;
   private Widget rootWidget;

   @Override
   public int getID() {
      return ID;
   }

   // Set root pane when enter
   @Override
   public void enter(GameContainer container, StateBasedGame game) throws SlickException {
      super.enter(container, game);
      myGame.setRootPane(getID());
   }

   @Override
   public void init(GameContainer container, StateBasedGame sbGame) throws SlickException {
      rootWidget = new Widget();
      myGame = (MyStateBasedGame) sbGame;

      // Add a button to test
      Button button = new Button("The button of the First state");
      button.addCallback(new Runnable() {
         @Override
         public void run() {
            System.out.println("move to state 1 to state 2");
            myGame.enterState(MyBasicGameState2.ID, new FadeOutTransition(), new FadeInTransition());
         }

      });
      button.setSize(200, 20);
      button.setPosition(200, 200);
      rootWidget.add(button);
   }

   @Override
   public void render(GameContainer container, StateBasedGame sbGame, Graphics g) throws SlickException {
      ((MyStateBasedGame) sbGame).renderTWL();
   }

   @Override
   public void update(GameContainer container, StateBasedGame sbGame, int delta) throws SlickException {
      ((MyStateBasedGame) sbGame).updateTWL();
   }

   @Override
   public Widget getRootWidget() {
      return rootWidget;
   }

}


Code:
public class MyBasicGameState2 extends BasicGameState implements ITWLGameState {

   public static final int ID = 2;

   private MyStateBasedGame myGame;
   private Widget rootWidget;

   @Override
   public int getID() {
      return ID;
   }

   // Set root pane when enter
   @Override
   public void enter(GameContainer container, StateBasedGame game) throws SlickException {
      super.enter(container, game);
      myGame.setRootPane(getID());
   }

   @Override
   public void init(GameContainer container, StateBasedGame sbGame) throws SlickException {
      rootWidget = new Widget();
      myGame = (MyStateBasedGame) sbGame;

      // Add a button to test
      Button button = new Button("The button of the Second state");
      button.setSize(200, 20);
      button.addCallback(new Runnable() {
         @Override
         public void run() {
            System.out.println("move to state 2 to state 1");
            myGame.enterState(MyBasicGameState1.ID, new FadeOutTransition(), new FadeInTransition());
         }

      });
      // Change the postion compare to the first state
      button.setPosition(200, 400);
      rootWidget.add(button);

      //THIS LINE WILL CUT EVENT !!!
      MouseOverArea test = new MouseOverArea(container, new Image("resources/images/fow.png"), 200, 200);
   }

   @Override
   public void render(GameContainer container, StateBasedGame sbGame, Graphics g) throws SlickException {
      ((MyStateBasedGame) sbGame).renderTWL();
   }

   @Override
   public void update(GameContainer container, StateBasedGame sbGame, int delta) throws SlickException {
      ((MyStateBasedGame) sbGame).updateTWL();
   }

   @Override
   public Widget getRootWidget() {
      return rootWidget;
   }

}


I need to used both TWL and MouseOverArea in my project so any help is welcome :wink:


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2010 11:07 pm 
Offline
Slick Zombie

Joined: Fri Jan 29, 2010 7:02 pm
Posts: 1173
Hmm, I don't use Slick so I have no idea why this happens - just from looking at MouseOverArea code it's not obvious. But it also uses addPrimaryListener (in the base class AbstractComponent).

Can't you just use TWL for these buttons too? Instead of mixing different UI approaches?

_________________
TWL - The Themable Widget Library


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 21, 2010 8:49 am 
Offline
Regular
User avatar

Joined: Thu May 21, 2009 10:25 am
Posts: 161
Location: France
Well I need to use the two :( . With the old version of TWLAdapter this problem didn't exist and I didn't have time to find an other solution so I will keep the old version and replace ComboBox by Editfield :?


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 21, 2010 8:27 pm 
Offline
Game Developer
User avatar

Joined: Wed Feb 17, 2010 12:24 am
Posts: 594
Vince wrote:
Well I need to use the two :( . With the old version of TWLAdapter this problem didn't exist and I didn't have time to find an other solution so I will keep the old version and replace ComboBox by Editfield :?


In TWL you can use the 'hover' property in the theme file to change an image. You can also setup 'tool tips' to display text on hover. What are you trying to do?


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 21, 2010 10:50 pm 
Offline
Regular
User avatar

Joined: Thu May 21, 2009 10:25 am
Posts: 161
Location: France
dime wrote:
Vince wrote:
Well I need to use the two :( . With the old version of TWLAdapter this problem didn't exist and I didn't have time to find an other solution so I will keep the old version and replace ComboBox by Editfield :?


In TWL you can use the 'hover' property in the theme file to change an image. You can also setup 'tool tips' to display text on hover. What are you trying to do?


The game code for the GUI in game (which used MouseOverArea) was created before using TWL and I create a system which used a complete spritesheet to make tab buttons and a lot of RTS special buttons, so the thing is that the code is too advance to go back using TWL buttons :? (but I still can do that if I don't find a better solution).

In other way I can maybe create my own MouseOverArea system (and cut the association with the container).

To show the in game GUI I have an old screenshot (1 month ago), the game use Lost Garden Sprites:

Image

I will continue to seek in the code for a quick solution. Thanks for your help :)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group