Mapping a behaviour between ECS and OOP (Core implementation). - RamilGauss/MMO-Framework GitHub Wiki
При конструировании сцены или префаба возникает задача использовать возможности модулей.
Напрямую воспользоваться конечно можно (nsTornadoEngine::Modules()->...()
), но лучше использовать ECS что бы не опускаться на низкий уровень.
Например, для модуля с такой иерархией: Можно создать объекты с таким набором комопнентов:
Здесь зеленым помечены свойства. Например, Size содержит только данные. Класс C использует эти данные для логики. Если использовать объекты типа C, в которых есть свойства от родительский классов, то аналогичные им компоненты должны быть в Entity. Если в Entity не будет хотя бы одного компонента, то это может фатально закончится для исполнения программы. За комплектностью компонентов должен следить редактор.
У каждого модуля есть конвейер логики до вызова Work - Begin и после - End. При инстанцировании префаба или сцены используется именно Begin. На первом этапе происходит создание объектов. MakerFeature - например, для CComponent:
pCComponent->value = new C();
Далее нужно в этой же системе задать всем свойствам указатель на соотвествующий класс:
mEntMng->ViewComponent<SizeComponent>(eid)->pOwner = dynamic_cast<Size>(pCComponent->value);// аналогично и для Pos, Color, Title.
Для автомотизации используется TPropertyManager, в настройках которого заданы деревья, которые отражают дерево из модуля и содержат пары Component <-> Type (для конвертации).
Это делается для следующих этапов - Builder. В BuilderFeature обрабатывается реакция на добавление значений компонентов-свойств. Для реакции на добавление компонента Size:
pSizeComponent->pOwener->SetSize(pSizeComponent->value);
Аналогично и для других компонентов.
Изменение значений может быть двух типов: исходит от Модулей и исходит от кода логики разработчика.
Следует каждый кадр синхронизировать значения Entity и свойства соответсвующего объекта. Это нужно делать в EndFeature.
void Execute()
{
SizeComponent sizeComponent = {newSize};
auto eids = mEntMng->GetByHasCopy<SizeComponent>();
for (auto eid : eids) {
mEntMng->SetComponent(eid, sizeComponent);
}
}
Когда разработчик меняет свойство он вызывается SetComponent и происходит обработка реакции в BeginFeature (код такой же как в Builder).
При уничтожении Entity есть реакция только для главного компонента:
delete pC->value;
Для свойств ничего делать не нужно.
Если нужно, например, отправить какие-то данные (не важно какой компонент и из чего состоит). Нужно создать Entity с номером сессии (который был получен от клиента, или в случае запрос на сервера - можно хранить сессию сервера) добавить отправляемый компонент и пометить тэгом для реакции системы на отправку.
Допустим существует реактивная система с накоплением событий, в которой есть реакция на добавление компонента SendTagComponent. Нужно всего лишь перечислить все компоненты (кроме тэга и номера сессии), которыми владеет Entity, и сериализовать данные компонентов и вызвать метод SendPacket.
mEntMng->GetComponentList(eid, typeIdentifierList);
auto componentReflection = nsTornadoEngine::Project()->mScenePartAggregator->mComponents;
componentReflection->mEntMng->SetEntityManager(pEntMng);
auto netTransport = nsECSFramework::SingleComponent<NetTransportComponent>();
auto sessionId = mEntMng->ViewComponent<SessionIdComponent>(eid)->value;
for (auto rtti : typeIdentifierList) {
if (rtti == sendTagRtti || rtti == sessionIdRtti) {
continue;
}
auto pC = componentReflection->mEntMng->ViewComponent(mEntMng, eid, rtti);
std::string json;
componentReflection->mJson->Serialize(pC, json, rtti);
netTransport->SendPacket(sessionId, json.c_str(), json.size());
}