What about a component-based engine?
You would have a main class named Engine
, which would keep a list of GameScreens
, which would themselves hold a list of Components
.
The engine has an Update
and a Draw
method and both call the GameScreen
's Update
and Draw
methods, which themselves go through every component and call Update
and Draw
.
Presented like that, I agree that it sounds like a poor and repetitive design. But believe me, my code became much cleaner by using a component-based approach than it did with all my old manager classes.
It's much simpler to maintain such code too, since you're just going through a big class hierarchy and not having to search through BackgroundManager
for all the specific different backgrounds. You just have a ScrollingBackground
, ParallaxBackground
, StaticBackground
, etc. which all derive from a Background
class.
You'll eventually build up a pretty solid engine that you can reuse over all your projects with a lot of frequently used components and helper methods (e.g. a FrameRateDisplayer
as a debugging utility, a Sprite
class as a basic sprite with a texture and extension methods for vectors and random number generation).
You would no longer have a BackgroundManager
class, but a Background
class which would manage itself instead.
When your game starts, all you have to do is this basically:
// when declaring variables:
Engine engine;
// when initializing:
engine = new Engine();
engine.Initialize();
engine.LoadContent();
engine.AddGameScreen(new MainMenuScreen());
// when updating:
engine.Update();
// when drawing:
engine.Draw();
And that's it for your game start code.
Then, for the main menu screen:
class MainMenuScreen : MenuScreen // where MenuScreen derives from the GameScreen class
{
const int ENEMY_COUNT = 10;
StaticBackground background;
Player player;
List<Enemy> enemies;
public override void Initialize()
{
background = new StaticBackground();
player = new Player();
enemies = new List<Enemy>();
base.AddComponent(background); // defined within the GameScreen class
base.AddComponent(player);
for (int i = 0; i < ENEMY_COUNT; ++i)
{
Enemy newEnemy = new Enemy();
enemies.Add(newEnemy);
base.AddComponent(newEnemy);
}
}
}
You get the general idea.
You would also keep the reference of the Engine
within all your GameScreen
classes, to be able to add new screens even within a GameScreen
class (e.g. when the user clicks on the StartGame button while within your MainMenuScreen
, you can transition to the GameplayScreen
).
The same goes for the Component
class: it should hold the reference of its parent GameScreen
, to have both access to the Engine
class and its parent GameScreen
to add new components (e.g. you can make a HUD-related class called DrawableButton
which holds a
DrawableText
component and a StaticBackground
component).
You can even apply other design patterns after that, like the "service design pattern"(not sure about the exact name) where you can keep different useful services within your Engine
class(you simply keep a list of IService
s and let other classes add services themselves). e.g. I would keep a Camera2D
component over all my project as a service to apply its transformation when drawing other components. This avoids having to pass it as a parameter everywhere.
In conclusion, there certainly may be other better designs for an engine, but I found the engine proposed by this link very elegant, extremely easily maintainable and reusable. I would personally recommend at least trying it.