Система компонентов C ++
Система компонентов позволяет реализовать логику вашего приложения с помощью набора строительных блоков — компонентов и назначать эти блоки нодам, предоставляя им дополнительную функциональность. Комбинируя эти маленькие и простые блоки, вы можете создать очень сложную логическую систему.
Логический компонент объединяет ноду, класс C++, содержащий логическую реализацию (действия, которые необходимо выполнить), и свойство (property), определяющее набор дополнительных параметров, которые будут использоваться. Параметры и их типы одинаковы как для компонента, так и для соответствующего свойства.
Компоненты дают вам больше гибкости в реализации вашей логики, позволяя:
- управлять тем, какие части кода (реализованные как методы компонентов) должны выполняться, а какие нет;
- контролировать порядок выполнения этих частей кода;
- неоднократно использовать части кода, написанные один раз, для любого количества объектов без каких-либо изменений. Если вы хотите изменить свой код, вы меняете его в одном-единственном источнике (как в случае с NodeReference, если сравнивать с контентом);
- объединять определенные части кода, которые будут выполняться для определенных нод. Можно создать очень сложную систему из множества маленьких и простых блоков (это как использовать NodeReference для построения большой сложной структуры из множества простых нод).
Дополнительная информация#
- API Системы компонентов C++ для получения дополнительных сведений об управлении компонентами через C++ API.
- Пример использования Системы компонентов C++ для получения дополнительных сведений о реализации логики с использованием системы компонентов.
- Пример C ++: source/samples/Api/Logics/ComponentSystem
Логика#
Логика компонентов реализована с помощью набора методов, которые вызываются соответствующими функциями скрипта world:
- init() — создаются и инициализируются все необходимые ресурсы.
-
updateAsyncThread() — указываются все логические функции, которые вы хотите вызывать в каждом кадре независимо от потока рендеринга.
Этот метод не имеет защитных блокировок, поэтому не рекомендуется изменять другие компоненты внутри этого метода, если вы не уверены, что эти компоненты не будут изменяться или удаляться где-либо еще. -
updateSyncThread() — указываются все параллельные функции логики, которые вы хотите выполнить до update(). Этот метод можно использовать для выполнения некоторых ресурсоемких вычислений, таких как поиск пути, создание процедурных текстур и т. д.
Этот метод следует использовать для вызова только тех методов API, которые относятся к текущей ноде: самой ноде, ее материалам и свойствам. - update() — указываются все логические функции, которые должны вызываться в каждом кадре.
- postUpdate() — корректируется поведение в соответствии с обновленными состояниями нод в том же кадре.
- updatePhysics() — выполняется симуляция физики: выполнение непрерывных операций (продвижение машины вперед в зависимости от оборотов двигателя, имитация постоянного ветра, выполнение немедленных реакций на столкновение и т. д.).
-
swap() — работа с результатами метода updateAsyncThread(), все остальные методы (потоки) уже выполнены и ждут. После этой функции происходят только два действия:
- все объекты, поставленные в очередь на удаление, удаляются;
- выполняется визуализация Профайлера.
- shutdown() — выполняется очистка при выключении мира.
Рабочий процесс#
Базовый рабочий процесс выглядит следующим образом:
- Унаследуйте новый класс C ++, представляющий ваш компонент, от класса ComponentBase. Шаблон этого класса доступен в заголовочном файле UnigineComponentSystem.h в виде комментария.
- В заголовочном файле определите и объявите список параметров, которые будут использоваться этим компонентом. Все эти параметры со значениями по умолчанию (если они указаны) будут сохранены в специальном файле свойств.
- Реализуйте компонентную логику внутри определенных методов (init(), update(), postUpdate()и т. д.), которые будут вызываться соответствующими функциями основного цикла движка.
- Скомпилируйте проект и запустите его один раз, чтобы сгенерировать свойства (property) для всех компонентов.
- Назначьте созданное свойство ноде, чтобы дать ей желаемую функциональность.
Каждый раз, когда свойство, зарегистрированное в Системе компонентов, назначается ноде, создается экземпляр соответствующего компонента. Этот экземпляр будет удален, когда соответствующее свойство будет заменено другим или удалено из списка ноды, или когда нода будет удалена.
Логика определенного компонента активна только тогда, когда соответствующая нода и свойство включены. Таким образом, при необходимости вы можете включать / отключать логику каждого конкретного компонента во время выполнения.
Вы можете назначить несколько свойств, соответствующих различным компонентам, на одну ноду. Последовательность, в которой выполняется логика компонентов, определяется значением order, указанным для соответствующих методов (если значения order совпадают или не указаны, последовательность определяется в соответствии с иерархией нод).
Вы также можете создавать компоненты для существующих свойств.
Компоненты могут взаимодействовать с другими компонентами и нодами.
Применение#
В качестве примера можно использовать компоненты для реализации логики преследования врагами в вашей игре: независимо от их размера, формы, скорости, все они будут проверять позицию игрока и пытаться найти путь, по которому можно максимально быстро дойти до игрока. Код будет по большей части одинаковым, могут различаться лишь параметры (скорость, меш и, возможно, звуки), поэтому можно добавить все эти параметры в свойство (чтобы иметь возможность изменять их в любой момент) и код соответствующего класса компонента (например, добавление врагов в мир в методе init() и преследование игрока в методе update()).
Затем нужно просто назначить свойство всем объектам врагов и настроить параметры (определить меши, звуки и т. д.). Система компонентов сделает все остальное: выполнит код на соответствующих этапах основного цикла движка для всех объектов врагов, используя их конкретные параметры. Если вы впоследствии захотите изменить код, это можно сделать в одном единственном месте — классе компонента.
Интеграция с инструментом Microprofile позволяет отслеживать общую производительность системы компонентов, а также добавлять необходимую информацию по компонентам.