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.
ECS 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.
ECS 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
One of the first publications on the matter.
> There has been an explosive growth in component software technologies since the first edition of this classic book was published. The advent of EJB, J2EE, CORBA 3, COM+ and the .NET framework are evidence of a maturing market in component software that goes 'beyond OOP'.
This book seems to be discussing distributed object systems, which is a sense of the word "component" that has little or nothing to do with the sense used by game developers in reference to the ECS architecture. Distributed object systems are designed to enable an object-oriented design philosophy to be used for a system which spans multiple address spaces or machines. The entity-component-system architecture is a methodology for organizing data layout and program functionality, usually within a single address space on a single machine, where the data associated with a given entity is spread across multiple components or subsystems and associated by index relations (much like a relational database), rather than being all grouped together in one place (as encouraged in OOP).
These two concepts (distributed object systems and ECS) are designed to solve different problems, they are generally used in different scenarios, and they apply at different levels of system organization. There is so little resemblance between the two that I have to conclude someone calling them "sides of the same coin" is either completely unfamiliar with one of them or is being deliberately misleading.
Not 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.
I 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
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.
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/ (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.)
I 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.
Rust 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 [2] https://github.com/kvark/froggy
I really like Jonathan Blow, but I sometimes wish he would substantiate these kind of claims more so an outsider could learn what he is actually saying and why. I've watched a lengthy video of his on that topic but I didn't get out anything other than "you don't need it". No concrete implementation cases where it gets in the way or how a category of problems is better modeled in a different way.
I am beginning to suspect people like him because he speaks slowly with lots of repetition and in a relatively accessible way.
Sure he might have lot's of valuable experience but every video I have watched of him was like a 1 hour rambling opinion piece which could be 3 minutes of actual content.
His video reaction to the rustconf ECS video had some good points but they were very nitpicky and not really justifying the length of the video at all.
On why ECS gets in the way for him:
> Because it is far more complicated, thus takes far more work, than what you actually need to do. That work has a large opportunity cost. https://twitter.com/Jonathan_Blow/status/1427378984145154048
To me this has more nuance than, āyou ain't gonna need itā. I don't think āconcrete implementationsā would do all that much to strengthen his argument that unnecessary complexity gets in the way of shipping for indie devs:
> Even 10% friction more than I ever had would have killed me. I wouldn't have been able to make the things I had. Even 5% more friction would have been really bad. https://youtu.be/4t1K66dMhWk?t=3635
Andā¦
> I have, several times, built games where I barely managed to finish. ⦠I've just experienced that too many times to increase friction. I need to decrease friction. https://youtu.be/4t1K66dMhWk?t=3072
And again, in relation to entities rather than Rust's borrow checker:
> If you are trying to focus on the way your entities are set up, you are mis-directing your effort and that's going to make it harder. Try to solve the problem that makes your game interesting. What is it about the gameplay that makes it interesting ⦠that users can see? Focus on that, solve those problems. https://www.youtube.com/watch?v=w7W3xM2tzRA
On what he uses instead of ECS/components in his engines:
> One struct per entity type, with a base struct that is common to all of them. https://twitter.com/Jonathan_Blow/status/1427376307453665280
This is an interesting happenstance in the Rust community that I think is largely cultural rather than technical. Backlash against some figurative idea of OOP is popular. ECS is popular as the messianic retort to that OOP figure. But when you look at it from an architectural standpoint, ECS is quite possibly one of the hardest things to do in Rust compared to other designs. Let me get this straight - you want to avoid shared mutability but you have all your gamestate in centralized stores? That systems will necessarily have to have shared, mutable access to? Possibly concurrently? I think it's a good idea architecturally, but also I think it's a tough problem to solve, and I think the claims that Rust lends itself to that architecture are false. It's even tougher in Rust than in other languages, I'd say. And though I've read the source code to many Rust ECS libraries, they all do it in different ways, and all of them feel like hacks.
I definitely feel like the most "rustic" way to do game design, just based on what's easy to do in the language itself without resorting to workarounds, is to have individual actors maintain their individual state, and then have a common interface via a trait that would call update() or render() virtually in a loop or whatever. Then have them message each other via mpsc channels. That's pretty much rust straight from the book, and, it's also a bog standard GameObject architecture straight outta the 2000s.
are these message channels deterministic? Or would order of delivery and processing be resolved by chance?
The 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.
ECS 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.
So 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++).
Iāve not yet used Godot. What is the Godot style like? And are there some documents about it and example code of it?
Godot 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.
Consistently interesting content coming out of Pragmatic Bookshelf publishers these days. Recently, I have particularly enjoyed the Ray Tracer Challenge (https://pragprog.com/titles/jbtracer/the-ray-tracer-challeng...) and Web Development in Clojure (https://pragprog.com/titles/dswdcloj3/web-development-with-c...)
I started reading this book a few months ago. It is pretty good, I really like learning while working on a "sort of real world" project.
However, being completely new to Rust I find that the author doesn't spend enough time discussing the language, it's syntax and nuances. It is hard to talk both about rust alongside video game design techniques.
I put it down after reading 1/4th of it. I'm planning to spend some time on a book that focuses on the language first and then get back to it ;)
That's a very difficult balance as an author writing a project-based book for a language. It's really difficult to know how much time to spend where: the language (Rust), the projects background (gamedev patterns) or the project code.
Looks pretty good! I will say, as someone who programs in their day job and has been trying for ages to get into game dev as a hobby, love2d [0] has been excellent for getting started. My github has a few repos of previous attempts at making simple games (in .cpp, .rs, etc) which I abandoned from the amount of work it took.
If you're in a similar boat, I would recommend checking the framework out. Lua's a pleasure to program in and you can focus on the game development itself instead of getting bogged down in the details of rust / cpp. In fact I've been thinking lately about how easy it would be to use it for things other than games -- quick prototyping of graphical simulations, psychophysics experiments, etc.
[0]: https://love2d.org/
I don't think most non-rust programmers know what ECS is.
ECS is a pattern to manage composable logic and shared behavior. very very loosely like splitting logic in to class level and object level.
Lots of game developers know what ECS is. It existed before rust gamedev became a major thing. Unity even has an (experimental to be clear) ECS engine you can use to replace traditional monobehavior style objects.
fair, but I had to lookup what ECS was so I though I'd save others a bit of googling.
Yeah explaining it is great. Just don't want people thinking it is only a Rust thing.
I worked through this book a couple of weeks ago and I had a blast. I heartily recommend it to roguelike fans that havenāt tried out participating in the 7DRL game jam yet.
Book is also online: https://bfnightly.bracketproductions.com/
and on Github: https://github.com/amethyst/rustrogueliketutorial
(Author here) That's the Roguelike tutorial I created, not the Hands-on Rust book. The two are quite different beasts, with a bit of overlap.
Hands-on Rust is designed for the newcomer to Rust, and carefully maps tutorial sections through teaching beginner-to-intermediate Rust concepts. It starts with some basic Rust exercises, works through a Flappy Bird clone, and then uses Roguelike development to teach a lot of underlying Rust concepts. It also teaches gamedev, and tries to do so in a way you can reuse in other games.
The tutorial is all Roguelike, all the time - focused on building a working roguelike.
Congratulations on releasing your book. I watched one of your talks about procedural generation[0] and really enjoyed it, thanks!
Mentioning this here since the tutorial linked above has a lot of procedural generation content
It is a very good learning guide. Keep in mind I am someone who negotiates the borrow checker by adding-and-removing */&/to_owned() rather haphazardly.
I found it especially satisfying to port the game from racketlib/rltk to bevy afterwards.
I was thinking of following the book but with Bevy. How do you replace bracket-lib?
how you replace bracket-lib is the core of the exercise. There's more than one way to do it.
Translating the implementation to bevy on the fly as you read the book would probably be much more difficult than what I described.
Get a daily email with the the top stories from Hacker News. No spam, unsubscribe at any time.