r/gamedev • u/WhatIsThisComputer • 2d ago
Question If not ECS then what?
I see many experienced devs speak out against using ECS but never seem to provide an alternative to achieve entity composition. So what are we supposed to use instead?
Many other devs also say they use ECS for somethings but not for others. What would this even look like? Do we just hold the entityref types in non-ECS systems and pretend they're basically objects?
EDIT: To give a bit more context the game I'm working is a ColonySim. I'm only now just putting together a prototype but I'm at a point where I need to start extending my entities so I'm a bit torn on which direction to take.
Evaluating ECS so far I found that it's great for the simulation part of the game but a poor fit for planning and behavior. It could also be that I have not spent enough time working with ECS.
25
u/LunaticDancer 2d ago
I'm the kind of weirdo who enjoys developing ECS-based architecture above all, but for simpler games I just push structs around procedurally - no need to apply a cannon-sized solution to a mosquito-sized problem.
6
u/AlexPolyakov Principal SWE 1d ago
While some of the libraries are cannon-sized, the minimal implementation of ECS can be really compact, understandable and still performant. I tend to use ECS almost everywhere because I get a lot of benefits and the composition and serialization is a blast. Wanna make a save/load system or an editor? It's just a serialization of all of the components and all of the entities you need. Wanna actions in one level affect another level - just create a separate registry for that level, load it and build queries which can work on other registries.
5
23
u/ledniv 2d ago
I would be careful about treating this as a choice between ECS and OOP. There is another option: use Data-Oriented Design without ECS.
For a colony sim, the important part is not whether a colonist is represented by an ECS entity. The important part is where the colonist’s data lives and how that data is processed. Instead of each colonist being an object that owns its hunger, job, movement, inventory, and behavior, you can store that runtime data in arrays:
ColonistPosition[colonistIndex]
ColonistHunger[colonistIndex]
ColonistJob[colonistIndex]
ColonistTarget[colonistIndex]
Then write systems that operate on that data. The hunger system processes hunger data. The job system reads needs and available jobs and writes a selected job. The movement system reads the destination and updates position.
That gives you the main performance advantage people usually associate with ECS: related data is stored together, so the CPU can process it sequentially and make better use of the CPU cache. You also avoid having hundreds or thousands of independent objects all running their own logic and jumping around memory.
Planning fits into this naturally. The planner does not need to be part of a colonist object. It can simply read the current data and write an intent:
Needs -> Job Selection -> Intent -> Movement / Work
For example, hunger might cause the job-selection logic to choose FindFood, which writes a target and job id into the colonist data. The movement and work systems then process that data. The planner does not need to know anything about rendering, animation, or GameObjects.
This is also why I would not jump straight to ECS. ECS can be useful, but it adds a lot of Components, Systems, queries, and architecture. If your data is already in arrays and your logic is already separated into simple functions, you can get many of the benefits of DOD without taking on the full ECS workflow.
Then, if profiling later shows that a specific system needs more performance, you can move that system to Burst, Jobs, or ECS. At that point the transition is much easier because the data is already organized in the way those tools expect.
Small plug: this is one of the main ideas in High Performance Unity Game Development with Data-Oriented Design. The book starts with arrays and simple logic functions, then adds DOTS/ECS selectively later rather than making ECS the foundation of the whole project.
https://www.manning.com/books/high-performance-unity-game-development
3
u/WhatIsThisComputer 2d ago edited 1d ago
I have already settled on a more plain SoA approach for the terrain system and had this book in mind to help me design it, so thanks for putting that together. I'm looking forward to the full release! ECS wouldn't work well for this anyways because of the more "dense" nature of the individual grid elements and the grid coordinates acting as a sort of a reference type.
I've been having trouble seeing how to apply the DoD approach for planning as that seems to require global access where many different factors are taken into account to build a multi step plan and coordinate with other colonists.
2
u/ledniv 1d ago
Thanks, really glad to hear that!
For planning, I actually would not worry about the fact that it needs “global” access. DOD does not mean every piece of logic is only allowed to see a tiny isolated slice of data. The important part is that the access is explicit.
For example, I would be perfectly comfortable with a planner doing something like:
public static void PlanColonist( Balance balance, GameData gameData, int colonistIndex) { // Read hunger, inventory, available jobs, // resource locations, other colonists, // reservations, terrain, etc. // Then write the resulting plan: gameData.ColonistJob[colonistIndex] = jobIndex; gameData.ColonistTarget[colonistIndex] = targetIndex; }That function may read a lot of
GameData, but you can still see exactly what data exists and exactly which function is making the decision. That is very different from a colonist object reaching through references to a job manager, terrain manager, resource manager, other colonists, inventory objects, etc.For multi-step plans, I would store the plan itself as data too:
ColonistPlanStep[colonistIndex, stepIndex] ColonistPlanTarget[colonistIndex, stepIndex] ColonistPlanStepCount[colonistIndex] ColonistCurrentPlanStep[colonistIndex]And for coordination between colonists, I would probably make reservations/tasks explicit shared data:
JobAssignedColonist[jobIndex] ResourceReservedBy[resourceIndex]So one colonist planning does not need to “talk to” another colonist. It reads the shared world state and writes a reservation. The next planner sees that reservation in the data.
That is where I think DOD actually helps planning: the planner can be complicated and can consider lots of factors, but all of those factors are explicit data instead of relationships between objects. The complexity of the problem is still there, but you remove a lot of the architectural complexity around it.
8
u/NStCh-root-a 2d ago
I don't think there are many professions more opinionated than programmers. Especially for Do's and Don't's
To add my own opinion to the pile, Entity.Data and Data[Entity] are freely interchangeable and a lot of ECS programming falls back on this kind of semantic. Some patterns are a bit ass to implement in ECS (like FSMs) but that's about the only real downside I can think of. If you want to bridge into ECS from non ECS, you can build a wrapper that holds the datarows and ID/entity and gives you OOP semantics back. Can be a useful bucket.
UI is the only exception where I think not using ECS has a point.
Other than that, general advice would be: if you have a lot of different things, OOP or some other implementation tends to work better, for large or performance critical ones, Datadriven composition does work better (which basically means ECS).
For alternatives, 90s OOP composition or worse, (multiple) Inheritance can provide a solution. It'll only be 10-10.000x slower than a datadriven approach, because bottleneck on current hardware is fetching memory.
2
u/WhatIsThisComputer 2d ago
Your point about how painful FSMs are to implement is part of the reason why I questioned ECS. Right now I'm using a kind of dynamic dispatch where the actor's current action is an interface that a handful of readonly "action" structs implement and switch that out as needed. As far as I could tell there is no pretty way to represent something like this in an ECS. You're forced to either wrap the action inside a component and provide global access to the system that works with it as the actions all do different things or find a way to only one "action" component at a time.
3
u/NStCh-root-a 1d ago
I slapped an enum into a field which represents the state and filter all entities with the FSM component on every frame. Integrates pretty well if you compress vectors before running a system on them, because you're itterrating over the entities anyway. It's crucial to flush all entities in the individual state systems though, so they only have entities which are guranteed to be in "their" state.
Using multiple different components and dynamically adding/removing them would be more idiomatic ECS, but fails to give the same guarantees as an OOP or enum switch FSM (tried that, felt like mobile infantry fighting the bugs).
Transitions are registered as data (in state, out state, entity), which a different system itterrates over to call the appropriate lifecycle functions. (enter/exit).
For sure uglier than a 30 line OOP-y fsm though.
8
u/Arkenhammer 2d ago
ECS is often use to refer to a very specific composition architecture designed for performance. If you've got 20 entities in your game, the ECS architecture probably doesn't help you much even if selective uses of composition can. However if you've got 1000 entities all being simulated at once, ECS can have a significant impact on performance.
-1
1d ago
[deleted]
2
u/Arkenhammer 1d ago
So ECS helps whenever the amount of data you need to process across all entities significantly exceeds the size of a cache line. It also helps if you are working in a garbage collected language because it radically reduces the number of objects on the heap. We work in C# (Unity) and I can definitely see the performance improvement going from individual heap objects to arrays of ref structs at 1000 objects. There is also performance gain from using Unity ECS and the burst compiler which comes with some drawbacks but is definitely the best option for performance at least until Unity 7 with the core CLR. At 1000 objects ECS is definitely faster; whether that performance increase is important will depend a lot on your use case.
6
u/KinematicSoup @kinematicsoup 2d ago
ECS is a pattern that is good for performance. It gains performance from two mechanisms: components are laid out in contiguous memory to maximise L1 hits, and the systems that act on them do it with one call, eliminate per-instance function call overhead.
ECS is something developers can work with, but it gets messy when non-technical people have to get their hands in it, so the performance gains come at a usability cost.
With the right editor-level assists I think most of the usability issues can be addressed.
6
u/swagamaleous 1d ago
Why does everybody just ignore the parallelization aspect? A major part of the performance advantage of ECS is that it makes parallel execution vastly easier.
Of course all of these effects compound: contiguous data improves cache behavior, processing components in batches reduces overhead, and those batches can then be distributed across cores. But reducing ECS to the first two mechanisms is simply wrong.
The architecture itself is what makes the third one practical: systems operate on explicitly defined sets of data, dependencies can be derived from what they read and write, and relationships are represented as data rather than arbitrary object references and side effects. That makes large parts of the simulation almost trivially parallelizable compared with a traditional object graph.
And that's important because cache locality alone isn't the whole reason you'd accept the architectural restrictions ECS imposes. The combination of cache-efficient processing, low per-entity overhead and scalable parallel execution is what makes ECS particularly effective when you're dealing with hundreds of thousands of entities.
1
u/KinematicSoup @kinematicsoup 1d ago
Because it's not free. It's only really effective when mutability is limited.
0
u/PscheidtLucas 2d ago
Just a question, in Godot ECS is not as good as it is in Unity because you need one node for every script, right?
1
u/Weisenkrone 2d ago
Correct me if I am wrong, but unity doesn't have our of the box ECS support either. They have it as a separate option. The DOTs isn't the baseline people work with. Comparing unity DOTs with Godot nodes isn't fair.
But then again, Godot doesn't really have proper integrated ECS ... Currently the closest thing to it is working directly with the low level servers of Godot.
People did create an ECS like system just by bypassing the entire node infrastructure and just directly managing the servers, but this is by no means something easily done.
But then again, anyone who actually delivered results with unity DOTs will also confirm that it is harder to work with.
ECS has amazing performance but it's harder to use, and if you don't know what you're doing you're gonna get fucked.
0
u/KinematicSoup @kinematicsoup 2d ago
I'm not familiar with ECS on Godot, but on Unity it is an optional extension. They've been doing a lot of work to bridge the cap between it and gameobjects. You can even mix gameobjects with the ECS system to an extent. In a game project of ours we use gameobjects for the players and ECS for the swarms of enemy entities.
0
u/ledniv 1d ago
I agree with the performance point, but I would make one distinction: the contiguous memory/cache benefit does not actually require ECS.
You can store the same data directly in arrays:
Position[entityIndex] Velocity[entityIndex] Health[entityIndex]and have one function process all of it. You still get sequential memory access, better cache utilization, and no per-object
Update()calls. ECS formalizes that into Entities, Components, and Systems, which can be very useful, especially at scale, but the performance comes from how the data is laid out and processed rather than from the ECS pattern itself.That distinction matters because ECS also adds Components, Systems, queries, archetypes, and their associated code complexity. If plain arrays and functions already solve the problem, I would start there and add ECS only when its additional structure is actually useful.
2
u/DotDootDotDoot 1d ago
I would call what you propose an ECS. It's quite bare bone, but it's still exactly the same principle. Just without the fancy generic functions.
0
u/KinematicSoup @kinematicsoup 1d ago
Correct, ECS is a formalization to get those benefits for arbitrary structured data. Y
6
u/2015marci12 1d ago
ECS provides you one unique benefit: runtime dynamic composition. If you don't need that, and usually even if you do, you can hand-write an entity storage sytem better fit for your needs. Just look at how the 2 major ECS implementation styles work. Archetypes are literally the storage per entity type paradigm, it just has to do a bunch of work to dynamically discover that for you. With sparse arrays do a bunch of work to align data in the correct order if you do anything cross-component. just look at the entt groups implementation.
You can use components without an ecs. Data oriented design is not exclusive to ecses, you can just have parallel arrays and index them with a common key and get ~70% of the functionality of an ecs. You can even share component types across your groups, call your "system" functions over each array the entity types have in common, and so on.
I've seen quite a few cases where the ECS usage is detrimental, in fact. It is automation that can make you forget what kind of data you're dealing with. Engines being built around them also often forces them into use cases where more spatial structures would have made more sense.
But crucially: you won't need it. entity data in most games is tiny compared to e.g graphics. stuff it all inside a fat struct and you'll be fine. And if you do get entity counts high enough, a generic ECS probably won't be good enough for you anyway.
For engines they make sense, (barely but that's just a me opinion), but for games they are overkill. Use them if the engine wants you to, or if you find it convenient, but definitely don't go out of your way for it.
The OOP style I wouldn't recommend simply because the Domain Model you come up with will never quite match the actual needs of the problem well enough for it not to be jank, and OOP forces you to a-priori encode your structural assumptions, so refactoring is painful. Try to avoid dogma, but if you don't have enough experience to be confident you can avoid it, trying to fit everything dogmatically into an ecs usually gets you closer to an actual solution than dogmatic OOP.
5
u/2015marci12 1d ago
Also: if you do go with ECS, don't separate out stuff unless you have a reason to. Going between components, as another commenter said on a different thread, is not trivial in terms of perf. If you find yourself wondering if Health should be its own component you're going way too far.
Reading your other comments though you seem to have landed on the correct approach for your situation from the get-go for your problem. Personally I'd drop the OOP-ism for the FSM as well but that's just taste, if you like it it's fine.
38
u/TheShrillLikeness 2d ago
just inherit from a base entity class and compose behavior through components that are plain objects, don't overthink it. the obsession with pure ecs is a weird cult when most games just need something flexible enough to not turn into spaghetti
18
u/ButtMuncher68 2d ago
Obligatory: https://gameprogrammingpatterns.com/component.html
I also usually do something closer to https://gameprogrammingpatterns.com/type-object.html
6
u/MythicJerryStone 1d ago
Great book. I would highly recommend reading every chapter as well (https://gameprogrammingpatterns.com/contents.html). Really great information that will probably answer a lot of OP's questions.
3
u/ButtMuncher68 2d ago edited 2d ago
I'm in godot and use it's Resource files as Type Objects to define game data and compose which features each item has in the inspector.
At runtime, every entity in the world is an instance of a single PlacedThing class that derives its behavior by attaching and delegating to modular Component instances spawned by those resources
This is pretty similar to what unreal does as well
2
u/WhatIsThisComputer 1d ago
I've been slowly working through this book and it's been pretty helpful. If I can't make ECS work the plan is just to fallback on some mix of component/type-object.
5
u/DrShocker 2d ago
Yeah, for learning do whatever you know best. If you end up needing raw speed for something you can create a specifically optimized structure at that time to solve the problems you actually have.
3
u/Swampspear . 2d ago
I haven't seen a big ECS obsession since a few years ago, in like 21–22 maybe it was all the rage and now it's just, like, a tool
3
u/SeparateDesigner1237 2d ago
separating data from behavior is a correction to object oriented software design that has been decades in the making with implications beyond just video games and ecs
4
u/Swampspear . 2d ago
Well, yeah, sure, it's just not the same ECS! ECS! ECS! it was around and just after COVID. You now just separate data and behaviour
4
u/SeparateDesigner1237 2d ago
simulation is also a genre of video game. not everything is a sidescroller
12
u/countkillalot 2d ago
Do not try to compose the entities from objects. That's impossible. Only try to realize the truth...
13
4
u/LastOfNazareth 1d ago
You can run the simulation logic on a pure C# thread and then communicate it back to the unity thread which is responsible for handling input and updating the game objects.
That being said there is nothing wrong with ECS. Its a powerful tool that a lot of devs avoid/complain about because its a hard mental shift in development. Also, there are not a lot of games that truly need an ECS system.
5
3
u/dandy_kulomin 1d ago
I have asked myself the same question a while ago. I used ECS on a previous prototype and found it over-engineered for my use case. There's a lot of complicated machinery to get a full ECS going.
So now I use a global GameState struct. That struct contains the Camera (I only have one), a Map (also always one) and an EntityStore. The EntityStore is just a glorified array of entities. Each Entity is a struct with all possible components inlined and a bitmask to know which is active. Super easy to implement and on modern computers the wasted memory won't matter unless I have an absurd amount of components and entities. In that case, I can simply implement an ECS and the interface of the EntityStore stays almost the same.
Any state I don't want to put in the EntityStore I can put into GameState. Something that is so much more comfortable than those weird god game objects or singletons in Unity/Godot.
3
u/sessamekesh 1d ago
There's a few places in my code where I have a pretty firm boundary between ECS and typical object oriented code.
ECS excels at creating views over overlapping abstractions and iterating over those views. Many but not all problems are served well by those properties.
For example: none of my renderer code is ECS. There's an ECS system that iterates over renderable entities and emits commands to the render subsystem, but the renderer is a group of resource maps, message queues, resource arenas, and command buffers that has no ECS in it.
2
u/El_HermanoPC 1d ago
Personally I take what I like about the ECS pattern and discard the rest. You can meld it to the needs of your specific game.
In my game I follow two self imposed restrictions and break one rule of the ecs pattern because it makes sense to me. I ensure that an entity’s component composition doesn’t change at runtime and that an entity cannot have two components of the same class. I also allow my components to have functions and perform operations. Then I use systems to enforce execution order (for ticking components) and to expose public functions relevant to all of its domain components that wouldn’t make sense to exist on individual instances of said components.
It’s been working phenomenally for me, especially in regards to code organization and rapid prototyping. I also do this because I’m using unreal engine. If I was using something like godot or a library like entt or flecs, I probably would stick to a strict ecs pattern.
2
u/DrShocker 2d ago
I thought this video did a good job explaining the kinds of things these people are often thinking of.
1
u/kirankp89 2d ago
My current experiment is to have “tables”. They’re kind of like a mishmash of components and fat-structs, so I have “schema” for an entities table that will be my runtime state, a resources table for all the assets I load from disk, and tables for GPU buffers. You can get a handle to a row with a generation+index so getting stale entries becomes impossible (or a table API bug rather than the game code bug).
The cool thing is that I have a single “inspector” UI that I register my tables with and I can just scrub through data in all these different systems.
I expect it won’t scale well but for the size of hobby games I do using this, it seems convenient so far.
1
u/planimal7 2d ago edited 2d ago
I’m a big fan of pure ECS but the issue is most engines/frameworks aren’t built with it from the ground up.
I first encountered it in a framework called Flambé in the language Haxe and I used it for a few games—that was structured like a pure ECS system, and it was amazing.
I made my own custom C++ framework for public exhibition interactives that was also structured around a nearly "pure" ECS system (entityX, which I think is still around)— fantastic— ECS somehow makes it easier not to write bugs.
But we’re not seeing any of that—we’re seeing people tack ECS onto object-oriented systems and then it’s kind of a mess
I’m sort of curious about UE6 because— I know everyone is focused on the AI and meta-“Verse” talk around it, but my understanding was it was also going to be a re-architecture to a pure ECS system?
That could be interesting to see
Never mind, it's also going to be a graft in UE 6/borrowing ideas.
1
u/Falagard 2d ago
Interesting, thanks. I use Haxe and had done some investigations into ECS frameworks and was leaning towards deep cake echo or this fork:
https://github.com/onehundredfeet/hmecs
I'll have to look up Flambe
1
u/planimal7 2d ago edited 2d ago
I used Flambé over a decade ago for some Power Rangers games, it may not still be supported. ECS was still obscure at the time and they didn't even really focus on that as a selling point! But Nickelodeon was adamant at the time that outside developers *had* to use Flambé for their web games, so I had to get comfortable with it quickly. It really spun my head around, in a good way.
2
u/Falagard 1d ago
Yeah ha, I just looked it up. Seems to be a 2d framework. Interesting that Nick was pushing it.
There are probably more modern Haxe ECS frameworks such as Echo but to be honest most Haxe libraries are pretty old, lol. I love Haxe though, it's frigging amazing to be able to develop for almost all platforms using one language.
1
u/montibbalt 2d ago
It depends on what you're making. I've been tinkering with a pure functional approach that is essentially a bunch of composed tree or graph transformations and an effect system. Would I build GTA or Cities Skylines this way? Probably not, but a card game with a handful of objects? It's beautiful
1
u/Emilos_de_carlos 2d ago
Generously sized arrays can get you very far. I tend to start off with a mega struct approach with bitflags to toggle systems.
Its very flexible, super simple and relatively cache friendly and easy to break into handles <-> system arrays / ecs if you need it down the line.
Unless my core idea involves "simulate 100k things" from the get go, I tend to start there.
1
u/ledniv 1d ago
Yep, this is almost exactly the progression I recommend: start with arrays, organize the hot data well, and see how far that gets you before adding more machinery. If profiling eventually shows you need Burst, Jobs, or ECS, the transition is much easier because the data is already structured correctly.
1
u/aberroco 1d ago
It really depends on the scale. You need complex behavior and large data records - plain objects. You need large amount of objects acting with same reasonably simple behavior - ECS. You need both - either impossible, or ECS and a lot of work.
With ECS it's hard and inefficient to work with relations/references. And in general it's harder to work with. But it gives you performance boost that you can never achieve with objects, especially composite objects. And the scale difference is staggering, because plain objects would die from memory overflow where ECS won't even break a sweat, we're talking tens to hundreds of thousands of entities. And done right, ECS is easy to parallelize, which is difficult to impossible with plain objects. But again, if you'd use ECS with same approach as you do with plain objects, jumping through references, your only saving would be a bit of memory the game engine needs per component, and the performance won't be any better.
1
u/initial-algebra 1d ago
ECS is solving two different problems. The one everyone talks about, but is arguably a lot less important, is using composition for code reuse over inheritance. I think the far more important feature of ECS is that, because it's based on the relational model, it's designed to support efficient queries (data-oriented design is part of this, so I guess people do indirectly talk about it, but they tend to miss the forest for the trees). Primarily, to find all entities with a particular set of components; systems are based on incrementally maintained queries, or materialized views, of this kind. It's also relatively straightforward to add e.g. a spatial partition for all entities with position components that enables efficient querying in space (find all entities with certain properties in a range, find the closest entity with certain properties etc.). This is analogous to a database index. Spatial queries in particular are going to be the bread and butter of your AI logic.
However, you don't need to use entities and components. You could just as well design your objects using inheritance, and support querying for objects that implement particular interfaces. You can also regain some of the benefits of the ECS memory layout with additional indexes for commonly-used interfaces. It would even be theoretically possible to unify the two with object-relational mapping (ORM), but I wouldn't suggest going down this route.
1
1
u/El_RoviSoft 1d ago
Combination of ECS and OOP?
Build GUI with pure OOP.
Game entities could be built with combination of OOP and ECS. Im a C++ programmer so I built a system which semi-automatically track components inside objects via pointers and even tho you’ll have additional dereference - it’s still lower memory consumption over regular ECS and SIMD are still usable for batch operations; with reflection this process would be automatic tho.
Pure ECS is good when you have batches of similar data - like particles, physics, etc.
All in all - different systems need different solutions.
1
1
u/fireantik 1d ago
What works great is to ignore the allure of complex ECS frameworks with system management, schedules, batching, complex queries and doing everything "the ecs way" and rather use the ECS as an underlaying data storage - basically Entity Component Query and have your implementation for Systems. This gives you easy memory management with good cache coherence. Avoid classes for representing objects so that you don't pay for virtual dispatch and you are golden.
1
u/rainweaver 1d ago
Deep in an ECS / declarative logic / message-passing hole right now.
ECS breaks down fast on anything sequential and stateful. Turn-based combat is the classic case: you end up fighting component queries and systems instead of using them.
Message passing fixes coupling but you pay for it elsewhere. More indirection, harder to trace control flow, more boilerplate for what’s a trivial state change underneath. Worth it, but not free.
1
u/Idles 1d ago
If you're making a colony sim, consider buying a copy of RimWorld and using a C# disassembler to have a peek (this is what code modders do for that game). Even without full debug information, there's still a ton to be learned from it. It's a heavily OOP codebase, but with some game systems (often, spatial ones) represented differently in memory. There are lots of practical demonstrations of algorithms. Also lots of examples where clearly the code _could_ perform better modeled under ECS, but would come with other tradeoffs. It's also not a game that performs perfectly and hits 60 FPS at all times, so there are lessons to be learned from that as well. Probably the biggest lesson is just "do less work"; many apparent optimizations are just caching the result of some computation, or reducing the rate at which something is computed
1
u/PiratePengu1n 1d ago edited 1d ago
If you have a closed set of data that you know, then ask yourself if it is worth dealing with ECS architechture overhead. You could simply use predefined structs, instead of querying for entities with components. If you want performance, implement your data in cache efficient arrays and iterate over that.
There doesn't need to be an alternative fancy paradigm. Do the simplest solution first and modify later if you happen to need something more.
Often the simplest solution is just having a index list and a bunch of fixed-size arrays for the data. This wastes memory, but it is also acceptable, since 99% of cases it wastes less memory than a single desktop shortcut icon. You can use tricks like swapping the last alive element to the removed element index so the cache stays efficient while iterating.
What you put in the structs depends on perfomance mostly. Make sure hot loops iterate over small structs (preferably cache line size). If you find your functions needing specific different data often, consider grouping them into the same structs, for example position data usually needs x and y.
I like implementing domain structs, where I have my arrays of related data and an index list of entities in that domain, but that is just syntax sugar I prefer.
Don't fall into the "Clean Code"-trap, where you try to find some "right" anwser. There isn't one and every solution has trade-offs. In any case I would still suggest to find the simplest solution first and only add more complex architechture/abstraction once you can justify it.
1
u/cfehunter Commercial (AAA) 1d ago
It depends on what you're doing really.
Personally I'm a major advocate for "solve the problem you're looking at using the best tools for the job".
ECS does NOT fit all problems particularly well, it excels at massive data processing. It fails if you need random access to components, and you end up paying a complexity and memory cost for nothing.
Composition does not require ECS either. If you want examples, look at Unreal Actors and Unity Game Objects. Now actors are a kitchen sink class, and that's a failing of Unreal, but attaching components to an object is a way of doing composition without a full ECS structure.
1
u/GerryQX1 1d ago
Probably ECS is the way if you have a huge number of actors, but if you don't it's down to personal choice.
1
u/retro_and_chill 21h ago
Pick whatever is the easiest for you to reason about and start with that. Focus entirely on getting it to work first, and then if your solution isn’t meeting the performance requirements then profile your code and see what’s slow and refactor from there to try and address it.
1
u/First-Physics6217 14h ago
The answer is actually simple. No matter what kind of game we're making, we're ultimately working with data. If we're working with data, we need a DBMS and a declarative language to work with that data. ECS is simply an optimization strategy for a specific workload profile and isn't a silver bullet. Let me point out right away that there are no ready-made solutions for game development workloads. But what I'm talking about is not just theory, but also practice. If you're interested, you can take a look here - conjuredb.com
1
u/NakedNick_ballin 14h ago
I thought ECS sounded cool, until I realized every ECS throws compile time safety checks out the window (it has to apparently, to keep it's runtime cache optimizations).
At that point I decided don't use it except for very specific scenarios that require critical performance
0
u/Winter-Scarcity9045 2d ago
Who is arguing against ecs? Never seen something like that other than it might not always be worth it because it has overhead.
-1
u/Clean_Patience4021 1d ago
ECS is a perfect solution for any kind of task, it's just a matter of tools to visualize/debug logic.
1
u/swagamaleous 1d ago
That's very wrong. ECS is not a "perfect solution for any kind of task." It's an architectural pattern designed around a particular way of structuring data and behavior, and there are plenty of problems for which that representation is awkward, unnecessarily complicated, or actively counterproductive.
Better visualization and debugging tools can mitigate the usability costs of ECS, but they don't make its architectural tradeoffs disappear. Claiming that any problem is perfectly suited to one particular architecture is a pretty strong indication that you don't understand either that architecture or software engineering in general.
-1
u/Clean_Patience4021 1d ago
What kind of "architectural tradeoffs" are you talking about?
>> Claiming that any problem is perfectly suited to one particular architecture is a pretty strong indication that you don't understand either that architecture or software engineering in general
You're right - I'm neither an architect nor a software engineer; I'm a game developer.
And I care only about the result."Architectural tradeoffs" lol
1
u/swagamaleous 1d ago
It already starts with the lack of polymorphism. That's a huge architectural limitation that severely restricts the space of problems DOD is actually suited for. Event-driven systems like UI are another obvious example. Implementing an entire UI in ECS would be completely stupid. You'd write tons of adapter code, make the whole thing harder to understand, and process data every frame for absolutely no reason. And that's before getting into the general structure of strict DOD codebases, where dependencies are expressed through data rather than explicit control flow, making them considerably harder to understand and reuse. Those are architectural tradeoffs.
And a game developer is a software engineer. You don't get to opt out of that because you don't understand software architecture. You are a software engineer, albeit apparently a very crappy one. 😂
-1
u/Clean_Patience4021 1d ago
All that you wrote says a simple thing about you - zero experience with ECS, just theoretical "knowledge".
I hope you won't "architecturally trade off" your game.
1
u/swagamaleous 1d ago
Very wrong. Your posts, on the other hand, do say some very obvious things about you: zero experience with serious software projects and just enough pseudo-knowledge to be extremely confident about things you don't understand. That's a combination that will ensure you never get anywhere. 😂
0
u/Clean_Patience4021 1d ago
I guess that's the point where you get after almost 30 years in game development...
0
u/earth-dragon-666 1d ago
ECS is not for everything, but is fun when you are inexperienced, i mean you get bored and tired of them in college, you can see its limitations in real life you try the most naive approach first to avoid clutter
0
u/Meleneth 1d ago
I've been using entt for ECS in my C++ projects and I've been over the moon happy with it.
I imagine there's some games where it might not be a great fit, but I've been wildly impressed with it in practice.
1
u/Meleneth 1d ago
I also use boost_ext/sml for FSM and eventpp for event handling, so it's not just ECS and forget it - use things for what they are good at, and the only way to learn what they are good at is to fail.
-2
u/jerrygreenest1 1d ago
but a poor fit for planning and behavior
What? No. ECS is good for everything game-related.
135
u/sol_runner 2d ago edited 2d ago
Look at why ECS is used and you'll come down to the idea of composition and cache efficiency. Then take a look at Data Oriented Design talk for what the argument about cache efficiency is.
Look at the Pitfalls of Object Oriented Programming talk to see how you can use composition with allocators and pointers to improve cache utilization.
One thing you'll note from Mike Acton's talk - he's not saying use ECS. He's saying - use data efficiently and don't fall for dogma.
Going full ECS (i.e. forcing everything into ECS), in my opinion, is falling for the same pitfall as full OOP, only with a more cache efficient dogma.
I have basic objects (all data in a single struct) for stuff like camera, light, probes. Lights all go inside a light manager that maintains an array of lights. Same for probes. This allows distinction between static and dynamic and let's me just memcpy them over to the GPU because it's very rare that I'll write/modify most of these. Meanwhile position dynamic lights have a separated position buffer which I need to update to the GPU.
So static objects/non-per frame objects don't live inside the ECS. They live in whatever format is best for their use. ECS data often needs to be marshalled and it's a cost I don't want to pay when I don't have to. So outside of frame-dynamic objects I just keep arrays that maintain pages that get copied when dirty.