7 ms·
I can't help but be heavily skeptical of approaches to a (traditional) roguelike that use ECS. The idea is very entrenched in the rust gamedev community, but fo
by halvnykterist 5y ago
I can't help but be heavily skeptical of approaches to a (traditional) roguelike that use ECS. The idea is very entrenched in the rust gamedev community, but for a turn based tile based game there's extremely little benefit and a lot of added complexity. Bob Nystrom has an excellent talk on roguelike architecture [0] and rust as a language itself doesn't prevent any of these approaches. If anything, the existence of sum types as enums make many of them all the more powerful.
[0]: https://www.youtube.com/watch?v=JxI3Eu5DPwE https://www.youtube.com/watch?v=JxI3Eu5DPwE
- ffhhj 5y agoECS is nothing compared to the eclipsing aspects of games that a gamedev has to solve: * Structures persistency (save/load/stream) * Optmized seamless world expansion * Where to get assets and content from (licenses, generated, etc.) * Affordable and responsive multiplayer
- anothernewdude 5y agoThe reason ECS is so common for roguelikes, is because they provide a simulationist experience. The main draw of several roguelikes is having enemies on the same footing as the player, and a good way to do that is to have them use the exact same systems. It's also a good way to make interesting interactions between environment and objects happen. Also, having collections of entities works well for Rust's borrow checker.
- pizza234 5y ago> The idea is very entrenched in the rust gamedev community I don't think it's very entrenched, although it's certainly present. The reason for this perception, I guess, is that Bevy (which is based on ECS) is all the rage. Amethyst was ECS as well, but it's dead now (and Bevy is essentially its successor). Veloren is ECS though, and active. The remaining major engine (AFAIK) is RG3D, but it's not ECS. Macroquad provides a node graph (but it's a smaller engine). All the other game engines are comparatively small, and they don't provide storages/ECS engines. (edit: added Veloren)
- halvnykterist 5y agoI think Kyren's 2018 CustConf talk [0] caused it to become popular earlier. Arewegameyet has an entire section for ECS crates, with 10 different entries. I'm aware of multiple smaller homegrown solutions, too. https://kyren.github.io/2018/09/14/rustconf-talk.html https://kyren.github.io/2018/09/14/rustconf-talk.html
- modernerd 5y agoI think Jonathan Blow's take is right: > ECS only starts to make sense when you are big enough to have multiple teams, with one team building the engine and the other using the engine to make the game; or you are an engine company and your customer makes the game. If that is not your situation, do not rathole. https://twitter.com/Jonathan_Blow/status/1427358365357789199 https://twitter.com/Jonathan_Blow/status/1427358365357789199 Most of the arguments I've seen for ECS in Rust suggest it helps to work with memory management/borrowing. For example, here's Patrick Walton's take: > There's a reason why Rust game engines all use ECS and it's not just because the traditional Unity OO style is out of fashion. It's because mutable everywhere just doesn't work in Rust. Mutex and RefCell explosion. https://twitter.com/pcwalton/status/1440519425845723139 https://twitter.com/pcwalton/status/1440519425845723139. And here's Cora Sherratt's discussion of ECS options in Rust: > So why do you need one? Well all ECS developers will claim it’s largely about performance, and show you a mountain of numbers to back it up. The more honest answer probably comes down to the difficulty of making ownership work in Rust without some type of framework to manage the transfer of ownership for you. Rust punishes poor architecture, so ECS’ are here to help. https://csherratt.github.io/blog/posts/specs-and-legion/ https://csherratt.github.io/blog/posts/specs-and-legion/ (This post also has the best visualisation and explanation of an ECS I've read.) I've read Hands-On Rust and you could definitely implement the game without an ECS. But at the same time it was useful to play with that pattern because it's in common usage in the Rust community. (Bevy also makes heavy use of it, for example, where it feels pretty lightweight because they made some good design decisions: https://bevyengine.org/news/bevys-first-birthday/#bevy-ecs https://bevyengine.org/news/bevys-first-birthday/#bevy-ecs.)
- kvark 5y agoRust has ways to deal with mutability. Three-rs [1] uses a classic scene graph tree, like ThreeJS. It's based on Froggy [2], which is a general low level primitive for building a "traditional" topology of the classes. [1] https://github.com/three-rs/three https://github.com/three-rs/three [2] https://github.com/kvark/froggy https://github.com/kvark/froggy
- halvnykterist 5y agoI think the idea that you need something like ECS to deal with mutability everywhere is a misconception. Traditional architectures you'd use in C or C++ are all going to more or less have a tree-based ownership graph, and these translate very well to Rust. Long-lived pointers to things that you don't own are a great way to get UAF errors or similar, and having a collection of entities you can look up by ID is a common pattern to use outside of ECS. > But at the same time it was useful to play with that pattern because it's in common usage in the Rust community. And in doing so it perpetuates it. ECS is a good solution for lots of problems (in particular I don't agree with jblow's take) but parading it as _the_ way to make games in Rust feels a lot like how OOP has been championed in the past. If you're going to teach someone how to make games in Rust, doing with extra patterns that don't add much other than complexity (in this case) doesn't seem like a good strategy for teaching or introducing people to the language.
- cardanome 5y agoECS is for me the natural way to design games. I wouldn't even know to design them any other way. I started game dev with love2d which is a pretty minimalist framework. When I participated in a game jam, I needed a very flexible system that would allow for quick prototyping and would handle many different entities. I ended up writing something which I later realized would be an ECS system. It worked great and I would copy it over for other games. I know there is that ECS-faction that is talking about performance benefit and stuff but honestly I don't care. I don't even know most of the terminology these people use. I just use ECS to structure my code while being very flexible. I use OO in other domains but it would never occur to me to structure games that way. OO just feels way to brittle for a domain where you do lot's of experimentation and can't really know what you will need and how your entities might end up looking.
- pjmlp 5y agoECS and OOP are sides of the same coin, although apparently it is only visible to those that read SIGPLAN papers. "Component Software: Beyond Object-Oriented Programming" https://www.amazon.com/-/en/Clemens-Szyperski/dp/0201745720 https://www.amazon.com/-/en/Clemens-Szyperski/dp/0201745720 One of the first publications on the matter.
- deleted 5y ago[deleted]
- dgb23 5y agoNot sure I get what you mean. As I see it, ECS has more of a relational character. Feels more like data pipeline than operations/messages on objects. I think the mental model matters the most here and that depends on how you think of objects. But then any attempt of defining OO objectively seems to be futile, there are conflicting historical and contemporary notions plus a whole bunch of jargon on top, depending on who you're asking. As an example you could say that Scheme is more object oriented than Java or vice versa and there would be valid, typically cultural reasons for each. In terms of ECS what kind of happens is that, yes, you have a model of an entity and can think of that as an object, but that is a projection of a set of components or a relation. You're not really talking to the entity as a whole all that much anymore. And it's not just "it satisfies this set of interfaces" either. Your systems literally define data transformations, each on a focused set of related components that matter to a system, which seems kind of the inverse of hiding data behind object interfaces.
- rtoway 5y agoBevy, while an ECS based library, can easily be used to program a game in a more traditional way (excuse my formatting): struct Player { health: Health, } // ECS style fn update_player(query: Query<&mut Player>) { } // Traditional "OOP" style impl Component for Player { fn update() { } }
- thebracket 5y ago(Author here) Bob makes some good points, so I'd like to share my $0.02 on the ECS debate. The posters below who point out that a lot of Rust setups use ECS to avoid mutability issues are correct (although internally Bevy is an ECS that maps its own node graph) - and that certainly helps - but it's not the whole picture. I think it's important to separate the EC from the S in ECS. Entity-Component storage is basically a fast, in-memory database. It's a great way to store global state, and provides for really efficient querying. Using it as a database gives you some advantages: * Composition over inheritance (especially in Rust, which doesn't really have inheritance - although you can fake it with traits). It becomes easier to glue on new functionality without realizing that you need to rearrange your object tree, and there's real performance boosts to not doing virtual function calls or dynamic casting to see what an object is. * Replication; if you want to replicate state across multiple nodes, a good ECS can really help you. * Mutability; as mentioned above, you don't need mutable access to everything at all times, and your code is definitely safer if you have explicit mutability control. * Surprising flexibility; Rust EC setups typically let you put anything into a component. I have one slightly crazy setup that stores an Option<Enum> as a component with the enum featuring a number of different union setups. So what about systems? Sometimes systems have some real advantages: * For simulation type games, it's great to be able to add a simulation feature and have it apply everywhere. For example, when I added gravity to Nox Futura it instantly worked for player characters, NPCs, and objects. (It also worked on flying creatures, killing them instantly - but I fixed that). * It really helps with parallelism. Your systems declare the data to which they will write, allowing the ECS to order your systems in such a way that you get parallelism without having to think about it too much (especially in Rust). * If you're in a team, it's a great way to break out work between team-members. * It's often helpful for finding bugs, because functionality of one type is localized to that system. You can get the same result by being careful in a non-system setup. Sometimes, systems aren't so great. It can be really tricky to ensure that linked events occur in the correct order. You can make a bit of a mess when you want something to work one way for one type of entity and another for a different type. But here's the thing: the systems part is optional. You can easily have your main loop query the EC data-storage directly and work like a traditional game loop - without losing the benefits of the storage mechanism. If you prefer, you can attach methods to components and call those. Or you can build a message-passing system and go that way. There's no real right way to do it. Once you've got the hang of your ECS's query/update model, you can tailor the game logic however you want. (I happen to like systems, but that's a personal choice more than a "you must do this" belief). (Edit: My formatting was awful, sorry.)
- nulldata 5y agoECS might have a higher initial complexity overhead, but once you scale up, ECS can really help you keep complexity at bay. There's a great talk [0] from the Overwatch developers where they talk about how they built Overwatch using the ECS architecture. They barely mention performance at all, but instead talk about how it helped deal with complexity. [0] https://www.gdcvault.com/play/1024001/-Overwatch-Gameplay-Architecture-and https://www.gdcvault.com/play/1024001/-Overwatch-Gameplay-Ar...
- wlamartin 5y agoAt https://story.ai https://story.ai we're using Shipyard ECS to model the data behind the structured editing experience. Although at first I was suspicious, I've come to realise it's an extremely elegant way to handle our complexity. Consider for example, a snippet that reads along the lines of: when there is a new stripe charge send a slack message It's very nice to attach something like a "ScopeContext" component to each Entity that has a "TokenLine" component, that is a Vec of EntityIds that are sharing values into scope. Very easy to model and add without interfering with other data structures.
- deleted 5y ago[deleted]
- bcrosby95 5y agoI've coded MUDs for about 25 years now, and I use a lot of these patterns in my code. Nice to see someone else thinks its useful. I think a key strategy that is not touched on too much in the talk is flexibility in your effect system, whatever your effect system may be. It should be trivial to take an effect (heal, damage, stat boost, etc) and let anything in your game apply it to actors with just a few lines of code: items, spells, tiles, regions, the whole game, etc. As an example, lots of games will specifically give certain classes hps/mana/better chance to hit/new skills as they gain levels. I just use my effect system for this. Now I can have items that give skills too, because items use the same effect system.
- dkersten 5y agoDo you know of any articles or resource that go into your style a bit deeper? I'd love to hear more on how your effects system works and how that compares to a component-based or ECS architecture.
- bcrosby95 5y agoI wouldn't call it an architecture, more a goal. When I say effect system, I just mean how you apply various effects to your players/npcs. I wouldn't prescribe how to get there - it could just be your component system. For myself, I go at it from a baseline approach: effects are one of the fundamental building blocks of the game. As much as possible, if something in the game does something, I try to do it through effects. This includes skills, spells, weapons, armor, commands, AI, systems, etc. If everything does everything through these effects, you can make anything do anything in response to anything without having to special code it. I don't make it that flexible for various reasons. But I think it's a good place to be philosophy-wise for something like a MUD. As an example from the video, there is an Attack, Use, and Defense class that Items use, with specific attributes assigned to them (armor, dodge bonus, min damage, max damage, etc). With effects, you might have a list of effects that trigger on wear, on attack, on activate, etc. A common effect is a damage effect. You could make an item that damages someone you hit with it (typical weapon), damages you when you wear it, or damages a target when you activate it. But you could do this with literally any effect you programmed for any spell or command. You can make an item heal you when you wear it. Or provides a heal over time when you wear it. Or heals your target when you hit them with it. You can even combine them. I could make an item that, when worn, transfers everyone in the game to the wearer's location and kills them. I could do the very same thing for a spell. Or when someone enters a tile. Or a command. Etc. I can do this because there is an admin command that can transfer everyone in the game to a location. It does this by effects. I have an admin command that can insta-kill anyone. It does this by effects. I can assign these effects to any place that uses effects. I arrived at this solution because, when working long term on MUDs, I found myself coding the same thing over and over for different subsystems for different content creators. Ultimately my mantra turned into "non coders shouldn't need coders to build new, cool things" and this is how I went about doing that.
- verdagon 5y agoIndeed, ECS might not be a good fit for turn-based games. We explored the topic quite thoroughly in [0] and came to the conclusion that Roguelikes are more suited for something in-between ECS and OO, named just "EC". ECS is better suited to real-time games, or games which have a lot of iteration over large arrays and parallel (not as in multi-threading, but as in, batchable) computations, where ECS can really shine due to its data layout. Most turn-based games like roguelikes don't iterate over large arrays in the same way. With EC on the other hand, you're a little more free to group components into their parent entity, and use interfaces (traits), which can make things more understandable. Whether idiomatic Rust likes EC is an open question though. Idiomatic Rust tends to dislike heap allocation and virtual dispatch, both which can make life a lot easier for Roguelike games, which tend to prioritize flexibility and features, and don't need the data-oriented optimization. Additionally, a lot of people suggest using ECS in Rust because that's the only architecture that the borrow checker doesn't fight you in, for various reasons. [0]: https://www.reddit.com/r/roguelikedev/comments/i3xekn/ec_vs_ecs_for_roguelikes/ https://www.reddit.com/r/roguelikedev/comments/i3xekn/ec_vs_...
- brundolf 5y ago> Idiomatic Rust tends to dislike heap allocation and virtual dispatch Maybe Rust the way most people end up writing it, but Rust the language has no issue with these things. I think the idioms come more from the fact that Rust empowers you to avoid these things, not that it's poorly-suited to them.
- verdagon 5y agoI think both are true. Rust is poorly-suited to certain architectures, and empowers us to use other architectures. Rust's idioms take both its strengths and weaknesses into account. It can make up for it in other ways, but in my experience, Rust is poorly-suited for Roguelike architecture specifically.
- brundolf 5y agoBroadly speaking yes, it's good and bad at different things. Specifically ownership is obviously the big challenge, and is often associated with heap allocation and dynamic dispatch. But the latter two in their own right are not any harder or more limited in Rust than anything else.
- dkersten 5y agoECS is entrenched in rogue development in general, at least going by r/roguelikedev. It (or at least a component architecture, if not fully blown ECS) seems to be a good fit since these style of games tend to have a lot of composition (in items, effects, behaviour). Personally, while my experience is a bit limited, I quite like the ECS style. It just makes logical sense to me as a way of composing entities from different parts. The implementation details (cache friendliness or whatever) are not important to me since I've never made anything of a scale where it matters, but that style of developing/designing how things act and interact is logical to me personally. I haven't watched Bob Nystrom's talk yet though (or, actually, I may have, it seems familiar, but I don't remember any details. I plan to watch it tonight). With that said, many people find the Godot style more natural and it certainly is nice too.
- codetrotter 5y agoI’ve not yet used Godot. What is the Godot style like? And are there some documents about it and example code of it?
- meheleventyone 5y agoGodot uses a hierarchy of nodes, you can think of it as one step further than entities being composed of components. The hierarchy and how nodes operate on it define the game. In practice though you end up with things that look very entity like IMO so it’s not that different.
- dkersten 5y agoSo after watching Bob Nystrom's talk: I have actually seen it before, its a very good talk and I definitely agree with him and with his solutions. But, as he even says himself, its not a replacement for ECS if you need that kind of complexity, just that for what he was doing its overkill and that depending on what you're doing it may also be overkill, and that his simple solutions solved those particular problems really well for him, in a way that is much simpler than ECS. I completely agree with that. Of course, he didn't go into much detail about when you might need an ECS other than "when you have something graphically complex" (paraphrased), I would extend that to include: 1) when your entity modelling is becoming a performance bottleneck, 2) when your entities are becoming complex enough that you want to have anything become anything; you could build on what he proposed to make this work by making more complex and flexible components but at some point using an ECS may well just save on effort, especially if you use an existing one like what Bevy has or EnTT in C++, 3) your logic can naturally decompose into systems that operate on sets of components and you have a lot of components to operate on. In all three of my additions, scale is a factor. If you only have 10 entities, then you can solve all 3 with less effort in other ways. Similarly, if you only have 10 components or 10 systems, its not such a big deal. If you have tens of thousands of entities, a hundred components and a few dozen systems, then an ECS is probably the right choice. For me, I like the ECS style. I also like the solutions proposed in his talk. I'm also not planning on writing my own ECS ever but using existing ones (I've tinkered a lot with EnTT in C++).
- jyriand 5y agoSorry, what is ESC?
- avinassh 5y agoI think you meant to ask ECS which gets referred a lot in this thread. ECS - Entity component system [0] - https://en.wikipedia.org/wiki/Entity_component_system https://en.wikipedia.org/wiki/Entity_component_system