Яку техніку я повинен використовувати для полегшення спілкування між XNA GameComponents (або між компонентами будь-якого типу в грі)?


9

Я починаю свій перший «правильний» ігровий проект, і я неминуче потрапляю на блок, намагаючись вирішити, як ігрові компоненти в XNA повинні спілкуватися.

З попередніх подій програмування (Java) графічного інтерфейсу, обробники та слухачі здавалися дорогою вперед. Тож у мене був би якийсь автобус подій, який приймає реєстрації подій та класи, підписані на ці події, з обробниками, які мають справу з ними. Наприклад (псевдокод):

class SpriteManager
    Update(){
        if(player.collidesWith(enemy)
             // create new 'PlayerCollisionEvent'
    }

class HUDManager
    onPlayerCollisionEvent(){
        // Update the HUD (reduce lives etc)
    }

Однак я не впевнений у налаштуванні коду (на C #), який би знадобився для повного виконання цього завдання. Що відстежує події (якась шина?), І як вона структурована?

Також, мабуть, багато згадується про ігрові сервіси, за допомогою яких ви можете зареєструвати GameComponent в своєму головному класі Game.cs, а потім отримати його з будь-якої точки вашого коду, яка має посилання на головний об’єкт 'Game'. Я спробував це з моїм об'єктом SpriteBatch, і це здається дуже простим .. однак, я не можу бачити, щоб це було настільки гнучко, як модель події.

Візьмемо для прикладу, коли ворог гине. Ми хочемо оновити рахунок гри. Використовуючи сервіси, я можу отримати посилання на мій об’єкт StateManager, створений в Game1 та доданий як послуга, а потім встановити «оцінка» на нове значення. Я б подумав, що подія "onEnemyDeath", яку можна по-різному обробляти безліччю класів, але ініційоване 1 рядком коду у відповідному розділі "Виявлення смерті ворога", було б краще, ніж індивідуальне введення кожного необхідного GameComponent, а потім виклик будь-якого потрібні методи.

Або ці нижчі стратегії чимось іншим?

Я усвідомлюю, що це частково мої слабкі знання на C # стільки, скільки парадигми ігрового спілкування, але я дуже хотів би зрозуміти цю фундаментальну річ.

Оновлення

Переглянувши Служби більш детально, я менш переконаний - це в основному передача глобальної змінної навколо (з того, що я розумію).

Оновлення 2

Переглянувши цей основний підручник з обробки подій та тестування зразкового коду, схоже, що події будуть логічним вибором для того, що я обговорюю. Але я не можу багато чого використовувати у зразках, які я бачив. Чи є якась очевидна причина, чому цього не слід робити?


Я б рекомендував спершу спробувати FlatRedBall. Він розташований на вершині XNA і робить життя дійсно легким.
ashes999

3
Мені хотілося б по-справжньому зрозуміти основи того, що відбувається перед тим, як перейти до двигуна.
кодування руками

Я думаю, що ваш недолік тут справді нестача інформації про C #. C # використовує події для полегшення спілкування між класами, як правило. Як це зробити, XNA надає клас SpriteBatch, і більшість робіт залежить від написання. Двигун дасть вам чітке уявлення про те, як все працює на більш високому рівні, перш ніж заглиблюватися в деталі.
ashes999

1
Я погоджуюся в тій чи іншій мірі, але, оскільки я прочитав більшість міжкласових комунікацій між GameComponents можна здійснити за допомогою Сервісів. Я звик використовувати події. Я не впевнений, що найкраще, або чи є дійсні альтернативи (синглтони, статичні класи, маскуючі як глобальні, як 2 інші методи, які я опустив)
кодування

Відповіді:


4

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

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

Вам не потрібен жоден із них, щоб зробити робочу гру, але якщо це така система, якою ви зручні, вона вам також добре послужить і тут. На жаль, ніхто не може точно сказати, як їх зробити, тому що всі згортають їх трохи інакше, якщо вони взагалі їх використовують. (Я, наприклад, не.) Ви можете почати з простої System.Collections.Generic.List<Event>для черги, з різних подій, як підкласи подій, і список підписників для кожного типу подій. Для кожної події в черзі отримайте список передплатників, а для кожного абонента телефонуйте handle(this_event)за ним. Потім вийміть її з черги. Повторіть.

Також, мабуть, багато згадується про ігрові сервіси, за допомогою яких ви можете зареєструвати GameComponent в своєму головному класі Game.cs, а потім отримати його з будь-якої точки вашого коду, яка має посилання на головний об’єкт 'Game'. Я спробував це з моїм об'єктом SpriteBatch, і це здається дуже простим .. однак, я не можу бачити, щоб це було настільки гнучко, як модель події.

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

Я усвідомлюю, що це частково мої слабкі знання на C # стільки, скільки парадигми ігрового спілкування, але я дуже хотів би зрозуміти цю фундаментальну річ.

C # майже однакова мова Java. Все, що ви зробили на Java, можна зробити в C #, лише з різним синтаксисом. Якщо у вас виникли проблеми з перекладом поняття, це, мабуть, тільки тому, що ви знаєте лише ім'я, яке стосується Java.


Дякую @Kylotan З цікавості, яку систему ви самі використовуєте для міжкласового спілкування - Сервісний підхід чи щось інше?
кодування руками

AFAIK, автобуси подій схожі на речі черги повідомлень, але для подій.
ashes999

2
@codinghands, я зазвичай не використовую жодного формалізованого методу зв'язку між класами. Я просто все просто: якщо одному класу потрібно викликати інший, він зазвичай або має посилання на другий клас як його член, або другий клас передається як аргумент. Однак іноді, коли я працюю з двигуном Unity, є такий підхід, подібний до того, який ви називаєте підходом до "служб", тому що я шукаю об'єкт на ім'я, який містить потрібний мені компонент, шукаю компонент на об'єкт, а потім викликати методи цього компонента.
Килотан

@ ashes999 - Повідомлення та події майже однакове в цьому контексті. Якщо ви покладете подію на чергу, або в автобус, чи будь-що інше, те, що ви насправді зберігаєте, - це повідомлення, яке позначає, що подія відбулася. Аналогічно ставлення повідомлення до черги означає, що сталася подія для його генерування. Просто різні способи перегляду асинхронного виклику функції.
Килотан

Вибачте, я ще не позначив це як "відповів", але я подумав, що буде більше точок зору, враховуючи, що кожна гра повинна якось спілкуватися між компонентами, незалежно від мови ... Чи ми розкрили основні можливості?
кодування руками

1

Ви пробували просто використовувати прості статичні події? Наприклад, якби ви хотіли, щоб ваш клас балів знав про те, коли загинув ворог, ви зробите щось подібне:

Score.cs

class Score
{
    public int PlayerScore { get; set; }

    public Score()
    {
        EnemySpaceship.Death += new EventHandler<SpaceshipDeathEventArgs>(Spaceship_Death);
    }

    private void Spaceship_Death(object sender, SpaceshipDeathEventArgs e)
    {
        PlayerScore += e.Points;
    }
}

EnemySpaceship.cs

class EnemySpaceship
{
    public static event EventHandler<SpaceshipDeathEventArgs> Death;

    public void Kill()
    {
        OnDeath();
    }

    protected virtual void OnDeath()
    {
        if (Death != null)
        {
            Death(this, new SpaceshipDeathEventArgs(50));
        }
    }
}

SpaceshipDeathEventArgs.cs

class SpaceshipDeathEventArgs : EventArgs
{
    public int Points { get; private set; }

    internal SpaceshipDeathEventArgs(int points)
    {
        Points = points;
    }
}


0

спробуйте використовувати агрегатор подій, як цей


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