Java-Gaming.org Hi !
Featured games (91)
games approved by the League of Dukes
Games in Showcase (762)
Games in Android Showcase (229)
games submitted by our members
Games in WIP (847)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Using a switch statement to determine state of game  (Read 5941 times)
0 Members and 1 Guest are viewing this topic.
Offline Spontaneous

Junior Newbie





« Posted 2016-04-08 21:37:16 »

I'm making a tower defense game using libgdx and scene2d. For my UI during gameplay, I implemented a State Pattern (or at least a variant) along with MVP and Observer pattern.
To do this, I have a view, presenter, and uiStateManager class. The uiStateManager is referenced by various presenters and services to alter that state of the UI. For instance, when placing a tower on the map, the HUD should disable buttons, hide certain widgets, etc.

The following is my uiStateManager class:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
public class GameUIStateManager {
   
   private GameUIState state;
   private CopyOnWriteArrayList<IGameUIStateObserver> observers = new CopyOnWriteArrayList<IGameUIStateObserver>();
   public GameUIStateManager(){
      this.setState(GameUIState.STANDBY); //init in standby
   }
   public void attach(IGameUIStateObserver observer){
      observers.add(observer);
   }
   public void notifyObservers(){
      for(IGameUIStateObserver observer : observers){
         observer.changeUIState(state);
      }
   }
   public void setState(GameUIState state){
      this.state = state;
      notifyObservers();
   }
   public GameUIState getState(){
      return state;
   }
   
   public enum GameUIState {
       INSPECTING,
       OPTIONS,
       STANDBY,
       GAME_OVER,
       PLACING_TOWER;
       
   }
}


So the UIStateManager holds a list of IGameUIStateObservers which looks like:
1  
2  
3  
public interface IGameUIStateObserver {
   public void changeUIState(GameUIState state);
}

The State Manager notifies all of its observers whenever it changes states.
So for instance, my HUD Presenter implements IGameUIStateObserver and attaches itself to the uiGameStateManager object.
When it is notified that the uiStateManager has changed states, it uses a switch to determine how the HUD View should update itself.

Method inside HUD Presenter:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
   @Override
   public void changeUIState(GameUIState state) {
      switch(state){
      case GAME_OVER:
         view.gameOverState();
         break;
      case OPTIONS:
         view.optionsState();
         break;
      case STANDBY:
         view.standByState();
         break;
      default:
         break;
      }
   }


So for example when the StateManager changes to GAME_OVER, it notifies my HUD Presenter which calls the gameOverState method on the HUD View. The HUD view then updates itself to disable buttons.

Method in HUD View:
1  
2  
3  
4  
5  
6  
   public void gameOverState(){
      btnEnlist.setTouchable(Touchable.disabled);
      btnWave.setTouchable(Touchable.disabled);
      btnSpeedGroup.setTouchable(Touchable.disabled);
      btnOptions.setTouchable(Touchable.disabled);
   }


The GameOver Presenter is also notified to render itself and show the GameOver overlay which shows various buttons and other widgets. However, the game is still in the background with the disabled buttons.

Method in GameOver Presenter:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
   public void changeUIState(GameUIState state) {
      switch(state){
      case GAME_OVER:
         view.gameOverState();
         setWavesCompleted();
         break;
      default:
         view.standByState();
         break;
      }      
   }

   public void setWavesCompleted() {
      view.setWavesCompleted(String.valueOf(player.getWavesCompleted()));
   }


Method in GameOverView:
1  
2  
3  
4  
5  
6  
7  
8  
   @Override
   public void gameOverState(){
      this.setVisible(true);
   }
   @Override
   public void setWavesCompleted(String wavesCompleted){
      lblWavesCompletedCount.setText(wavesCompleted);
   }


So the GameOverView also updates itself, although it isn't as complex as the HUD.

I know switch statements are a code smell, and I've been trying to come up with a way to improve on this design. However, nothing seems to be as flexible and easy as this. I tried to come up with a polymorphic way to handle this with actual concrete state classes, but the issue is that the views update themselves. I could do away with the whole MVP pattern and just have State classes and Views. Then pass in as a parameter the various views to the State Class. So for example, a state class called GameOver would have references to the HUD View and GameOverView. It would then call hudView.gameOverState() and gameOverView.gameOverState() on the views themselves, but I'm not sure if that helps at all.

How can I refactor my design pattern? I like what I have now, but I would like to learn other alternatives and if possible, improvements.

Thanks!
Offline KaiHH

JGO Kernel


Medals: 521



« Reply #1 - Posted 2016-04-08 22:36:44 »

Usually, when I don't know how to design something or have some trouble with it, the first thing I do is to get rid of all design patterns and try to come up with the most straightforward and unobstrusive solution possible. Which in many cases happens to be the best solution in the end.
Point being: Don't introduce design patterns and then try to force your application into them, but design your solution straightforward (concrete methods calling concrete other methods in concrete classes and not via dynamical dependencies (aka. observer)).
Design patterns always come at a cost and when time comes you have to weigh out whether introducing them actually makes anything easier. And in the past I found that they really hardly make things easier.
This is not meant as a rant on design patterns, but just to make sure one uses them when needed.
When we want flexibility it is usually not because we know we need it but because we don't know whether we don't need it. And that is because we don't know what our application actually does (i.e. which concrete state changes exist and via which conrete methods they are being triggered).
So maybe draw a state diagram first and then implement that straightforward via method calls.
Probably you realize that you just end up with a single class with concrete methods for each concrete state. Who knows? But simpler is always better. Smiley

Also: Who told you that using switch statements on enums is a code smell?
Offline Spontaneous

Junior Newbie





« Reply #2 - Posted 2016-04-08 23:01:06 »

Thanks for the reply.

I agree in that sometimes it's best to not worry about a design pattern. It's what I originally did. However, my goal in making my game was mostly to become a better programmer. Therefore, I tried to implement different design patterns to learn more and further improve my code. I probably went a bit overboard, but I definitely learned a lot!

The one thing I couldn't help but notice was the amount of circular dependency, specifically with the framework I'm using (libgdx w/ scene2d). I often found my self in places where I had no option but to have some circular dependency. I felt like most of the examples I was following or repositories I was viewing also suffered from this and didn't really show a way to correct it. They too often had parent classes passing references to itself to classes that they owned (specifically the Game and Stage classes in libgdx/scene2d). Therefore, in an effort to improve my code, I decided to implement my State Manager classes. That way different classes could subscribe to a specific state manager and receive notifications and react accordingly. The state manager/states(enums) have almost zero knowledge. It's up to the observers to decide what to do. Sometimes they update themselves, other times they just ignore the state because it has nothing to do with them so they go/stay in standby. I still think there are a few issues with that idea, but it was a vast improvement of what I had before. I felt that it was best to still describe them as StateManagers, but perhaps it's isn't much of a state pattern.

My understanding with switch statements was that they are often the cause of poor design and can be replaced with polymorphism. I tried to do that, but I didn't really see much of an improvement. Perhaps the fact that I'm using an enum makes it better?

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline ags1

JGO Kernel


Medals: 367
Projects: 7


Make code not war!


« Reply #3 - Posted 2016-04-08 23:04:14 »

Maybe it's late over here, but I found it difficult to read your first post. I think part of my brain switches off when it encounters texts with a high degree of references to design patterns! At least I find it more helpful to proceed on the basis of building something that works and then improving it. Refactoring tools are powerful these days and I prefer not to invest in upfront design.

Offline KaiHH

JGO Kernel


Medals: 521



« Reply #4 - Posted 2016-04-08 23:28:49 »

Quote
Therefore, I tried to implement different design patterns to [...] further improve my code.
Quote
Therefore, in an effort to improve my code, I [...]
Quote
I tried to do that, but I didn't really see much of an improvement.
The question in all three quoted cases above is: on what basis do we evaluate which design decision is better or worse? Feeling will not help in most cases when the domain is probably not known well.
If you are maybe a Java EE/Spring/Web programmer you are probably used to design patterns applicable in web applications. That may not be the case with games.
I agree with ags1 that in your problem description you rushed in with design patterns and left interesting aspects about the actual game design (the business logic if you will) unmentioned.
The only thing that is known so far is that it is a tower defense game. And then there are some technicalities known, such as that you use libGDX.
That's why I feared that design-pattern-envy may be the problem in your case. Smiley

So also like ags1 said: Don't concern yourself with big upfront design and whether some decision will hold under the test of time, because we usually don't know that upfront.
Keep the design simple, so that it can always be refactored easily.
Pages: [1]
  ignore  |  Print  
 
 

 
EgonOlsen (364 views)
2018-06-10 19:43:48

EgonOlsen (364 views)
2018-06-10 19:43:44

EgonOlsen (304 views)
2018-06-10 19:43:20

DesertCoockie (540 views)
2018-05-13 18:23:11

nelsongames (869 views)
2018-04-24 18:15:36

nelsongames (852 views)
2018-04-24 18:14:32

ivj94 (1303 views)
2018-03-24 14:47:39

ivj94 (424 views)
2018-03-24 14:46:31

ivj94 (1087 views)
2018-03-24 14:43:53

Solater (442 views)
2018-03-17 05:04:08
Java Gaming Resources
by philfrei
2017-12-05 19:38:37

Java Gaming Resources
by philfrei
2017-12-05 19:37:39

Java Gaming Resources
by philfrei
2017-12-05 19:36:10

Java Gaming Resources
by philfrei
2017-12-05 19:33:10

List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!