А як щодо двигуна на основі компонентів ?
У вас був би основний клас з іменем Engine, який би зберігав список GameScreens, який би сам містив список Components.
Двигун має Updateі Drawметод, і виклик GameScreen's, Updateі Drawметоди, які самі проходять кожен компонент і call 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компонент у всьому моєму проекті як послуга, щоб застосувати його перетворення під час малювання інших компонентів. Це дозволяє уникнути необхідності передавати його як параметр скрізь.
На закінчення, безумовно, можуть бути й інші кращі конструкції для двигуна, але я вважав, що запропонований цим посиланням двигун дуже елегантний, надзвичайно легко обслуговуваний та багаторазовий. Я особисто рекомендував би принаймні спробувати.