Я хочу позбутися моєї моделі дизайну "все-все-статична-і-глобальна", але як?


11

Я роблю невеликий гусеничний підйомник у просторі, і я хотів би почути поради, як зробити гарнітуру двигуна приємнішою. В основному, зараз все базується на навантаженні менеджерів:

  • BackgroundManager: має AddBackground(image, parallax)метод створення крутих фонових ефектів.
  • ConfigManager: читає / робить конфігураційний файл, а також містить дані, прочитані з цього конфігураційного файла.
  • DrawManager: має Draw(sprite)метод малювати речі на екрані, і такі речі, як SetView(view)і GetView()т.д.
  • EntityManager: містить усі активні об'єкти та має методи додавання, видалення та пошуку сутностей.
  • DungeonManager ( на насправді називається GridManager, але це для простоти): є такі методи , як GenerateDungeon(), PlaceEnemies(), і PlaceFinish()т.д.

Зараз я навіть не перебуваю на півдорозі, перелічуючи всіх своїх менеджерів, тому я думаю, що це має змінитися. Моя гра також заснована на екранах, що робить її більше дратує, тому що мені не потрібна половина речей, якими керують мене менеджери, наприклад, головне меню (мені не потрібно фізика / сутності / підземелля в моєму головному меню !)

Тепер я подумав про те, щоб зробити менеджерів не статичними, і надати моєму ScreenMainGame примірник всіх ігрових менеджерів. Але це робить дзвінки / отримання чого-небудь, пов’язаного з менеджерами, великим безладом ... наприклад, якби я хотів намалювати свою підземелля з будь-якого місця, в якому не знаходиться ScreenMainGame.Draw(), я повинен був би це зробити ...

((ScreenMainGame)ScreenManager.CurrentScreen).GridManager.Draw()

Це справді потворно.

Отож, товариші розробників ігор, чи має хтось із вас ідею, як вирішити цей безлад? Буду вдячний!


Я не впевнений, як саме вам допомогти, але рекомендую прочитати хорошу книгу про дизайнерські зразки. Я читав шаблони дизайну Head First, і це дуже легко зрозуміти. Приклади є на Java, але я думаю, це було б досить близько до C #, щоб допомогти вам. Вибачте, якщо моя пропозиція занадто послушна.
Amplify91

Що ви використовуєте для взаємодії з відеокартою? XNA? SlimDX?
BlueRaja - Danny Pflughoeft

@BlueRaja: Я використовую SFML.NET.
Dlaor

У такому випадку вам слід просто використовувати звичайні методи вирішення цих проблем на C #: Дивіться моє останнє посилання нижче, а також Що таке ін'єкція залежності?
BlueRaja - Danny Pflughoeft

Відповіді:


10

А як щодо двигуна на основі компонентів ?

У вас був би основний клас з іменем 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компонент у всьому моєму проекті як послуга, щоб застосувати його перетворення під час малювання інших компонентів. Це дозволяє уникнути необхідності передавати його як параметр скрізь.

На закінчення, безумовно, можуть бути й інші кращі конструкції для двигуна, але я вважав, що запропонований цим посиланням двигун дуже елегантний, надзвичайно легко обслуговуваний та багаторазовий. Я особисто рекомендував би принаймні спробувати.


1
XNA підтримує все це нестандартно через GameComponents та сервіси. Немає потреби в окремому Engineкласі.
BlueRaja - Danny Pflughoeft

+1 за цю відповідь. Також, чи має сенс розмістити малюнок окремим потоком від потоку оновлення гри?
f20k

1
@BlueRaja - DannyPflughoeft: Дійсно, але я вважав, що XNA не використовується, тому я пояснив трохи більше про те, як це зробити.
Джессі Емонд

@ f20k: Так, майже так само, як це робить XNA. Я не знаю, як це реалізується. = / Десь в Інтернеті повинна бути стаття про те, як це зробити. :)
Джессі Емонд

Так, я не використовую XNA, але це здається приємною альтернативою тому, що я роблю. Зараз я читаю книгу "Шаблони першого дизайну", щоб побачити, чи є ще альтернативи. Ура!
Dlaor

1

Що ви хочете зробити, це розділити ваш код на компоненти та служби. XNA вже має вбудовану підтримку для них.

Дивіться цю статтю про GameComponentsта послуги. Потім перегляньте цю відповідь про використання сервісів у XNA, і цю більш загальну відповідь про послуги будь-якою мовою.


-3

Тільки тому, що вони статичні або глобальні, не означає, що їх потрібно завжди завантажувати / ініціалізувати. Використовуйте шаблон Singleton і дозвольте менеджерам бути безкоштовними та завантажуватися знову на вимогу.


4
Синглтон - це лише фасад основної проблеми, яку ОП намагається вирішити тут.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.