Обработка ввода в компонентном дизайне

12

Я знаю, что этот вопрос задавался несколько раз, но я все еще не уверен, как реализовать обработку ввода в компонентном движке.

Основанный на компонентах дизайн, который я использовал, был основан на серии блогов T = Machine и на Artemis, в которых сущности - это просто идентификаторы.

У меня есть три основные идеи по реализации обработки ввода:

  1. Компонент ввода будет содержать интересующие его события. Система ввода преобразует события клавиш и мыши в игровые события и проходит по объектам с компонентом ввода, и, если они заинтересованы в событии, система ввода предпримет соответствующее действие. Это действие будет жестко закодировано в системе ввода.
  2. Нет входного компонента. Вы должны зарегистрировать объекты с конкретными событиями в системе ввода. Затем система ввода будет отправлять сообщения (с идентификатором объекта и типом события) в другие системы, чтобы они могли предпринять соответствующие действия. Или, как в первом случае, действия будут жестко закодированы в системе ввода.
  3. Аналогично первому методу, но вместо жесткого кодирования действия для системы ввода компонент будет содержать карту событий для функций (то есть std::map<std::function>), которые будут вызываться системой ввода. Это дает дополнительный эффект возможности связывать одно и то же событие с разными действиями.

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

Примечание. Одно дополнительное требование, которое я хотел бы выполнить для реализации, заключается в том, чтобы я мог передавать один и тот же вход многим объектам, например, перемещая объект камеры и проигрыватель одновременно.

Grieverheart
источник
2
Обычно (когда камера следует за игроком), вы не хотите получать ввод в камеру, вместо этого вы заставляете камеру проверять положение игрока и следовать ей.
Люк Б.
1
Концептуально не имеет значения, следует ли камера за игроком или «за собой». Тем не менее, я не уверен, как ваше предложение будет реализовано в компонентном дизайне без нарушения принципов проектирования.
Grieverheart
1
@ Люк Б .: Подумав немного, я вижу, что вы также можете сделать камеру отдельным классом, взяв указатель на объект для подражания.
Grieverheart

Ответы:

8

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

Похоже, у вас уже есть система, которая обрабатывает ввод данных от игрока. Я бы выбрал подход, который затем переводит этот вклад в действия («двигаться вперед» или «двигаться назад») или события и рассылает их заинтересованным сторонам. В прошлом я запрещал компонентам регистрировать себя для этих событий, предпочитая подход, в котором система более высокого уровня явно выбирала «контролируемую сущность». Но это может работать иначе, если вы предпочитаете, особенно если вы собираетесь повторно использовать одни и те же сообщения для выполнения действий, которые не были стимулированы непосредственно вводом.

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

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

Сообщество
источник
Я немного обдумал свой вопрос и собирался ответить на него сам. Я также пришел к выводу, что мне лучше отсоединить обработку ввода от системы EC, так что приятно видеть подтверждение этому. Как я думал об этом, так это об использовании сигналов и возможности связать несколько объектов с типом события. Я также решил отделить камеру, хотя это не является действительно необходимым, и обладание ею как единым целым было бы одинаково жизнеспособным. Я думаю, что когда вы все еще новичок в ЕС, вам действительно нужно подумать о том, какие преимущества дает создание какого-либо компонента или объекта.
Grieverheart
4

Мой опыт может быть предвзятым, но в мультиплатформенных проектах устройства ввода напрямую не связаны с системой сущностей.

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

Эти события затем отправляются через слой контекстно-зависимых генераторов намерений.

Каждый генератор регистрирует изменения состояния компонентов, объектов и систем, которые имеют отношение к его функциям.

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

Таким образом, вы можете просто полагаться на «всегда», имеющее один и тот же вход, то есть JUMP_INTENT (1), JUMP_INTENT (0), AIM_INTENT (1) ...

И «вся» грязная работа, зависящая от платформы, остается за пределами вашей системы сущностей.


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

В противном случае, если он следует за игроком, он никогда не должен слушать входы, предназначенные для игрока. Он должен слушать изменения состояния, испускаемые игроком (ENTITY_MOVED (transform)) ... и двигаться соответственно. Если вы используете физическую систему, вы можете даже прикрепить камеру к плееру с помощью одного из различных соединений.

койот
источник
Койот, спасибо за ваш ответ. Я также прочитал ваш другой пост здесь . Моя самая большая проблема не в том, как абстрагировать ввод. У меня уже есть конструкция более низкого уровня, которая обрабатывает нажатия клавиш и тому подобное, и добавить еще один уровень косвенности не составит труда. Моя проблема в обработке событий, генерируемых, например, вашей системой намерений. Если я правильно понимаю, у вас нет входных компонентов в вашем методе. Как вы знаете, какие объекты нуждаются в вводе и как вы справляетесь с этим? Не могли бы вы привести более конкретные примеры?
Grieverheart
2

Какую пользу приносит InputComponent? Конечно, прерогативой входной команды является определение того, над какими объектами она выполняет действие. Классический пример - прыжок игрока. Вместо того чтобы иметь InputComponent на каждом объекте, прослушивающем события «Jump», почему бы не выполнить команду перехода для поиска объекта, помеченного как «player», и выполнить саму необходимую логику?

Action jump = () =>
{
    entities["player"].Transform.Velocity.Y += 5;
};

Еще один пример из ОП:

Action moveRight = () =>
{
    foreach (var entity in entities.Tagged("player", "camera"))
        entity.Transform.Position.X += 5;
};
AlexFoxGill
источник