Need advise on game architecture. Removing statics.

Anything libgdx related goes here!

Need advise on game architecture. Removing statics.

Postby EasternDude » Sat Feb 03, 2018 8:23 pm

Hello everyone, I'd like someone more experienced to put me on the right track when choosing the game architecture (Android).

I used to abuse static variables to hold things such as asset manager when working on desktop projects. But now I have an android project and I've read that using statics and static asset manager in particular, can cause bugs and crashes on Android. Moreover, using static managers is said to be a sign of bad OO design, so I want to learn to properly design my code.
This the current architecture ( very simplified of course, to make it easier to understand).

Image

BaseScreen constructor takes a CustomGame parameter, so that every screen instantiated always has a reference to an instance of CustomGame.


Let look at for example how I work with assets:

i declare a static Assets variable at my CustomGame class:
Code: Select all
public class CustomGame extends Game {
//assets manager - don't make it static they say
  public static Assets assets;


But i only create an instance of Assets on CustomGame create() method, so that Assets object is not instantiated before some native libraries it depends on. As I know as soon as application is launched static suff is instantiated first, and theres no way to tell the order in which statics are initialized ( I might be wrong here).
Code: Select all
@Override
  public void create() {
    System.out.println("CustomClass :: create");
    assets = new Assets();
    assets.loadGroup("base");
    assets.finishLoading();
  }



The Assets class encapsulates AssetManager and provides extra functions such as loading and unloading a specific group of assets, etc.

Code: Select all
public class Assets implements Disposable, AssetErrorListener {

  private AssetManager manager;
  ObjectMap<String,Array<AssetDescriptor>> groups;

  public Assets(){
    System.out.println("Assets :: constructor");
    manager = new AssetManager();
    //add support for Tiled Maps
    manager.setLoader(TiledMap.class, new TmxMapLoader(new InternalFileHandleResolver()));
    manager.setErrorListener(this);
    createGroups();
  }

...



Now, suppose GameObject and the whole hierarchy of game objects know nothing about existance of Screens, CustomGame class , etc. All they do know is how to render and update themselves. But because Assets object is static in CustomGAme I can fetch required Textures, etc from CustomGame.assets.

example:
Code: Select all
public class Bullet extends GameObject {
  //bullet texture to render
  TextureRegion texture;

  public Bullet(float x, float y){
    super(x, y);
    texture = new TextureRegion(CustomGame.assets.get("img/bullet.png", Texture.class));
  }


BAM, the bullet just got its texture. This is just an example of how i used to use static Assets object. I use similar approach for all other stuff that is usefull in a lot of places in the code. For example, static instance of ScreenManager, allows me to swich between screens from anywhere in the code without passing any references aroud.
Now, if i make assets non-static all this convenience is gone. I see a couple of ways to go around this:

1. I could pass CustomGame instance all around the place. Hence, EVERY object ever created in the game could hold a CustomGame variable and access assets, screen manager through it. However, passing instance of CustomGame to every constructor in the project seems a bit crumblesome and not 'right' .

2. I could, for example, get the Texture in the context where assets are reachable (Eg, Any screen, as all screen hold a CustomGame reference) and then just pass it to the object on creation. However this seems even more crumblesome, imagine an object which requires dozen of Textures, Animaitons, etc... Moreover, in such case obejcts could ONLY be intantiated in the context where 'assets' can be obtained.

None of these seem right, am I missing some design pattern I should use?

And more, how bad are static stuff for android? Should I absolutely avoid using any static variables, methods?
For example i have another class called 'MapObjectsFactory' which likea a utility class, only contains static methods, for example:
Code: Select all
public static Array<GameObject> getObjects(TiledMap map){ .. };

This method takes a path to '.tmx' file and returns an Array of GameObject's. Is that bad to use for Android as well?
EasternDude
 
Posts: 17
Joined: Mon Oct 31, 2016 2:28 pm

Re: Need advise on game architecture. Removing statics.

Postby shatterblast » Sat Feb 03, 2018 11:00 pm

EasternDude wrote:And more, how bad are static stuff for android? Should I absolutely avoid using any static variables, methods?

I am learning Android now, but I tried to introduce a static variable into Android Studio 3.0.1. The software immediately brought up an obvious warning about how even one static variable can cause serious memory leaks.

When it comes to LibGDX stuff, I would suggest using the heck out of "instance variables", which are variables not in methods, and "encapsulation", which is the getter and setter stuff. Just from experience, it helps to set "new" stuff in the methods. However, even when not targeting Android, it seems in this forum that static variables are very much frowned upon.
shatterblast
 
Posts: 653
Joined: Sun Jul 06, 2014 1:14 pm

Re: Need advise on game architecture. Removing statics.

Postby xoppa » Sun Feb 04, 2018 5:54 pm

I looks to me that you are trying to fix the wrong problem. Why would a bullet need to know how it's drawn and stored on disk in the first place? What if you run the code on a headless device (so no textures)? Or, what if you find out that one texture per object isn't very efficient? (in which case you should use a TextureAtlas e.g.) Or, what if you want to have two bullet instances that use a different image? Or, what if your game gets bigger and you can't have everything loaded all the time? Then accessing your Assets class from practically all around your game is going to be a nightmare.

The usual design rules apply. For example, don't mix game logic and render logic (and ofcourse also resource management), prefer composition over inheritance, keep the scope as small as possible, separation of concerns, etc.

So your Bullet class could e.g. have a Sprite member which it modifies reflect its state and you provide the Sprite to the bullet in the constructor, which is gotten from the TextureAtlas.

Static methods are fine.

This is probably a good read for you: http://gameprogrammingpatterns.com/contents.html (although it's not java, it's similar enough to be easily readable), the part called singleton discusses this topic (statics) as well.
xoppa
 
Posts: 689
Joined: Thu Aug 23, 2012 11:27 pm

Re: Need advise on game architecture. Removing statics.

Postby CodeMaven » Mon Feb 05, 2018 9:44 am

One common approach to the issue of statics is "dependancy injection". There are different ways to realize this. A common method in busniness apps is something called IOC (Inversion of Control). In fact I've written an IOC container for LibGDX that we use for our games but we have not released it.

However, in it's simplest form, dependancy injection just means that you initialize all of your objects in one master start class... Basically your GameMain class just creates the 'new' instances of everything that your game will ever need (all of the classes that were static, and any classes that depends on those statics). This means you cannot lazy create your classes since you need to create them all up front, so you should have light constructors. You can still do lazy loading using an init() method later on. After you've created all of your instances just use setters to 'inject' the dependancies into where they need to go.. And finally, kick everything off... So it might look like this:

// Create Instances
assetManager = new AssetManager();
spriteBatch = new SpriteBatch();
stage = new Stage();
soundHandler = new SoundHandler();
animationControler = new AnimationController();
menuScreen = new MenuScreen();
aboutScreen = new AboutScreen();
socialDialog = new SocialDialog();
gamePlayScreen = new GamePlayScreen();

// Inject Dependancies
menuScreen.setAssetManager(assetManager);
menuScreen.setStage(stage);
menuScreen.setAboutScreen(aboutScreen);
menuScreen.setSocialDialog(socialDialog);
menuScreen.setGameSgreen(gamePlayScreen); // The play button will change to gamePlayScreen

gamePlayScreen.setMainMenu(menuScreen); // Quitting will return to main menu.
gamePlayScreen.setAssetManager(assetManager);
gamePlayScreen.setSocialDialog(socialDialog);
gamePlayScreen.setSpriteBatch(spriteBatch);
gamePlayScreen.setAnimationController(animationController);
gamePlayScreen.setSoundHandler(soundHandler);

init(); // Loadassets
setScreen(menuScreen); // Start the game...
.... etc....
// After it's all done, this class should dispose everything too.

So that gives you one clear place where everything starts from... So you should be able to understand the dependancies just by looking in one place. Note that in the above example "stage" is only used by the menu and not the gamePlayScreen... You can control what classes are used where this way and clearly see where any given class is used by looking in this one place. If it is well commented then a person should be able to look at it and understand the whole flow of the application by looking for lines like:
menuScreen.setGameSgreen(gamePlayScreen); // The play button will change to gamePlayScreen

Inversion Of Control takes this a step further and abstracts out the above code to either a configuration file or annotations... It's generally overkill for an Android game but we use our IOC container as a game engine that allows us to use a game editor to drag and drop what features (classes) we need in the applicaiton and graphically define how they will interact with each other... The game editor lets us just build the game and it just produces a configuration file which our IOC container reads and generates the entire dependancy tree from. At least that's the theory.... The graphical editor doesn't exist yet so we just write the config files by hand. It still saves us lots of time and when (if) we ever do write the graphical editor then we might actually release it.

Cheers,
Troy.
CodeMaven
 
Posts: 73
Joined: Sat Sep 13, 2014 5:24 pm

Re: Need advise on game architecture. Removing statics.

Postby EasternDude » Mon Feb 05, 2018 4:00 pm

Thanks for answers guys, really learning a thing or two here.

CodeManven, IOC is a new thing to me, definitely reading about it soon. thanks.


xoppa wrote:The usual design rules apply. For example, don't mix game logic and render logic (and of course also resource management), prefer composition over inheritance, keep the scope as small as possible, separation of concerns, etc.

So your Bullet class could e.g. have a Sprite member which it modifies reflect its state and you provide the Sprite to the bullet in the constructor, which is gotten from the TextureAtlas.

I do know some design patterns, although it not very clear to me how to use them properly.
Some things that are not clear to me at the moment:

1. You say provide Sprite in the constructor of the bullet (or any other object) instead of actually accessing the instance of Assets and fetching required stuff in the constructor.

Okay, now suppose the instance of Player class creates bullets when the button is hit. The Player instance will have to fetch required texture from the Assets anyway? Moreover, the Player has to render itself, plus it might be able to create not only bullets but spells and many other objects each requiring Sprites. Should all these assets be passed to constructor of Player? it might as well be single TextureAtlas but still... I think you see what I mean. This approach seems to be much more crumblesome.

This is the approach I'd take: I could create an ObjectsFactory class, this is the only class having access to the instance of Assets. I'll have a method for each possible object in the game, for example?:
Code: Select all
 'public Player createPlayer()'
method will fetch the assets that a player needs and pass them into the constructor of Player. But now, still, every class in the game where objects can be created needs access to the instance of ObjectFactory which in turn accesses instance Assets.
It seems to me that in this way we are only adding another layer. But not solving much, right?

2. Separation of concerns - Should my GameObject class and all subclasses have a render method?
This is what I have atm:
Code: Select all
public abstract class GameObject {
 ...
 boolean visible;
 ...
  public abstract void render(SpriteBatch batch);

  public abstract void update();
 ...
}


Hence, every object in the game overrides render(); and update(); methods. And then in my Screen i keep all objects in
Code: Select all
Array<GameObject> game_objects;


and in render, update I do this:
Code: Select all
for (GameObject game_object : game_objects) {
      if (game_object.getVisible()) {
        game_object.render(batch);
      }
    }


Is that a very bad approach? Should I go for more MVC-like pattern and completely remove 'render' from my GameObjects? and introduce some kind of Controller that knows how to render GameObjects? ( I might aswell call them GameModels then..)
EasternDude
 
Posts: 17
Joined: Mon Oct 31, 2016 2:28 pm

Re: Need advise on game architecture. Removing statics.

Postby CodeMaven » Mon Feb 05, 2018 4:59 pm

I would say your idea of a factory is a good approach. You would have to pass that factory into every class that needs to construct it. But if you keep those dependancies clear and in one place and inject them where needed then it's not that bad. When Java was designed (based on C++ and it's ilk) true Object Orient Design with deeply nested heirarchy relationships was the 'proper' popular way to do things... But we live and we learn and we move on and we now know that it has lots of drawbacks so the current reccomendation is "Composition over Inheritance"... That can be a littlebit cumbersome at times in Java because Java was designed around Inheritance... But when you get used to it you stop noticing...

Also, different architectures can help, like the Entity Component System architecture. Ashley is a greate ECS library for LibGDX. However ECS is quite a different way of working. It addresses a lot of your issues and helps on big projects but it's also a very big learning curve and being a completely different way of looking at things takes a lot to get used to... and generally for the size of most Android projects it's complexity can outweigh it's usefulness... At least when you're new to things.

We developed our first few games without the ECS architecture and then switched to it for the last few releases. I am a big fan of it now, but my advice is to learn the basics of writing a LibgGDX game without it first otherwise it will just confuse you even more. On the other hand, if you want to read up on it and if it does make logical sense to you right away then go for it.

Cheers,
Troy.
CodeMaven
 
Posts: 73
Joined: Sat Sep 13, 2014 5:24 pm

Re: Need advise on game architecture. Removing statics.

Postby xoppa » Mon Feb 05, 2018 10:02 pm

EasternDude wrote:Okay, now suppose the instance of Player class creates bullets when the button is hit.

Why would a player create bullets or know when a button is hit? Better move such game logic out of such class (independent of how you allocate resources and such). Btw, you want to hold back on creating things (using the new keyword) during gameplay on Android, because it can cause stutter caused by the GC (better use a Pool or create a number of bullets upfront).

EasternDude wrote:This is the approach I'd take: I could create an ObjectsFactory class, this is the only class having access to the instance of Assets. I'll have a method for each possible object in the game, for example?:
Code: Select all
 'public Player createPlayer()'
method will fetch the assets that a player needs and pass them into the constructor of Player. But now, still, every class in the game where objects can be created needs access to the instance of ObjectFactory which in turn accesses instance Assets.
It seems to me that in this way we are only adding another layer. But not solving much, right?

Still sounds like fixing the wrong problem. Why would every class in the game need to create things in the first place? Note that this doesnt really has to do with assets or statics.

EasternDude wrote:2. Separation of concerns - Should my GameObject class and all subclasses have a render method?

If you think of GameObject of (relatively passive) objects that don't contain much other logic and drawing them is simple, then I think it's fine that they have a render and update method. Even though that's not really MVC, it is a convenient way. But if your GameObject classes are also handling input, managing resources, more complex rendering and such, then I wouldn't recommend that.

Let's think of your GameObject class as a "game object". So just a single object within the bigger game. Such object can not and should not be aware of things not related to just that object within the game. For example, if such game object would be responsible to render itself (to the screen), then it can't do any optimizations for that rendering that would involve other game objects, like culling, multiple render passes, shadows or perhaps reusing particles, etc. That might be sufficient for one game and might not be acceptable for another game. Either way, the scope of object (just an object within the game), is well defined.

If you still think that game objects should somehow create or otherwise influence other game objects, then you could use events to notify an object higher within the hierarchy about that. But that's just one of the options as you have seen. It looks like that in your case the separation of concerns is your biggest concern, so indeed GameModels might be a good idea for you to try (btw, the controller part is not the render part).
xoppa
 
Posts: 689
Joined: Thu Aug 23, 2012 11:27 pm


Return to Libgdx

Who is online

Users browsing this forum: Google [Bot] and 1 guest