Последние 3-4 года я работал над некоторыми хобби-проектами. Просто простые 2d и 3d игры. Но в последнее время я начал больший проект. Так, в последние пару месяцев я пытался создать класс игровых объектов, который может стать основой всех моих игровых объектов. Поэтому после долгих испытаний и испытаний я обратился к Google, который быстро указал мне на некоторые PDF-файлы GDC и PowerPoints. И теперь я пытаюсь понять, какие игровые объекты основаны на компонентах.
Я понимаю, что движок создает игровой объект, а затем присоединяет различные компоненты, которые обрабатывают такие вещи, как здоровье, физика, сети и все, что вы заставляете их делать. Но я не понимаю, как компонент X знает, изменил ли Y состояние объекта. Например, как PhysicsComponent узнает, жив ли игрок, потому что здоровье контролируется HealthComponent ..? И как HealthComponent играет "анимацию игрока-умершего"?
У меня сложилось впечатление, что это было что-то вроде этого (в HealthComponent):
if(Health < 0) {
AnimationComponent.PlayAnimation("played-died-animation")
}
Но опять же, как HealthComponent узнает, что к игровому объекту, к которому он прикреплен, прикреплен AnimationComponent? Единственное решение, которое я вижу здесь, это
Проведите проверку, чтобы увидеть, подключен ли компонент AnimationComponent (внутри кода компонента или на стороне двигателя)
Для компонентов требуются другие компоненты, но это, кажется, борется со всей конструкцией компонентов.
Напишите, например, HealthWithAnimationComponent, HealthNoAnimationComponent и так далее, что, опять же, похоже, противоречит всей идее разработки компонентов.
Ответы:
Во всех ваших примерах есть ужасная проблема. Компонент здравоохранения должен знать о каждом типе компонента, который может потребоваться для реагирования на смерть объекта. Поэтому ни один из ваших сценариев не подходит. Ваша сущность имеет компонент здоровья. Он имеет компонент анимации. Ни зависеть, ни знать о другом. Они общаются через систему обмена сообщениями.
Когда компонент здоровья обнаруживает, что объект «умер», он отправляет сообщение «Я умер». Компонент анимации отвечает за ответ на это сообщение, воспроизводя соответствующую анимацию.
Компонент Health не отправляет сообщение напрямую компоненту анимации. Возможно, он передает его каждому компоненту в этой сущности, может быть, всей системе; может быть, компоненту анимации необходимо сообщить системе сообщений, что она заинтересована в сообщениях «Я умер». Существует много способов реализации системы обмена сообщениями. Как бы вы ни реализовали это, суть в том, что компоненту работоспособности и компоненту анимации никогда не нужно знать или заботиться о наличии другого, и добавление новых компонентов никогда не потребует изменения существующих для отправки им соответствующих сообщений.
источник
Способ, которым Артемида решает проблему, состоит в том, чтобы не выполнять обработку внутри Компонентов. Компоненты содержат только те данные, которые им необходимы. Системы считывают несколько типов компонентов и выполняют любую необходимую обработку.
Таким образом, в вашем случае у вас может быть RenderSystem, которая читает HealthComponent (и другие) и воспроизводит очереди соответствующей анимации. Такое отделение данных от функций облегчает правильное управление зависимостями.
источник
В вашем коде вы можете найти способы (я использовал их, возможно, существуют и другие способы) узнать, изменилось ли состояние объекта:
Для этого я использовал: 1. функцию HasComponent в GameObject или 2. когда вы присоединяете компонент, вы можете проверить зависимости в какой-либо конструктивной функции, или 3. если я точно знаю, что объект имеет этот компонент, я просто использую его.
В некоторых статьях я читал, что в идеале компоненты системы не зависят друг от друга, но в реальной жизни это не так.
Это плохая идея, чтобы писать такие компоненты. В своем приложении я создал компонент Health, наиболее независимый. Теперь я думаю о некотором шаблоне Observer, который уведомляет подписчиков о каком-то конкретном событии (например, «попадание», «исцеление» и т. Д.). Поэтому AnimationComponent должен сам решить, когда воспроизводить анимацию.
Но когда я прочитал статью о CBES, это произвело на меня впечатление, поэтому я очень счастлив сейчас, когда использую CBES и открываю для него новые возможности.
источник
Это как Майкл, говорит Патрик Хьюз и Блецки. Чтобы избежать простого перемещения проблемы, нужно в первую очередь отказаться от идеологии, которая вызывает проблему.
Его меньше OOD и больше похоже на функциональное программирование. Когда я начал экспериментировать с компонентным проектированием, я заметил эту проблему в будущем. Я погуглил еще немного и обнаружил, что «Функциональное реактивное программирование» является решением.
Теперь мои компоненты - это не что иное, как набор переменных и полей, которые описывают его текущее состояние. Затем у меня есть куча «системных» классов, которые обновляют все компоненты, которые имеют к ним отношение. Реактивная часть достигается путем запуска систем в четко определенном порядке. Это гарантирует, что какая бы система ни была следующей в очереди, чтобы выполнять свою обработку и обновление, и какие бы компоненты и объекты она ни собиралась читать и обновлять, она всегда работает с самыми последними данными.
Тем не менее, в некотором смысле вы все еще можете утверждать, что проблема снова изменилась. Потому что, если не ясно, в каком порядке должны работать ваши системы? Что делать, если существуют циклические отношения, и это только вопрос времени, прежде чем вы будете смотреть на беспорядок if-else и операторов switch? Это неявная форма обмена сообщениями, нет? На первый взгляд, я думаю, что это небольшой риск. Обычно вещи обрабатываются по порядку. Примерно так: ввод игрока -> позиции объекта -> обнаружение столкновений -> логика игры -> рендеринг -> начать заново. В этом случае у вас будет по одной Системе для каждой, предоставьте каждой Системе метод update (), а затем запустите их последовательно в вашей игровой петле.
источник