Как реализовать функции в системе сущностей?

31

Задав два вопроса о системах сущностей ( 1 , 2 ) и прочитав некоторые статьи о них, я думаю, что понимаю их гораздо лучше, чем раньше. У меня все еще есть некоторые неопределенности, в основном в отношении создания эмиттера частиц, системы ввода и камеры. У меня, очевидно, все еще есть некоторые проблемы с пониманием систем сущностей, и они могут относиться к целому ряду объектов, но я выбрал эти три, потому что они представляют собой очень разные понятия, должны охватывать довольно широкий круг вопросов и помочь мне понять системы сущностей и как справляюсь с такими проблемами, как я сам, по мере их появления.

Я создаю движок на JavaScript, и я реализовал большинство основных функций, которые включают в себя: обработку ввода, гибкую систему анимации, эмиттер частиц, математические классы и функции, обработку сцены, камеру и рендер, и целую кучу других вещей, которые обычно поддерживают двигатели. Я прочитал ответ Byte56, который заинтересовал меня в превращении движка в систему сущностей. Он по-прежнему останется игровым движком HTML5 с базовой философией сцены, но он должен поддерживать динамическое создание объектов из компонентов.


Теперь у меня проблема в том, чтобы вписать мою старую концепцию движка в эту новую парадигму программирования. Вот некоторые определения из предыдущих вопросов, обновленные:

  • Сущность является идентификатором. У него нет никаких данных, это не объект, это простой идентификатор, который представляет индекс в списке сцен всех сущностей (который я на самом деле планирую реализовать в виде матрицы компонентов).

  • Компонент является держателем данных, но с помощью методов , которые могут работать с этими данными. Лучший пример - Vector2Dкомпонент «Позиция». Он имеет данные: xи y, а также некоторые методы , которые делают работающие на данных немного проще: add(), normalize()и так далее.

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


камера

Камера имеет Vector2Dсвойство положения, свойство поворота и некоторые методы для центрирования ее вокруг точки. Каждый кадр, он подается на рендерер вместе со сценой, и все объекты переводятся в соответствии с его положением. Сцена затем отображается.

Как я мог представить этот вид объекта в системе сущностей? Будет ли камера объектом, компонентом или комбинацией (согласно моему ответу )?

Излучатель частиц

Проблема с моим эмиттером частиц, опять же, то, что должно быть чем. Я почти уверен, что частицы сами по себе не должны быть сущностями, поскольку я хочу поддерживать более 10 000 из них, и я считаю, что создание такого количества сущностей нанесло бы серьезный удар по моей производительности.

Как я мог представить этот вид объекта в системе сущностей?

Менеджер ввода

Последнее, о чем я хочу поговорить, - как обрабатывать ввод. В моей текущей версии движка есть класс с именем Input. Это обработчик, который подписывается на события браузера, такие как нажатия клавиш и изменения положения мыши, а также поддерживает внутреннее состояние. Затем у класса проигрывателя есть react()метод, который принимает входной объект в качестве аргумента. Преимущество этого состоит в том, что входной объект может быть сериализован в .JSON, а затем распространен по сети, что обеспечивает плавное многопользовательское моделирование.

Как это переводится в систему сущностей?

jcora
источник

Ответы:

26
  • Камера: Создание этого компонента было бы довольно аккуратно. Было бы простоisRenderingФлаг и диапазон глубины, как сказал Шон. В дополнение к «полю обзора» (я думаю, вы можете назвать его масштабным в 2D?) И выходной зоне. Зона вывода может определять часть игрового окна, к которой эта камера обращается. У него не будет отдельной позиции / поворота, как вы упомянули. Созданный вами объект, имеющий компонент камеры, будет использовать компоненты положения и поворота этого объекта. Тогда у вас будет система камер, которая ищет объекты, в которых есть камера, компоненты положения и поворота. Система берет этот объект и рисует все объекты, которые он может «видеть», из своей позиции, поворота, глубины и поля зрения в указанную часть экрана. Это дает вам много возможностей для имитации нескольких портов просмотра, окон «просмотра символов», локального мультиплеера,

  • Излучатель частиц: это тоже должен быть только компонент. Система частиц будет искать объекты, которые имеют положение, вращение и эмиттер частиц. Эмиттер обладает всеми свойствами, необходимыми для воспроизведения вашего текущего эмиттера, я не уверен, что это все, такие как: скорость, начальная скорость, время затухания и так далее. Вам не нужно было бы делать несколько проходов. Система частиц знает, какие объекты имеют этот компонент. Я полагаю, вы могли бы повторно использовать существующий код.

  • Входные данные: я должен сказать, что превращение этого в компонент наиболее разумно, учитывая предложения, которые я сделал выше. Вашinput systemбудет обновляться каждый кадр с текущими событиями ввода. Затем, когда он проходит через все свои объекты, которые имеют компонент ввода, он будет применять эти события. Компонент ввода будет иметь список событий клавиатуры и мыши со всеми связанными обратными вызовами методов. Я не совсем уверен, где будут жить обратные вызовы метода. Возможно какой-нибудь класс контроллера ввода? Что бы ни имело смысла для дальнейшей модификации пользователями вашего движка. Но это дало бы вам возможность легко применять управление вводом к объектам камеры, объектам игроков или тому, что вам нужно. Хотите синхронизировать движение группы объектов с помощью клавиатуры? Просто дайте им все компоненты ввода, которые отвечают на одни и те же входы, и система ввода применяет эти события перемещения ко всем запрашивающим их компонентам.

Таким образом, большая часть всего этого находится у меня в голове, поэтому, вероятно, это не будет иметь смысла без дальнейших объяснений. Так что просто дайте мне знать, что вам не ясно. По сути, я дал вам много работы :)

MichaelHouse
источник
Еще один отличный ответ! Благодарность! Теперь моя единственная проблема - это быстрое хранение и извлечение сущностей, поэтому пользователь может реализовать игровой цикл / логику ... Я попытаюсь понять это самостоятельно, но сначала я должен узнать, как Javascript работает с массивами, объектами и неопределенные значения в памяти, чтобы сделать правильное предположение ... Это будет проблемой, потому что разные браузеры могут реализовать это по-разному.
Jcora
Это кажется архитектурно чистым, но как система рендеринга определяет активную камеру за исключением итерации по всем объектам?
Pace
@Pace Так как я хотел бы, чтобы активная камера была обнаружена очень быстро, я, вероятно, позволил бы системе камер сохранять ссылку на объекты, которые имеют активную камеру.
MichaelHouse
Где вы размещаете логику для управления несколькими камерами (смотреть, вращать, перемещать и т. Д.)? Как вы управляете несколькими камерами?
плазмацел
@plasmacel Если у вас есть несколько объектов, которые совместно используют элементы управления, ваша система управления будет определять, какой объект получает входные данные.
MichaelHouse
13

Вот как я подошел к этому:

камера

Моя камера, как и любая другая, является объектом, к которому прикреплены компоненты:

  1. Transformимеет Translation, Rotationи Scaleсвойства, в дополнение к другим , для скорости и т.д.

  2. Pov(Точка зрения) имеет FieldOfView, AspectRatio, Near, Far, и все остальное , требуется , чтобы произвести матрицу проекции, в дополнении к IsOrthoфлагу используется для переключения между перспективой и ортогональной проекцией. Povтакже предоставляет ProjectionMatrixсвойство lazy-load, используемое системой рендеринга, которое внутренне рассчитывается при чтении и кэшируется до изменения любых других свойств.

Там нет выделенной системы камеры. Система рендеринга поддерживает список Povи содержит логику для определения того, какой из них использовать при рендеринге.

вход

InputReceiverКомпонент может быть присоединен к любому объекту. У него есть прикрепленный обработчик событий (или лямбда, если ваш язык поддерживает его), который используется для проведения обработки ввода, зависящей от объекта, которая принимает параметры для текущего и предыдущего состояния ключа, текущего и предыдущего положения мыши, состояния кнопки и т. Д. (На самом деле, есть отдельные обработчики для мышки и клавиатуры).

Например, в тестовой игре, похожей на астероиды, которую я создал, когда привыкал к сущности / компоненту, у меня есть два лямбда-метода ввода. Один управляет навигацией корабля, обрабатывая клавиши со стрелками и пробел (для стрельбы). Другой обрабатывает общий ввод с клавиатуры - клавиши для выхода, паузы и т. Д., Уровня перезапуска и т. Д. Я создаю два компонента, прикрепляю каждую лямбду к своему собственному компоненту, затем назначаю компонент приемника навигации объекту корабля, а другой - невидимый объект командного процессора.

Вот обработчик событий для обработки ключей, которые хранятся между кадрами, которые прикрепляются к InputReceiverкомпоненту корабля (C #):

  void ship_input_Hold(object sender, InputEventArgs args)
    {
        var k = args.Keys;
        var e = args.Entity;

        var dt = (float)args.GameTime.ElapsedGameTime.TotalSeconds;

        var verlet = e.As<VerletMotion>();
        var transform = e.As<Transform>();

        if (verlet != null)
        {

        /// calculate applied force 
            var force = Vector3.Zero;
            var forward = transform.RotationMatrix.Up * Settings.ShipSpeedMax;

            if (k.Contains(Keys.W))
                force += forward;

            if (k.Contains(Keys.S))
                force -= forward;

            verlet.Force += force * dt;
        }

        if (transform != null)
        {
            var theta = Vector3.Zero;

            if (k.Contains(Keys.A))
                theta.Z += Settings.TurnRate;

            if (k.Contains(Keys.D))
                theta.Z -= Settings.TurnRate;

            transform.Rotation += theta * dt;
        }

        if (k.Contains(Keys.Space))
        {
            var time = (float)args.GameTime.TotalGameTime.TotalSeconds - _rapidFireLast;

            if (time >= _rapidFireDelay)
            {
                Fire();
                _rapidFireLast = (float)args.GameTime.TotalGameTime.TotalSeconds;
            }
        }
    }

Если ваша камера мобильна, предоставьте ей свою собственную InputReceiverи Transformкомпонентную, подключите лямбду или обработчик, который реализует любой вид управления, который вы хотите, и все готово.

Это довольно удобно, поскольку вы можете переместить InputReceiverкомпонент с навигационным обработчиком, прикрепленным с корабля к астероиду, или что-нибудь еще в этом отношении, и вместо этого летать вокруг него. Или, назначив Povкомпонент чему-либо еще в вашей сцене - астероиду, уличному фонарю и т. Д. - вы можете просматривать свою сцену с точки зрения этой сущности.

InputSystemКласс , который поддерживает внутреннее состояние для клавиатуры, мыши и т.д. InputSystemфильтрует его внутреннюю коллекцию сущностей для лиц , которые имеют InputReceiverкомпонент. В своем Update()методе он выполняет итерацию по этой коллекции и вызывает обработчики ввода, связанные с каждым из этих компонентов, так же, как система рендеринга рисует каждый объект с Renderableкомпонентом.

Частицы

Это действительно зависит от того, как вы планируете взаимодействовать с частицами. Если вам просто нужна система частиц, которая ведет себя как один объект - скажем, фейерверк, показывающий, что игрок не может коснуться или ударить - тогда я создам одну сущность и ParticleRenderGroupкомпонент, который содержит любую информацию, необходимую вам для частиц - распад и т. д. - это не распространяется на ваш Renderableкомпонент. При рендеринге система рендеринга увидит, имеет ли объект RenderParticleGroupприсоединенный объект, и обработает его соответствующим образом.

Если вам нужны отдельные частицы для участия в обнаружении столкновений, реагирования на ввод и т. Д., Но вы просто хотите отобразить их как пакет, я бы создал Particleкомпонент, который содержит эту информацию для каждой частицы, и создал бы их как отдельные лица. Система рендеринга все еще может пакетировать их, но другие системы будут обрабатывать их как отдельные объекты. (Это очень хорошо работает с экземплярами.)

Затем, либо в вашем MotionSystem(или в любом другом использовании, которое обрабатывает обновление позиции объекта и т. Д.) , Либо в выделенном режиме ParticleSystem, выполняйте любую обработку, необходимую для каждой частицы на кадр. Он RenderSystemбудет отвечать за создание / пакетирование и кэширование коллекций частиц по мере их создания и уничтожения и отображать их по мере необходимости.

Хорошая особенность этого подхода заключается в том, что вам не нужно иметь никаких особых случаев для столкновения, отбраковки и т. Д. Для частиц; код, который вы пишете для любого другого типа объекта, все еще может быть использован.

Вывод

Если вы планируете переходить кроссплатформенно - не супер-применимо к JavaScript - весь ваш специфичный для платформы код (а именно рендеринг и ввод) разделен на две системы. Логика вашей игры остается в классах, не зависящих от платформы (движение, столкновение и т. Д.), Поэтому вам не нужно прикасаться к ним при портировании.

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

3Dave
источник
Где вы размещаете логику для управления несколькими камерами (смотреть, вращать, перемещать и т. Д.)?
плазмацел
7

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

Аналогично, излучатели частиц - это особые звери, особенно если вам небезразлична производительность. Компонент эмиттера имеет смысл, но графика будет создавать особую магию с этими компонентами, смешанную с магией для остальной части рендеринга.

Что касается вашей камеры, просто установите для камер активный флаг и, возможно, индекс «глубины», и позвольте графической системе отобразить все из них, которые включены. Это на самом деле удобно для многих хитростей, включая GUI (хотите, чтобы ваш GUI отображался в орфографическом режиме на вершине игрового мира? Нет проблем, это всего лишь две камеры с разными масками объектов и GUI с более высоким уровнем). Это также полезно для слоев со спецэффектами и тому подобного.

Шон Миддледич
источник
4

Будет ли камера объектом или просто компонентом?

Я не уверен, что этот вопрос действительно задает. Если в игре есть только объекты, то камеры должны быть объектами. Функциональность камеры реализована через какой-то компонент камеры. Не используйте отдельные компоненты «Положение» и «Вращение» - это слишком низкий уровень. Они должны быть объединены в какой-то компонент WorldPosition, который будет применяться к любому объекту, расположенному в мире. Что касается того, что использовать ... вы должны каким-то образом ввести логику в систему. Либо вы жестко запрограммировали его в систему обработки камер, либо вложили сценарии, либо что-то еще. Вы можете иметь флаг включения / выключения на компоненте камеры, если это помогает.

Я уверен, что сами частицы не должны быть сущностями

Я тоже. Излучатель частиц будет сущностью, а система частиц будет отслеживать частицы, связанные с данной сущностью. Такие вещи, когда вы понимаете, что «все является сущностью» абсурдно непрактичны. На практике единственными объектами являются относительно сложные объекты, которые выигрывают от сочетаний компонентов.

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

Kylotan
источник
1

Вот некоторые из моих идей по решению этих проблем. Возможно, у них что-то не так, и, возможно, будет лучший подход, поэтому, пожалуйста, направьте меня к тем, кто ответит!

Камера :

Существует компонент «Камера», который можно добавить к любому объекту. Однако я не могу понять, какие данные мне следует поместить в этот компонент: у меня могут быть отдельные компоненты «Положение» и «Поворот»! Этот followметод не нуждается в реализации, потому что он уже следует сущности, к которой он присоединен! И я могу свободно перемещать это. Проблема с этой системой будет заключаться в большом количестве различных объектов камеры: как RendererSystemузнать, какие из них использовать? Кроме того, раньше я просто передавал объект камеры, но теперь кажется, что RendererSystemнужно будет выполнить итерацию дважды по всем объектам: во-первых, чтобы найти те, которые действуют как камеры, и, во-вторых, чтобы фактически отобразить все.

ParticleEmitter :

Там будет ParticleSystem обновление, которое обновляло бы все сущности, которые имели компонент Emitter. Частицы - это тупые объекты в относительном координатном пространстве внутри этого компонента. Здесь возникает проблема рендеринга: мне нужно либо создать ParticleRendererсистему, либо расширить функциональность существующей.

Система ввода :

Главной заботой для меня здесь была логика или react()метод. Единственное решение, которое я придумал, - это отдельная система для этого и компонент для каждой системы, который бы указывал, какую из них использовать. Это действительно кажется слишком глупым, и я не знаю, как с этим справиться. Одна вещь заключается в том, что, насколько мне известно, реализация Inputможет быть реализована как класс, но я не понимаю, как я могу интегрировать ее в остальную часть игры.

jcora
источник
У RendererSystem на самом деле нет причин для перебора всех объектов - у нее уже должен быть список объектов для рисования (и камер, и источников света (если источники света не являются объектами рисования)), или знать, где находятся эти списки. Кроме того, вы, вероятно, захотите сделать выборку для камер, которые вы хотите визуализировать, поэтому, возможно, ваша камера может содержать список видимых для нее идентификаторов объектов для рисования. У вас может быть много камер и одна активная, или одна камера, которая подключается к разным POV, обе из которых могут управляться любым количеством вещей, таких как сценарии, триггеры и входы
@ melak47, это правда, я тоже думал об этом, но хотел реализовать это так, как это делает Аремис. Но эта «система хранения ссылок на соответствующие объекты» кажется все более и более ошибочной ...
Jcora
Разве Артемида не хранит каждый тип Компонента в своем собственном списке? так разве у вас не будет именно этих списков нарисованных компонентов, компонентов камеры, источников света и чего-то еще?