Как насчет компонентного движка ?
У вас будет основной класс с именем 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
классе (вы просто храните список IService
s и позволяете другим классам добавлять службы самостоятельно. ). Например, я бы оставил Camera2D
компонент над всем моим проектом как сервис, чтобы применить его преобразование при рисовании других компонентов. Это избавляет от необходимости передавать его как параметр везде.
В заключение, конечно, могут быть и другие лучшие конструкции для двигателя, но я нашел двигатель, предложенный по этой ссылке, очень элегантным, чрезвычайно легко обслуживаемым и многоразовым. Я лично рекомендовал бы по крайней мере попробовать это.