Как насчет компонентного движка ?
У вас будет основной класс с именем Engine, который будет хранить список GameScreens, который будет содержать список Components.
Двигатель имеет Updateи Drawметод , и как вызов на GameScreen«S Updateи Drawметоды, которые сами по себе пройти через каждый компонент и вызов Updateи Draw.
Представлено так, я согласен, что это звучит как плохой и повторяющийся дизайн. Но поверьте мне, мой код стал намного чище благодаря использованию компонентного подхода, чем со всеми моими старыми классами менеджера .
Также гораздо проще поддерживать такой код, поскольку вы просто проходите большую иерархию классов и вам не нужно искать BackgroundManagerвсе специфические фоны. Вы просто есть ScrollingBackground, ParallaxBackground, StaticBackgroundи т.д. , которые все вытекают из Backgroundкласса.
В конечном итоге вы создадите довольно солидный движок, который вы сможете повторно использовать во всех своих проектах со множеством часто используемых компонентов и вспомогательных методов (например, FrameRateDisplayerв качестве утилиты отладки, Spriteкласса в качестве базового спрайта с текстурой и методов расширения для векторов и генерация случайных чисел).
У вас больше не будет BackgroundManagerкласса, но есть Backgroundкласс, который будет управлять собой.
Когда ваша игра начинается, все, что вам нужно сделать, это в основном:
// 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();
И это все для кода запуска вашей игры.
Затем для экрана главного меню:
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);
}
}
}
Вы получите общую идею.
Вы также должны сохранить ссылку на Engineвсе ваши GameScreenклассы, чтобы иметь возможность добавлять новые экраны даже внутри GameScreenкласса (например, когда пользователь нажимает кнопку StartGame , находясь внутри вашего MainMenuScreen, вы можете перейти к GameplayScreen).
То же самое относится и к Componentклассу: он должен содержать ссылку на своего родителя GameScreen, чтобы иметь доступ как к Engineклассу, так и к его родителю GameScreenдля добавления новых компонентов (например, вы можете создать класс, связанный с HUD, DrawableButtonкоторый
называется DrawableTextкомпонентом и StaticBackgroundкомпонентом).
После этого вы даже можете применять другие шаблоны проектирования, например, «шаблон проектирования служб» (не уверен в точном названии), где вы можете хранить различные полезные службы в своем Engineклассе (вы просто храните список IServices и позволяете другим классам добавлять службы самостоятельно. ). Например, я бы оставил Camera2Dкомпонент над всем моим проектом как сервис, чтобы применить его преобразование при рисовании других компонентов. Это избавляет от необходимости передавать его как параметр везде.
В заключение, конечно, могут быть и другие лучшие конструкции для двигателя, но я нашел двигатель, предложенный по этой ссылке, очень элегантным, чрезвычайно легко обслуживаемым и многоразовым. Я лично рекомендовал бы по крайней мере попробовать это.