Войти
ПрограммированиеФорумГрафика

ECS во все поля (2 стр)

Страницы: 1 2 3 4 5 6 Следующая »
#15
11:04, 11 апр. 2020

Suslik
> нет у систем состояния

догма. Systems = системЫ. Log- система, и конечно имеет состояние. система без состояния - это чистая функция(синус). Тебе нравятся такие системы? Ни вопрос, юзай.
Но вообще я и не хочу пускаться в словоблудие про системы. Systems.Get<Log>() - вполне удобный код доступа к любому сквозному функционалу. И это в стиле DI, облегчает TDD.. и придумано ни мной. А вот в чем состоит ECS твой - ты и сам не понимашь.

#16
11:23, 11 апр. 2020

/A\
> от создателей танков (тут есть про сингл компоненты)
> https://habr.com/ru/company/wargaming/blog/245321/

да, здесь конкретики побольше. Мне это напоминает Dd in memory + опять все тот же DI. Никакой отмены ООП, актор-based  логики нет. Бд в памяти позволяет делать какие то запросы, примерно также как в бизнес приложениях ORM ни отменяет специфические запросы на сервер с SQL.
Почему это выдается за новый убер подход - хрен знает.

#17
12:39, 11 апр. 2020

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

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

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

/A\
> 1. Я думаю правильная ECS архитектура выглядит так:
>
> Компоненты это просто база данных (айди меша, материала и тд)
> системы создают свою, оптимизированную репрезентацию. Нужна иерархия - собирают
> граф зависимостей, нужно кэшировать пайплайны для вулкана - цепляют компонент с
> пайплайном.
например, это прямым образом расходится с основополагающим принципом, что в системах не содержится данных. однако, я тоже не представляю, как именно это должно работать, например, с физическим движком, который, являясь системой, просто by design ещё как содержит в себе оптимизированные для себя данные. вообще любая 3rd party система вроде рендерера всегда будет содержать какакие-то данные, я не видел stateless renderer'ов или stateless физических движков.

с другой стороны, в близзардовском докладе они говорили, что если какая-то система хочет построить своё представление объектов, то правильнее вынести это представление в отдельные компоненты и с большой вероятностью это представление может понадобиться ещё каким-то системам. однако, это никак не отвечает на вопрос, как хранить представления, которые не являются локальными для энтитей — например, общее AABB tree всей сцены, всякие графы порталов между комнатами и прочие нелокальные сущности, не относящиеся к отдельным энтитям. тот же фреймграф не получится хранить в компонентах энтитей, хотя он и рендерит эти самые энтити.

#18
(Правка: 12:50) 12:49, 11 апр. 2020

например, прикольно, что во flecs системы вообще — это функции, принимают на вход-выход указанный набор компонент. это, я считаю, классная идея, потому что by design эти функции не могут содержать в себе данных и поэтому сам движок ecs может производить над ними очень алгоритмически сильные оптимизации вроде автоматического распараллеливания или даже векторизации:

struct Position {
    float x;
    float y;
};

struct Speed {
    float value;
};

int main(int argc, char *argv[]) {
    flecs::world world(argc, argv);

    flecs::component<Position>(world, "Position");
    flecs::component<Speed>(world, "Speed");

    flecs::system<Position, Speed>(world)
        .each([](flecs::entity e, Position& p, Speed& s) {
            p.x += s.value * e.delta_time();
            p.y += s.value * e.delta_time();
        });

    flecs::entity(world, "MyEntity")
        .set<Position>({0, 0})
        .set<Speed>({1});

    while (world.progress()) { }
}
другими словами, у них само API указывает использовать структуры для компонентов и функции для систем, это офигенно. но никак не отвечает на проблемы, обозначенные выше, а именно — как этим, блин, пользоваться-то, лол.

#19
12:52, 11 апр. 2020

Suslik
> должно работать, например, с физическим движком, который, являясь системой,
> просто by design ещё как содержит в себе оптимизированные для себя данные.

В твоем первом видео же ответ. Физдвижок - не система, а компонент.

#20
13:12, 11 апр. 2020

jaguard
> В твоем первом видео же ответ. Физдвижок - не система, а компонент.
да, но меня смущает, что так больше никто кроме них не считает, лол. также не забывай, что компоненты не должны содержать логики. а 3rd party физдвижок ещё как содержит кучу логики. у них у самих ссылка на физдвижок хранится в их PhysicsSystem, что, вообще говоря, не совсем чисто.

#21
13:28, 11 апр. 2020

Suslik
> прикольно, что во flecs системы вообще — это функции, принимают на вход-выход указанный набор компонент
У меня вообще нет "систем", просто функция или лямбда, в которой указаны требуемые компоненты и сингл-компоненты.
Системы как классы у меня хранят айди запросов для быстрого поиска по архетипам.

#22
(Правка: 13:47) 13:46, 11 апр. 2020

/A\
> У меня вообще нет "систем", просто функция или лямбда, в которой указаны
> требуемые компоненты и сингл-компоненты.
> Системы как классы у меня хранят айди запросов для быстрого поиска по
> архетипам.
покажи пример кода, который делает то же самое, что код выше, с использованием твоего синтаксиса?

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

#23
(Правка: 14:06) 14:04, 11 апр. 2020

Suslik
до оптимизаций запросов было так

Registry  reg;

for (int i = 0; i < 100; ++i) {
  reg.CreateEntity<Position, Speed>();
}
reg.AssignSingleComponent<TimeDelta>() = 1.0 / 60.0;

reg.Enque([] (ArrayView< Tuple< /*count*/size_t, ReadAccess<Speed>, WriteAccess<Position>>> chunks, Tuple<TimeDelta> single)
{
  for (auto& chunk : chunks)
  {
    auto  speed = chunk.Get<Speed>();
    auto  position = chunk.Get<Position>();

    for (size_t i = 0, count = chunk.Get<0>(); i < count; ++i)
    {
      position[i] += speed[i] * single.Get<TimeDelta>();
    }
  }
});

После оптимизации все эти ReadAccess и WriteAccess ушли в закэшированные запросы.
Исходники моей ECS тут

Кстати:

flecs::entity e
...
e.delta_time()
это же не ECS !

#24
(Правка: 14:24) 14:24, 11 апр. 2020

для деятелей вроде меня, кто не является экспертом по ECS, но хочет узнать больше, очень крутой источник соображений по дизайну вот тут: https://github.com/SanderMertens/flecs/blob/master/Manual.md#design-goals там рассказывается, что именно делать не рекомендуется и почему. если задуматься, то это очевидно, но я лично не задумывался.
/A\
> это же не ECS !
как я понимаю, это связано с тем, что они в дизайн закладывают разную частоту обновления для разных энтитей, поэтому таймстеп может быть свой для каждой энтити, определяется самой ecs и является частью её дизайна. но я тоже покорчился, когда это читал.

/A\
> до оптимизаций запросов было так
интересно. у тебя, в отличие от них, поддерживается ReadAccess/WriteAccess, что, опять же, лично для меня явялется отсылкой к фреймграфам. ещё у тебя поддерживаются single component'ы.

однако, мне у них больше нравится, как осуществляется доступ к чанкам через массивы, а не через итераторы:

    flecs::system<Position, Velocity>(world)
        .action([](const flecs::rows& rows, 
            flecs::column<Position> p, 
            flecs::column<Velocity> v) 
        {    
            for (auto row : rows) {
                p[row].x += v[row].x;
                p[row].y += v[row].y;

                std::cout << "Moved " << rows.entity(row).name() << " to {" <<
                    p[row].x << ", " << p[row].y << "}" << std::endl;
            }
        });
потому что таким образом можно тривиально писать системы, которые итерируются по чанкам, например, четвёрками или восьмёрками, чтобы реализовывать SIMD.

#25
14:33, 11 апр. 2020

Suslik
> однако, мне у них больше нравится, как осуществляется доступ к чанкам через массивы, а не через итераторы:
у меня тоже через индексы, в функцию передается массив чанков (с разными археипами), а потом по каждому чанку пробегаешь как по массиву.

#26
15:38, 11 апр. 2020

Suslik
> PS вообще здорово, что я тему создал. иначе б я навелосипедил, как я себе это
> сам представлял, когда можно гораздо лучше.
Один мой друг целый год изучал ECS и писал свою оптимизированную версию и то недописал.
Но мне было у кого спрашивать, когда я все это изучал.
Написать ECS в лучших традициях это еще не так сложно, а вот потом вылезут архитектурные проблемы, типа того же "куда засунуть дебаг рендер". Но почему-то мало кто делится своим опытом.

#27
(Правка: 16:14) 16:12, 11 апр. 2020

/A\
> у меня тоже через индексы, в функцию передается массив чанков (с разными
> археипами), а потом по каждому чанку пробегаешь как по массиву.
а, точно, я осознал. у flecs разница в том, что они вызывают один раз лямбду на чанк, а ты сам итерируешься по чанкам. разница, например, в том, что ты обязан сам где-то выделять память под массив чанков для каждого итерирования, а они могут не выделять и строить их на ходу.

ещё мне у тебя кажется странным тут:
> for (size_t i = 0, count = chunk.Get<0>(); i < count; ++i)
почему ты сделал первым элементом тупла количество, вместо того, чтобы сделать какой-нибудь метод вроде .GetSize()? что если я забуду вот тут:
> reg.Enque([] (ArrayView< Tuple< /*count*/size_t, ReadAccess<Speed>, WriteAccess<Position>>> chunks, Tuple<TimeDelta> single)
перечислить size_t?

ещё мне кажется, что методы вроде AssignSingleComponent<TimeDelta>() можно было бы избежать и вместо этого сделать так:

reg.GetSingletonEntity().SetComponent<TimeDelta>(...);
таким образом можно избежать дублирования функциональности методов энтити внутри регистра.

> Один мой друг целый год изучал ECS и писал свою оптимизированную версию и то недописал.
я склоняюсь к мысли не изобретать велосипед в этом, потому что мне более-менее нравятся сразу несколько готовых реализаций. поэтому я присматриваюсь к тому, чтобы взять одну из них.

> Написать ECS в лучших традициях это еще не так сложно, а вот потом вылезут архитектурные проблемы, типа того же "куда засунуть дебаг рендер". Но почему-то мало кто делится своим опытом.
я всё ещё читаю все ссылки, которые ты привёл выше. если ещё что-то вспомнишь, пость. пока что очень полезно и я много нового узнал.

#28
16:33, 11 апр. 2020

читаю документацию к EnTT: https://skypjack.github.io/entt/md_docs_md_entity.html

EnTT is such that at every moment a pair (T *, size) is available to directly access all the instances of a given component type T.
This was a guideline and a design decision that influenced many choices, for better and for worse. I cannot say whether it will be useful or not to the reader, but it's worth to mention it since it's one of the corner stones of this library.

Many of the tools described below give the possibility to get this information and have been designed around this need.


вот я как раз думал, пока рылся — интересно, это мне только мне такое приспичило, или кому-то ещё показалось это полезным иметь? и вот нате — чувак именно этот принцип положил в основу.

вообще я заметил, что бывают области, в которых вообще нет решения, которое меня бы просто, блин, устраивало. например, я не нашёл нормального (удобного, компактного) загрузчика 3д моделей. или, например, я не нашёл нормального(в моём понимании) загрузчика текстур. однако, душа радуется, когда ищешь библиотеку, и находишь сразу несколько нормальных, и сидишь, выбираешь, какая из них нравится больше, хотя в общем-то, нравятся все.

#29
(Правка: 16:46) 16:45, 11 апр. 2020

ещё оттуда же прочитал, что энтити — это интовый id + версия. версия, карл, это же гениально! то есть у них id — это просто индекс в свой внутренний пул. но если энтити удалить и пересоздать, то этот индекс будет переиспользован и  я последние несколько лет ломал голову, как нормально написать функцию вроде IsAlive(id) для подобных систем с пулом, построенном на массиве. это же гениально, можно просто использовать версию объекта с этим индексом и проверить, совпадает ли версия ныне живущего энтити, соответствующий этому id, с тем, который запросили. аа, как я сам до этого не догадался?!

Страницы: 1 2 3 4 5 6 Следующая »
ПрограммированиеФорумГрафика