Mapping a behaviour between ECS and OOP (Core implementation). - RamilGauss/MMO-Framework GitHub Wiki

Изменение данных объектов из модулей

При конструировании сцены или префаба возникает задача использовать возможности модулей. Напрямую воспользоваться конечно можно (nsTornadoEngine::Modules()->...() ), но лучше использовать ECS что бы не опускаться на низкий уровень.

Например, для модуля с такой иерархией: Можно создать объекты с таким набором комопнентов:

image

Здесь зеленым помечены свойства. Например, Size содержит только данные. Класс C использует эти данные для логики. Если использовать объекты типа C, в которых есть свойства от родительский классов, то аналогичные им компоненты должны быть в Entity. Если в Entity не будет хотя бы одного компонента, то это может фатально закончится для исполнения программы. За комплектностью компонентов должен следить редактор.

Жизненный цикл Entity как отображение данных

Add

У каждого модуля есть конвейер логики до вызова 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);

Аналогично и для других компонентов.

Update

Изменение значений может быть двух типов: исходит от Модулей и исходит от кода логики разработчика.

0. От модулей

Следует каждый кадр синхронизировать значения Entity и свойства соответсвующего объекта. Это нужно делать в EndFeature.

void Execute()
{
    SizeComponent sizeComponent = {newSize};

    auto eids = mEntMng->GetByHasCopy<SizeComponent>();
    for (auto eid : eids) {
        mEntMng->SetComponent(eid, sizeComponent);
    }
}

1. От логики разработчика

Когда разработчик меняет свойство он вызывается SetComponent и происходит обработка реакции в BeginFeature (код такой же как в Builder).

Terminate

При уничтожении Entity есть реакция только для главного компонента:

delete pC->value;

Для свойств ничего делать не нужно.

Вызов методов объектов из модулей

Если нужно, например, отправить какие-то данные (не важно какой компонент и из чего состоит). Нужно создать Entity с номером сессии (который был получен от клиента, или в случае запрос на сервера - можно хранить сессию сервера) добавить отправляемый компонент и пометить тэгом для реакции системы на отправку.

image

Допустим существует реактивная система с накоплением событий, в которой есть реакция на добавление компонента 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());
}

Разделение данных и компонентов между модулями

⚠️ **GitHub.com Fallback** ⚠️