Comment by inigyou

1 day ago

My experience with ECS is that you shouldn't use an "ECS system". You should just do ECS. Have an array of all your particles and then update all the positions according to the velocities. Don't use a framework where you do something like get_all_entities_with<Particle, Position, Velocity>(). Just have struct particles {vector<vec3> positions, velocities;}. Well, managing those parallel arrays gets pretty annoying, but you solve that with a parallel-array class template rather than a whole framework that promises to do everything. (You might not actually need parallel arrays anyway - AoS might work just fine here because you usually touch each field of each particle once per frame and exactly in order.)

But why? This looks like the mother of all boilerplates. And for what purpose?

Also: just having stuff in an array or vector invites you to the ABA problem. You need a generation counter in there too, or else the array indice may get reused if something is deleted and another thing is reinserted at the same index. But that's yet another boilerplate that would easily be overlooked if you had to do everything manually.

Also: you seem to be saying this with C++ in mind. Do you think that applies to bevy_ecs too?

Modern Bevy has relationships to make sure that if an entity has a component that refers to another, it doesn't become dangling. It works a bit like foreign keys in databases. I think this makes ecs much more usable

(as an aside, there is a whole host of analogies between ecs and relational databases. entity archetypes are tables, entities are rows, components are columns, and systems are queries). Nobody tells people to just write their database from scratch though)

  • > You need a generation counter in there too, or else the array indice may get reused if something is deleted and another thing is reinserted at the same index.

    It's really sounding like you're pretending not to know what your program does, which is one of the core OOP ideas that DOD refutes. In OOP you have an array of Shape and you pretend not to know which shapes your program implements, so the only way to draw them is to call ->draw() on each one. And you pretend you don't know anything about the lifetime of a shape so you use smart pointers everywhere to extend it as long as needed. In DOD you assert that you do know what shapes are available and what their lifetimes are.

    • I certainly don't know "what my program does" if it's a game where, at every frame of the simulation, thousands of entities might or might not be created, deleted and recycled depending on player inputs.

      Can you describe a superior replacement for generation counters?

      1 reply →

  • > This looks like the mother of all boilerplates. And for what purpose?

    Isn't most programming? Isn't struct Particle {vec3 position, velocity;} also the mother of all boilerplates?

The problem is that there are a lot of subtleties to an ECS that these frameworks solve, and they perform better than a naive approach too. Your solution of a particles struct doesn’t even support a fundamental feature of ECS’s which is runtime composition. It’s really a different solution altogether, which is fine but it’s not a replacement for an ECS.

  • Often you don't have runtime composition and don't need it. By prematurely generalizing you're venturing close to OOP territory.

    • “Prematurely generalizing” is now an OOP thing? Are we just using OOP as a term for anything bad now?

      Ooh here is a controversial one. DOD is premature optimization. Most programs don’t have enough data where the storage and access is a factor for performance. In fact it might be slower to use DOD.

      7 replies →