Какой метод следует использовать для облегчения связи между игровыми компонентами XNA (или между компонентами любого типа в игре)?

9

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

Из предыдущих (Java) событий программирования GUI, обработчики и слушатели выглядели как путь вперед. Таким образом, у меня была бы какая-то шина событий, которая принимает регистрации событий и классы, подписанные на эти события, с обработчиками для их обработки. Например (псевдокод):

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

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

Однако я не уверен в настройке кода (в C #), которая потребовалась бы для полного выполнения этого. Что отслеживает события (какой-то автобус?) И как оно структурировано?

Похоже, что в Game Services также много говорится о том, что вы можете зарегистрировать GameComponent в своем основном классе Game.cs, а затем извлечь его из любого места в вашем коде, который имеет ссылку на основной объект «Game». Я пробовал это с моим объектом SpriteBatch, и это кажется очень легким ... однако я не вижу такой гибкости, как у модели событий.

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

Или эти стратегии уступают чему-то другому?

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

Обновить

Посмотрев на сервисы более подробно, я менее убежден - это, в основном, передача глобальной переменной (насколько я понимаю).

Обновление 2

Изучив этот базовый учебник по обработке событий и тестированию примера кода, кажется, что события будут логичным выбором для того, что я обсуждаю. Но я не могу использовать его в примерах, которые я видел. Есть ли какая-то очевидная причина, почему нельзя?

codinghands
источник
Я бы рекомендовал вам сначала попробовать FlatRedBall. Он находится на вершине XNA и делает жизнь по-настоящему легкой.
ashes999
3
Я хотел бы действительно понять основы того, что происходит, прежде чем перейти к двигателю.
codinghands
Я думаю, что ваш провал здесь - это действительно недостаток информации о C #. C # обычно использует события для облегчения связи между классами. Вам решать, как это сделать - XNA предоставляет класс SpriteBatch, и большая часть работы зависит от вас. Двигатель даст вам четкое представление о том, как все работает на более высоком уровне, прежде чем углубляться в детали.
ashes999
1
Я согласен в некоторой степени, но из того, что я прочитал, большая часть межклассовой связи между GameComponents может быть осуществлена ​​с использованием Сервисов. Я привык к использованию событий. Я не уверен, что лучше, или если есть допустимые альтернативы (синглтоны, статические классы, маскирующиеся под глобалы как 2 других метода, которые я пропустил)
codinghands

Ответы:

4

Из предыдущих (Java) событий программирования GUI, обработчики и слушатели выглядели как путь вперед. Таким образом, у меня была бы какая-то шина событий, которая принимает регистрации событий и классы, подписанные на эти события, с обработчиками для их обработки.

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

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

Похоже, что в Game Services также много говорится о том, что вы можете зарегистрировать GameComponent в своем основном классе Game.cs, а затем извлечь его из любого места в вашем коде, который имеет ссылку на основной объект «Game». Я пробовал это с моим объектом SpriteBatch, и это кажется очень легким ... однако я не вижу такой гибкости, как у модели событий.

Возможно, он не такой гибкий, но его легче отлаживать. События и сообщения являются хитрыми, потому что они отделяют вызывающую функцию от вызываемой функции, что означает, что ваши стеки вызовов не очень полезны при отладке, промежуточные действия могут привести к тому, что что-то сломается, что обычно работает, вещи могут молча потерпеть неудачу, если не подключены должным образом , и так далее. Явный метод «захватить компонент и вызвать то, что вам нужно» работает хорошо, когда речь идет о раннем обнаружении ошибок и наличии ясного и явного кода. Это просто приводит к тесно связанному коду и множеству сложных зависимостей.

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

C # в значительной степени идентичен языку Java. Все, что вы делали в Java, может быть сделано в C #, только с другим синтаксисом. Если у вас возникли проблемы с переводом концепции, это, вероятно, просто потому, что вы знаете только специфичное для Java имя.

Kylotan
источник
Спасибо @Килотан. Из любопытства, какую систему вы сами используете для межклассовой коммуникации - сервисный подход или что-то еще?
codinghands
AFAIK, шины событий похожи на вещи очереди сообщений, но для событий.
ashes999
2
@codinghands, я обычно не использую формализованный метод взаимодействия между классами. Я просто все упрощаю - если один класс должен вызывать другой, он обычно либо уже имеет ссылку на второй класс в качестве члена, либо второй класс передается в качестве аргумента. Однако иногда, когда я работаю с движком Unity, у которого есть подход, аналогичный тому, что вы называете подходом «сервисы», в том смысле, что я искал бы объект по имени, в котором есть компонент, который мне нужен, ищите компонент на объекте, затем вызовите методы для этого компонента.
Килотан
@ ashes999 - Сообщения и события в этом контексте - одно и то же. Если вы помещаете событие в очередь, или в автобус, или что-то еще, то, что вы на самом деле храните, это сообщение, которое обозначает, что событие произошло. Аналогично, помещение сообщения в очередь означает, что произошло событие, которое сгенерировало это сообщение. Просто разные способы взглянуть на асинхронный вызов функции.
Килотан
Извините, я еще не пометил это как «отвеченный», но я подумал, что будет больше точек зрения, учитывая, что каждая игра должна как-то взаимодействовать между компонентами, независимо от языка ... Мы рассмотрели основные возможности?
codinghands
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;
    }
}
Джон Поллик
источник
1

Если вам неудобно использовать подход, основанный на событиях, попробуйте Artemis C #, это среда Entity System, основанная на http://t-machine.org/index.php/2007/09/03/entity-systems-are. -The-будущее из-MMOG-развитие-часть-1 /

Источник: https://github.com/thelinuxlich/artemis_CSharp Пример игры: https://github.com/thelinuxlich/starwarrior_CSharp

thelinuxlich
источник
0

попробуйте использовать агрегатор событий, как этот

mindabyss
источник
4
Этот ответ был бы лучше, если бы вы уточнили свое предложение, а не просто указали ссылку.
MichaelHouse