For my games, I have a single “Main” class that is responsible for getting things set up. However, besides “initialization” code, I generally have very little in there. The individual systems (networking, physics, spatial management, GUI management, user input, etc) are put into their own classes implementing AppState
, with each one dedicated to one task. I would use controls to help with animations (i.e., making sure the character is holding the right weapon, rotating the head and neck bones to look at a target, ect), but I generally don’t use those outside of that.
However, to answer your specific question, I often create non-appstate classes if I need to group a bunch of data together. For example, I have an account system similar to how Terraria or Plants vs. Zombies handles it (albiet with the possibility of splitscreen in mind). While I do have an AccountState
to manage in-game accounts and logins and such. However, considering that a single account has a lot of information like UUID, name, high scores, controller preferences, etc., I put all that information in an Account
object that I make.
Also, if I weren’t using @pspeed’s beloved Entity System and instead using OOP to manage all my in-game objects like enemies and such (
heresy!), I would generally have a class for each type of enemy (generally with a lot of interfaces and inheritance).
On the other hand, entity components are themselves objects. They are generally a lot simpler (most of mine rely a lot on Strings, primitives, UUIDs, and specialized enumes), but there are nevertheless a lot of them. The same applies to network messages (for those applications where you aren’t using RMI). In any case, I also use an individual class for every type of screen I have (although my Screen
base class is technically another app state with a bunch of extra methods that my ScreenManagerState
can work with).
Another type of class that I work a lot with is interfaces and abstract classes. For varied stuff like screens and RMI networking, I usually have a base class for the general Screen or RMI interface, as well as extended implementations for specific screens and RMI types. In the case of RMI, these themselves are actually interfaces that I implement separately on each client/server. This is where I really use the strengths of inheritance in OOP.
In short, a general rule of thumb is: if I have a bunch of data that I need to treat as a unit, or if I have a bunch of similar things with specialized implementations (GUI screens), I make a class.
What I don’t often do is make classes containing nothing but static fields and methods. They have their place, but unless I have a bunch of related fields and methods, I’ll just put them in the Application implementation.