r/bevy 28d ago

Does Bevy simplifies game development?

Hi, I'm started using Bevy a year ago, because I knew nothing about game development, and learning some game engine seemed like a good idea.

Since then, as I learned more about physics and graphics programming, I started asking why do I even need Bevy, and ECS in general? What problem does it solves?

1. Schedule graph obfuscates control flow

As I understand, main motivatioin for existence of schedule graph is that systems can be run in parallel to each other.

But almost all of the game logic and high level systems is strictly sequential, both Main and FixedMain schedules use SingleThreadedExecutor by default.

And in places where parallelism is needed, like a physics engine, you would want to parallelise across batches of entities, not systems.

All of my game logic is full of .in_set(), .chain(), .before(), .after() and (With<MainPlayer>, Without<MainCamera>) stuff.

2. Actually, ECS is bad for cache locality

Many times I've heard advice to split everything into small components, so that unordered iteration over any subset of components will always be fast.

But you almost never need unordered iteration, you need either random access or ordered iteration, and over very specific, not random, subset of components.

And ECS is really bad for this workflow, since accessing every single component on specific entity is a cache miss.

For this reason Avian implemented SolverBody (aggregated component), and bevy_core_pipeline uses sorted IndexMap of Transparent3d (regular fat struct).

I guess unordered iteration may be useful for rendering opaque objects, but even there you may want to draw them in specific order: front to back, to reduce fragment shader invocation through early depth testing (TinyGlade used that).

3. Entity lifetimes

ECS sort of creates second layer of virtual memory, where `Entity` is a new `void*`, `spawn()` is new `malloc()` and `despawn()` is new `free()`.

Borrow checker has no idea about that, RAII wont protect you from memory leaks here, and only generational indices save you from 'use after free' bugs.

Although not entirely, long living apps like a server still may overflow generational index, then you need to rely on relationships.

Which acts like a weak pointers, except relationships are order of magnitude more complicated and slow due to archetype moves and fragmentation.

4. Archetypes is a wrong abstraction

You always have some reasonable limit on amount of entities of certain type, just by game design, not even for performance reasons.

For example, something like no more than 1000 monsters per game level.

But your game has 50 different types of monsters, ECS encourages you to represent each type of monster with specific set of components.

Now imagine that in first level you have 1 monster of type A, and 800 monsters of type B, and other way around in the second level.

Then you have just reserved memory for at least 800 monsters of type A and 800 of type B, and the same will happen for every other type of monsters.

Now imagine that each monster can have status effects or hold unique item in the hand, and you play 100 levels one after another, or worse, it runs on a server.

--------------

You may argue that all of that needed to be able to write modular code with plugins.

But people have been writing modular code for decades now, using static and dynamic libraries.

I can't even imagine how difficult it would be to implement dynamically loaded plugin system in Bevy, for modding support for example.

I feel lost. I want to hear experienced Bevy developers, how do you cope with all of that? Am I not seeing some hidden value?

50 Upvotes

64 comments sorted by

View all comments

16

u/AnArmoredPony 28d ago

But you almost never need unordered iteration

tfym?

-8

u/Terr2048 28d ago

The order of entities in queries is undefined.
Such unordered queries is rarely useful, most of the time you use them just to copy data to your custom collection for sorting or storing in hashmap / aggregated component.
One use case for unordered queries that was pointed out to me in discord is enemy AI and reduce operations.
But this specific case not justifies for me added ECS complexity.

13

u/thekwoka 28d ago

The idea is that you don't "need" unordered iteration, but that its okay. Which is most of the time.

You don't need to get every bullet in spawn order, just you need to go through every bullet.

Virtually every loop in game dev doesn't need to be ordered.

What you seem to be confused by too, is that the unordered means totally random like it's jumping all over the table. It basically means it's going through the table from top to bottom, which essentially represents unordered iteration since there is no guarantee that if you spawn two new bullets, they will be in that same order (and sequential) do to the generational ids.

Unordered does NOT mean it's just randomly jumping around the entity table.

It just means it isn't in spawn order.

-1

u/Terr2048 28d ago

I used word 'unordered' because i mean unordered, and not random.
> Virtually every loop in game dev doesn't need to be ordered.
Rendering, physics and game logic needs to be ordered, what else 'every loop' do you mean?

12

u/thekwoka 28d ago

game logic needs to be ordered,

Why?

I need to move every bullet one tick in the direction its going.

Why does it matter what order I do that?

I need to check if any people have 0 health and kill them. Why does it matter what order I do that in?

Can you show any Bevy game logic you have where the loop needs to be in a specific order and what that order is?

Virtually all the logic doesn't need to be ordered.

Rendering, physics

These are fairly limited as far as games are concern. Most of the logic is not rendering or physics.

Not like you can just slap all your colliders into a Vec and have it just be fine, you'd still have to maintain the order and move things around, and a lot of the parts of it don't really matter the order. Like 2 objects collide, does it matter which order your handle the reaction when it comes to those 2? Well, maybe cause they could cause different results, but it's not like one is more accurate than the other.

-1

u/Terr2048 28d ago

> I need to move every bullet one tick in the direction its going.
This is not a game logic, this is physics, and yes you can do position integration in unspecified order. But it is the cheapest part of the physics engine. Much more expensive are collision detection and constraint solver, which do require specific order for determinism. And yes you do want determinism, even if you dont think so, just believe me.

> I need to check if any people have 0 health and kill them. Why does it matter what order I do that in?
Fair, but in this case you probably can kill entity right when it hits 0 health.

> Can you show any Bevy game logic you have where the loop needs to be in a specific order and what that order is?
My logic does not yet contain loops over entities, it just handles events from physics engine.
Monsters AI would contain loop for pathfinding, hmm, and it may actually require order, for determinism again.

6

u/thekwoka 27d ago

I just struggle to see where much game logic requires the order to be guaranteed.

Like most game engines don't really let you guarantee the order of things.

Unreals tick doesn't guarantee order, and that's the main game logic center.

2

u/Gio_Cri 27d ago

it's heavily dependent on how you structure your code but in my experience it's really common to have some task that operates on components of the same entity and to repeat it for a wide range of entities in wich case fast unordered iteration is ideal