[HN Gopher] Hands-On Rust: Effective Learning Through 2D Game De...
___________________________________________________________________
Hands-On Rust: Effective Learning Through 2D Game Development and
Play
Author : nallerooth
Score : 262 points
Date : 2021-10-14 07:59 UTC (15 hours ago)
(HTM) web link (pragprog.com)
(TXT) w3m dump (pragprog.com)
| igh4st wrote:
| The book sounds awesome to get your hands dirty with Rust :)
|
| is there a promo code for Latin-american countries?
| thebracket wrote:
| I'm not sure what promos are active right now (I just got back
| from vacation). It will be included in the November
| Thanksgiving sales. You could also email support@pragprog.com -
| they would be able to help.
| igh4st wrote:
| I'll reach out to them, thanks!
| cultofmetatron wrote:
| by any chance, can you reccomend a good book for learning
| programming thats written for a spanish speaking audience?
| myth_drannon wrote:
| It's a great book but I struggled with an additional overhead of
| ECS library usage. Don't know if it would have been better just
| roll out it's own simple logic for ECS , but then it would
| probably double the length of the book.
| thebracket wrote:
| Page count was a real concern. I wanted to introduce Rust
| newcomers to easy concurrency, and keep the data-storage side
| of things manageable (storing a big list of dynamic objects
| with traits gets messy _fast_ and leads to a lot more borrow-
| checker fighting). Using an ECS let me dodge the latter bullet,
| at the expense of a bit of complexity. (I made sure Flappy didn
| 't need an ECS)
|
| Bevy didn't exist when I started writing, or I'd have probably
| used it. Legion is a great ECS, but it's heavier than I'd like
| - and Bevy makes a lot of things really easy.
| TylerE wrote:
| This concerns me a bit.. when I've made (failed) attempts to
| learn Rust in the past the borrow checker was always the
| sticking point.
| baq wrote:
| It needs a mindset change. The thing is trying to _help_
| you. It points out issues that you didn't think about. It's
| naturally difficult to think about things you've never had
| to think about before. It also turns out that architecting
| software in certain ways (like ECS) makes it easier to
| think less about borrowing.
| uDontKnowMe wrote:
| 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...)
| halvnykterist wrote:
| 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
| pizza234 wrote:
| > 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 wrote:
| I 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
| bcrosby95 wrote:
| I'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 wrote:
| Do 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 wrote:
| I 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.
| jyriand wrote:
| Sorry, what is ESC?
| modernerd wrote:
| 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.)
| moth-fuzz wrote:
| 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.
| [deleted]
| platz wrote:
| are these message channels deterministic? Or would order of
| delivery and processing be resolved by chance?
| nyanpasu64 wrote:
| Message channels are deterministic if they're only used
| on a single thread.
| dgb23 wrote:
| 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.
| cardanome wrote:
| 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.
| modernerd wrote:
| 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/s
| tatus/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/st
| atus/1427376307453665280
| meheleventyone wrote:
| Frictions a funny thing in this sense and what hurts one
| person helps another.
|
| One example is that a concrete entity is much harder to
| change than one that is composed of concrete components.
| So for example an artist I work with took some components
| from an FPS game we made, some other random components we
| had and a couple of components made in people's spare
| time, made a bunch of art and ended up with a pretty
| convincing prototype of a multiplayer shooter. He
| couldn't have done that at all following a concrete
| entity approach.
|
| Composition and in particular making composition data-
| driven and runtime malleable is very flexible even if you
| don't care for the ECS approach.
|
| But a large part of the problem is that we end up
| promulgating opinion divorced from context and often a
| lot of that context is not really more complex than the
| approach someone is used to. Like if you have no problems
| with the concrete entity approach which has been a
| successful pattern since forever then there isn't that
| much compelling you to change. That doesn't make it the
| one true way or that because someone famous and
| opinionated likes it that there isn't an alternative that
| will better serve someone else's needs.
|
| Horses for courses.
| halvnykterist wrote:
| 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.
| kvark wrote:
| 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
| rtoway wrote:
| Bevy, 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() { } }
| verdagon wrote:
| Indeed, 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_...
| brundolf wrote:
| > 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 wrote:
| I 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.
| cardanome wrote:
| 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.
| pjmlp wrote:
| 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.
| dgb23 wrote:
| 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.
| pjmlp wrote:
| That is when data oriented programming gets into the
| equation.
|
| Classical ECS as it originally appeared on the literature
| is coding against interfaces, COM or Objective-C
| protocols style.
|
| So yeah, one composes those interfaces together, there is
| no class inheritance, only composition, delegation, and a
| system is composed from a jungle of such components.
|
| It is also a reason why DirectX is COM based, instead of
| basic Win32 calls.
| ngrilly wrote:
| What do you mean by classical ECS? I've always read that
| the Entity-Component-System approach originated in the
| gaming industry, completely unrelated to what you wrote
| about COM, etc.
| pjmlp wrote:
| Yes, the famous GDC talk.
|
| Usually most stuff in game development tends to be
| rediscovered, as most people don't come from CS
| backgrounds.
| glowcoil wrote:
| > 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.
| pjmlp wrote:
| I also did not state that book was the canonical ECS
| model, rather that it was one of the first sources to
| move into discussing components instead of classes.
|
| COM and DirectX aren't distributed object systems, nor
| Objective-C protocols, for example.
|
| Don't confuse COM with DCOM and COM+.
|
| Then there are the component models based on traits,
| mixins, patterns, message passing, type classes,...
| plenty of variants scattered around SIGPLAN and ECOOP
| papers.
| Rusky wrote:
| You are still conflating two totally unrelated things.
|
| The book is one of the first sources to move into
| discussing "components," as in coding against
| interfaces/protocols/traits/etc.
|
| ECS deals with "components," as in pieces of data
| composed using a relational model. This has nothing to do
| with interfaces or protocols whatsoever! It is
| practically the _opposite_ thing- working directly with
| raw data, with no abstraction boundary.
|
| You can't just pattern match on the word "component" and
| expect it to mean the same thing to everyone.
| pjmlp wrote:
| That is the next step, data oriented programming, which
| many confuse with ECS, as they tend to be used together.
| Rusky wrote:
| I am not talking about data oriented programming. With or
| without that sort of memory layout optimization, ECS
| "component" still refers to un-abstracted chunks of
| concrete data rather than interfaces/protocols/etc.
|
| Let's step back even further and consider Unity's pre-
| DOTS "entities" and "components." These do not take the
| data oriented approach, are not typically even classified
| as ECS (e.g. because they lack the System aspect of that
| design). However, the components are _clearly_ chunks of
| concrete data (transforms, meshes, rendering parameters,
| rigid bodies, etc.) rather than interfaces /protocols.
|
| This is the sense in which ECS means "component." That
| book is not relevant to this sense.
| [deleted]
| nulldata wrote:
| ECS 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-
| Ar...
| wlamartin wrote:
| At 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.
| dkersten wrote:
| 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.
| codetrotter wrote:
| I've not yet used Godot. What is the Godot style like? And
| are there some documents about it and example code of it?
| meheleventyone wrote:
| 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.
| ffhhj wrote:
| ECS 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
| [deleted]
| thebracket wrote:
| (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.)
| halvnykterist wrote:
| > Composition over inheritance (especially in Rust, which
| doesn't really have inheritance - although you can fake it
| with traits)
|
| This does not require ECS, you can happily have something
| like struct Entity { components:
| Vec<Box<dyn Component>>, }
|
| (Nor do I think this is a good way of setting up a
| traditional roguelike)
|
| > Replication; if you want to replicate state across multiple
| nodes, a good ECS can really help you
|
| I'm not sure what you exactly mean by nodes here, but making
| something serializable in Rust for easy replication is hardly
| an issue when we have access to tools like serde.
|
| > 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
|
| Addressed above, but while ECS solves the mutability issue
| it's not a unique way of solving it and bringing it in to
| deal with that is overkill at the very least.
|
| > Surprising flexibility; Rust EC setups typically let you
| put anything into a component.
|
| Again, you can do this with regular old components too. This
| is also an oversold feature of components / mixins / anything
| like this in general, I think. You can never "just add a
| component", you need to fix all the issues that come with
| that, like making sure that gravity doesn't kill your birds.
|
| > For simulation type games, it's great to be able to add a
| simulation feature and have it apply everywhere.
|
| Yes, but that's not what a turn based tile based game is.
| Generally you want to iterate over things in order - gravity
| (if a roguelike has such a thing) gets applied on the
| player's turn, and only for the player. If you step over a
| ledge you don't wait for the "gravity" system to kick in and
| apply gravity to all entities, it is resolved in the same
| instant for only the entity that has just moved
|
| > It really helps with parallelism.
|
| Sure, although gameplay logic is not what's going to have to
| be parallel in a roguelike. Building up pathfinding maps and
| similar is useful to do in parallel, but ECS doesn't really
| help you with that.
|
| > If you're in a team, it's a great way to break out work
| between team-members.
|
| Not a particular strength of ECS, and if anything I could see
| issues arising from the fact that you basically have dynamic
| typing when it comes to what behaviors an entity has.
|
| > But here's the thing: the systems part is optional.
|
| If you're not doing query-based ECS with systems there's also
| no particular reason to not use vecs of components within
| entity structs.
|
| I believe what you're doing here is adding a bunch of
| complexity to something that could be much simpler, and it
| really does the language a disservice.
|
| As a final note, in the excerpt about items you have this
| justification for not using an enum instead of components:
| Each item you're adding provides only one effect. It's
| tempting to create a generic UseEffect component
| containing an enumeration. Enums can only have one
| value--if you want to make an item with multiple
| effects, you'd be out of luck. It's a good idea to separate
| effects into their own components in case you decide to
| create an item that does more than one thing.
|
| Not only does this violate YAGNI, it's trivial to work
| around: enum ItemEffect {
| Heal(i32), Poison(i32), Explode,
| MultiEffect<Vec<Box<dyn ItemEffect>>>, }
|
| It just feels like you're looking for problems to solve.
| [deleted]
| MaulingMonkey wrote:
| struct Entity { components: Vec<Box<dyn
| Component>>, }
|
| At this point you're effectively just hand-rolling your own
| ECS - and perhaps being in denial about it by touting the
| fact that the ECS is half implemented at best - rather than
| eschewing an ECS outright, IMO. If you want to skip the
| ECS, embrace the natural typing of the language - then you
| don't need to build something to query and filter
| components, and can instead just use the language's built-
| in constructs: struct Entity {
| pub position: Option<XY>, pub sprite:
| Option<SpriteId>, pub scripts: Vec<ScriptRef>,
| //... } fn render_world(entities:
| &[Entity]) { for entity in entities {
| if let Entity { position: Some(xy), sprite: Some(sprite),
| .. } = entity { // ...
| } } }
|
| Entity may eventually become a merge hazard on larger
| teams, and Entity enumeration without archetype filtering
| may eventually become a performance hazard if done too
| frequently and naively, but this kind of approach can work
| fine for many smaller projects.
| halvnykterist wrote:
| The crucial difference between having a vector of
| components is in access patterns - in ECS you iterate
| over entities that have a given set of component, using
| queries, with entities that have vectors of components
| you're still iterating over entities. For something like
| a roguelike you're generally just dealing with one entity
| at a time, so this is a very important distinction.
|
| That said, I don't think just having a vector of
| components is good design, you can do a lot better than
| that. I think the most natural thing to do in most
| roguelikes is to have an "Entity" type that's shared
| between players and monsters and has things that you'd
| expect those things to have, along with an inventory and
| potentially some kinds of tags governing behavior. Type
| Objects etc fit nicely into that kind of scheme.
|
| >and Entity enumeration without archetype filtering may
| eventually become a performance hazard if done too
| frequently and naively, I'm specifically talking about
| turn based tile based games, here, so iterating over all
| entities matching a given set of components is rarely
| going to be a performance concern. It's far more
| important to make sure you're efficient when it comes to
| things like AI routines and pathfinding.
| [deleted]
| thebracket wrote:
| We should probably agree to disagree; your approach is just
| as valid as mine. My first attempt at a roguelike in Rust (
| https://github.com/thebracket/rustyroguelike ) was a
| straight-from C++ approach, using dynamic traits and a lot
| of casting. It was my first try at using Rust, and is
| pretty bad - but at least it completed the "r/roguelikedev
| does the tutorial" event. Going from OOP to a non-
| inheritance language was quite the leap.
|
| You absolutely can have an entity structure containing a
| vector of dynamic component types. It can get quite messy
| when you start having a lot of component types. When your
| entity runs, it has to look at the data in its components.
| That either means that component is a big enum (no need for
| dyn and Box there), or component is a trait and you're
| going to be doing a bunch of dynamic casting to find out
| what components an entity has when it ticks. Either can
| work (as can having an Option for each component type,
| which replaces the dynamic cast with an `if let`).
| Ultimately, there's not a lot of difference between an
| entity iterating its components and calling methods on
| components (or branching based on the components it has)
| and running a query to retrieve the components you want and
| acting on them. It's not that different from a NoSQL
| database (everything in a node graph) vs. a relational
| database (tables keyed to an identifier) - both work, each
| has their strengths and weaknesses. (I also really didn't
| want to try and teach dynamic casting early in the book!)
|
| Some specific points from your reply:
|
| By "replication", I meant network replication. You can get
| the same benefits by tracking changes within an
| entity/component, but it's really handy when a single
| storage system does that for you. Not that useful to a
| traditional single-player roguelike, but a nice tool to
| have.
|
| > Yes, but that's not what a turn based tile based game is.
| Generally you want to iterate over things in order -
| gravity (if a roguelike has such a thing) gets applied on
| the player's turn, and only for the player. If you step
| over a ledge you don't wait for the "gravity" system to
| kick in and apply gravity to all entities, it is resolved
| in the same instant for only the entity that has just moved
|
| That very much depends upon what you're creating. (Nox
| Futura is a Dwarf Fortress like). In the gravity example,
| it was solved by remembering to exclude the Flying
| component from the gravity query. The roguelike example in
| Hands-on Rust actually runs the player and monsters in
| different phases, but re-uses a lot of systems in the
| process. Matching on the turn state and executing systems
| isn't all that different to matching on the turn state and
| running tick functions on the entities included in that
| turn - especially if you have an energy cost/initiative
| type system breaking out the moves (the tutorial I created
| does this). Both Hands-on Rust and the tutorial effectively
| use message-passing for chaining events together.
|
| > if anything I could see issues arising from the fact that
| you basically have dynamic typing when it comes to what
| behaviors an entity has
|
| You have the exact same problem with a dynamic vector of
| components.
|
| > If you're not doing query-based ECS with systems there's
| also no particular reason to not use vecs of components
| within entity structs.
|
| It mostly boils down to preference. I find
| Query<(MonsterAI, Position, LikesToEatPlayers)> easier to
| work with than having each entity iterate a component list,
| query the type of each component and then act accordingly.
|
| > As a final note, in the excerpt about items you have this
| justification for not using an enum instead of components:
|
| Again, we're solving the same problem in similar ways.
| There's really not a whole lot of difference between
| matching the enum and iterating MultiEffect and querying if
| an entity has components. Either way, you have code that
| says "oh, it explodes" and makes a boom.
|
| In other words, we both prefer different ways to accomplish
| exactly the same thing. Neither of us is right or wrong,
| there's plenty of ways to skin a cat. Hands-on Rust is as
| much about teaching Rust and gamedev in general as it is
| roguelikes in particular; if you want a big, working
| roguelike - the tutorial (
| https://bfnightly.bracketproductions.com/ ) does that.
| anothernewdude wrote:
| 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.
| vjust wrote:
| His explanations seem very clear and simple, I'd dig into it,
| just to get a better understanding of rust, and the game dev part
| also seems do-able, unlike other authors where they dive into
| some complex pieces causing frustration to the learner.
| [deleted]
| cius wrote:
| I recently finished this book and highly recommend it. Very fun
| stuff, a decent introduction to rust, and does a great job of
| setting the reader up for exploring further. Kudos to Mr.
| Wolverson.
| ttymck wrote:
| 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.
| rtoway wrote:
| I was thinking of following the book but with Bevy. How do you
| replace bracket-lib?
| dopu wrote:
| 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/
| greenail wrote:
| 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.
| runevault wrote:
| 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.
| greenail wrote:
| fair, but I had to lookup what ECS was so I though I'd save
| others a bit of googling.
| runevault wrote:
| Yeah explaining it is great. Just don't want people
| thinking it is only a Rust thing.
| dudul wrote:
| 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 ;)
| timClicks wrote:
| 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.
| Zolomon wrote:
| 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.
| Camas wrote:
| Book is also online: https://bfnightly.bracketproductions.com/
|
| and on Github: https://github.com/amethyst/rustrogueliketutorial
| thebracket wrote:
| (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.
| rudedogg wrote:
| 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_
|
| [0] https://www.youtube.com/watch?v=TlLIOgWYVpI
___________________________________________________________________
(page generated 2021-10-14 23:00 UTC)