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 IServices 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.