Bob Nystrom - Is There More to Game Architecture than ECS?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

One of my favorite talks on game software architecture

by the author of the awesome game programming patterns book

some nights I boot up Visual Studio and turn on this video to show my code what it should actually look like

👍︎︎ 15 👤︎︎ u/UnparalleledDev 📅︎︎ Jun 26 2020 đź—«︎ replies
Captions
hi everybody y'all excited about talking about software architecture oh wow that works better way better than I thought that's like the saddest hype statement ever yeah so uh I'm gonna talk about software architecture um I don't know there's any intro stuff I need to say nope let's get going so my name is Bob Nystrom I when I was a teenager my brother and I spent a lot of time working on a tabletop role-playing game and at some point I found myself starting to hack on my own roguelike and I've been tinkering on it ever since that was 20 years ago it's nowhere close to being done so I have poor resource management skills apparently during that time I worked for EA for eight years so I have experience being trapped in dungeons for a long time as part of my escape plan I wrote this book on software architecture called game programming patterns it's a real book but you can also read the entire thing online on the web at that kind of hard to read URL right there right now I'm in the middle of writing another book on programming languages you can't read the entire thing online because it's not done but the stuff that is done you can read online but the accomplishment that I'm most proud of in life above and beyond even having children is that I beat egg band never been in front of an audience that would appreciate that this is a high elf mage this is around two point nine point three very proud of myself okay so this talk is a there's bullet points oh I should point out that I wrote a custom program from my presentation using the same engine as my game because I wanted to just go full nerd so I'm gonna talk a little bit about what the the ECS pattern is or at least sort of you know my understanding of the classical conception of it I'm gonna talk about why I don't use it in my roguelike and stuff architectural patterns that I do use instead that I think are super cool so let's talk about let's talk about EC s stands for entity component system if you don't know some of those are pluralized but I'm never sure which ones are supposed to be plural so I didn't pluralize any of them so we'll start with entities so imagine you're you know back in the day maybe not back in the day writing a Triple A game and you are implementing you know a character in the game right so the entity is the the class that represents that character they might call it entity you might call a game object something like that and in a sort of modern real time interactive game there's a there's a lot of stuff in there across a lot of domains right so like you know you've got AI doing like pathfinding and like playing scripted events there's like physics interacting with the world there's this whole animation system that's driving your skeletal animation and then there's a rendering layer on top of that and all these domains interact with each other right so like the you know the the eye cave or foot planting in your animation needs to take into account the surrounding physics volumes and it's this big ball of stuff right so you could imagine just jamming it all in one class which is what they did in the 90s and you know you if you talk to old game developers you'll hear horror stories of like a game object class that was like 20,000 lines of code so that's that's that's bad so problem number one is complexity right so you have this one entity class that touches a lot of domains in it sort of everything is jammed together so the solution they came up with is components so basically what you do is you look at that entity class and you take each domain sort of you know you can imagine each specialization in game development and you pull the the data and the code for that out into a separate class so you have an AI component of physics component animation graphics you know maybe some sound maybe other stuff and then your your entity class becomes basically just a bag of components right so we get a lot smaller a lot simpler and each domain is encapsulated in separate code which is nice because a lot of times these are maintained by different people too so the next thing it does is okay so this is a this is a game loop right so you're running through your energies in your update updating them and you tell each entity to update it and it doesn't stop it's supposed to do except the this is not a correct game loop the way your game loop is supposed to work is that the each domain runs in a separate face you all the AI for every actor then you do all the animation and all the physics so let's see what what is my next slider so that the bad thing about this is if you do it this way where all of your entities are jammed into the same class all the domains are jammed into the same class it's slow so there's this performance problem so how many of you know anything about CPU caching so if I ask you like about cache misses and misses and stuff okay so a couple of you which is good so you know you know about Moore's Law right CPUs have been getting twice as fast every 18 months which is like awesome as you know we software users because it means we get to be twice as lazy every 18 months every developers unfortunately Ram didn't keep up right so Ram has not gotten faster like that and it turns out having a super fast CPUs not very useful if you can't actually get data onto it fast enough so what CPUs do is they have caching on the chip and when you read from Ram they speculatively pull in nearby memory onto the cache and that way it can be accessed faster and that's based on the assumption that if you access byte here the next thing you're gonna need is the byte next to it right so if you if you access them that way it's super fast and if it's not it's way slower and by way slower I mean like two orders of magnitude slower so the donors routine like a 60 frames per second game and like a 1 frame per second game so let's see you fight yeah so are when you have a monolithic entity class with all of the state for each domain you know mix together then you have an array of those in memory as you're skipping through updating the animation so if you're skipping over the physics and the other domains and you're just blowing your cache every time right so it's super slow when you pull this stuff out into components that means you can make these these homogeneous arrays of each component so when you're updating the physics you're just like stepping through you know memory and not skipping over anything so this makes a big difference for perf this is like if you are like I'm you know a modern like you know sort of triple-a real-time game developer like this stuff is your life for better or worse so this system is part of this is like a little bit vague but basically if you imagine the code that is sort of stepping through that domain that's kind of what is and some people pull that out of the component and some people don't so sorry this is an example of a system here so you can see it's just walking to AI components and that the the useful thing is that it is not mentioning the entity at all right it just goes straight to the components it's not skipping around in memory and that's what's kind of making your CPU happy okay so how does this apply to the world's best game genre so problem number one complexity so let's think about you know take a you know your your monster class in your in your robot I can think about the domains the physics is not super complicated you graphics fidelity is maybe not you know that's more or less equivalent to the complexity of like you know bones skin mesh animation right a is interesting though right like there is actually quite a bit of stuff in AI right like monsters can do lots of different stuff there's path finding these things interact with each other so maybe you know you have a monster that's afraid and it has a teleport special move doesn't know to use teleport to get away stuff like that so so this domain is kind of interesting so I'm gonna hit too far so if you imagine taking your entity class in your roguelike and splitting it along domains you get the super lopsided thing where you're you know your animation component and your physics component are useless and then like the AI component is all the things right so it's not super helpful for kind of you know breaking down your code and organizing it and then we can talk about performance so so of those of you who know what CPU caching is how many of you have performance problems in your roguelike you how many of you oh wait sorry was there another one I see three three of you how many of you know that it's because of CPU cache misses this is still three okay so the rest of you this pattern is not helpful for you right so uh which is kind of weird because you see a lot of people online saying like you know we're got my robe like and I want to use ECS and it's like okay well do you do you have these problems I sort of think of it as like going into the doctor and being like yeah I hear really good about c-sections can I have one it's like a are you pregnant no so at least in the classic incarnation of like splitting along domain boundaries I don't think it's a super good fit but that does not mean that rope legs do not have problems at least organizationally so how many of you actually are working on your own roguelike right now okay lots of hands it'd be weird if like none of you raise your hands why would you be listening to me talk about this would be strange so how many of you feel like you struggle with software architecture issues organizational issues you know your yeah I do I run into stuff like this all the time where it's like I know what I want the code to do but I have trouble figuring out how to organize it right so I'll go through a couple of problems that you know that I run into a lot so one thing this is something I really like about roblox is there's a lot of behavioral complexity right there's a lot just a lot of different stuff that entities in the game can do so this is it's my game right now just the different things that like monsters in the hero can do right like there's like I had a management there's like all things like special effects so you have a lot of a lot of behavioral richness and then there's a wider breadth of content so Fortnight not that there's like two kinds I don't know anything about four and I don't know how this works but like four and it has like sixteen different kinds of monsters I think they're basically all different zombies which is not like super much um but a Dom has according to the wiki like 317 something monsters so like your typical roguelike just has like a lot more breadth of content right and that is an organizational challenge right so the I'm gonna go through a couple of patterns and these are patterns that I think have worked well for kind of helping me tame these issues whether or not they will help you I don't make any promises okay so let's talk about items items are awesome you know a lot of games roguelikes were very equipment based a lot of the capabilities of your players based on equipment what that means is there's just a lot of stuff that items can do right so like weapons can be used for melee attacks or ranged attacks maybe you've got armors maybe shields increase your ability to dodge their stuff you can consume things you can activate and reuse sometimes stuff has passive effects just a lot of just stuff an item can do right and jam all of that in one class it's is not super awesome right so the the classic solution was like okay we'll use inheritance cuz inheritance will solve all the problems and you make subclasses for all the different kinds of of items you know where this is going really this is a talk in like nineteen ninety five you guys didn't like yeah so classic is awesome but now we have learned so the problem is like eventually you end up in a situation where like I want to have a sword that shoots a fireball and it's like okay well I don't want to deal with like multiple inheritance and stuff like that and it falls apart right so that's bad so instead what I do is I split out each capability that an item has into a separate class in a separate field so there is there's a capability for being able to given it do an attack there is a capability for being able to provide defense then there's capabilities for being able to use it and that's kind of polymorphic right so there is a base use class and then there are these subclasses for each of the different ways you can use it and this is a case where I think actually inheritance works really well where you have a hierarchy that's not very deep but it's really it really wide and then then the item itself is just a combination of these capabilities right and you can sort of freely mix and match so you want to make a sword that's just an item that has some happens to have a melee attack right you want to make a shield that's uh you know it turns out you can also have a melee attack because maybe you could shield bash with it right um so it works nicely too if you want to be able to you know define kinds of content in data files right because then you can read these from data and then you're constructing kinds of items procedurally so this is pretty nice so the whether or not you call it component is kind of an interesting question but I think of this as you know using components to represent capabilities instead of domains or a sort of more general way to say this is this is just the the old principle of preferring you know composition over inheritance I think this works pretty well I use it in a couple of different places so monsters have special moves and those are basically capability objects to speaking of monsters let's talk about monsters monsters are awesome so imagine your heroes in the dungeon right he's hanging out he's surrounded by 20 goblins and you know goblins have lots of interesting stuff about them you know position health how close to death they are things like that but there's a lot of stuff that they have in common right because they're they're all goblins and if you kind of stuff all that into the monster class it gets sort of you know is a lot of extra state it's kind of messy so the solution that I have is this is not super rocket science I know this is not like galaxies brain right here is to define a separate class that represents a kind of monster right so there is there's one instance of this breed class for a goblin and then every monster every Goblin just has a breed that points to that this is a an old pattern it's called the type object pattern you you are probably using this already you've probably reinvented it it is but it is super useful it's nice to think about one of the things I think is kind of interesting about it is you know so you're implementing your game probably in an object-oriented language and then you're basically reinventing an object orientation at the application level right so this breed class is a class that sort of represents a class right so breed itself is kind of a meta class in each instance of breed as a class and that you know gives you some interesting opportunities to pick object-oriented features and reemployment themselves if you're useful if they're useful like you know we could give breed a pointer to another breed that was its parent breed that it inherits from and we could define our own inheritance semantics about something that's useful for us so that's kind of cool I use the same thing for types of titles in the world for types of items special modifiers on items so the basic idea is defining a class that represents a type where each instance of that class is a new kind of type sounds a little weird and meta when I say like that okay so let's talk about actions which are awesome so my I don't call them entities I call them actors this is kind of the main class hierarchy for living the stuff in the the dungeon so there's there's a base actor class that has the the things that heroes and monsters have in common position health things like that and then there's a so class we're here in a sub class for monster which is just the stuff that's you know the heroes always a little bit special in the game and that's kind of the extent of the hierarchy and before you know I talked about all the different stuff that entities in the game can do right there's like a lot of behavioral richness there's like kind of management they're special effects walking around the dungeon combat that behavioral code needs to live somewhere right so if you stuff all of that in the main actor class you know you're right back to the sort of the original 90s ten thousand lines of code game object class where it's just like the whole game is inside there right and you hate your life and no longer want to live so that's bad there's kind of a related problem too so so and for those of you that have your own roguelikes how many of you have a speed system or an energy system we're different you know actors and monsters can move at different speeds it's a decent number of you so so this doesn't really work for your game loop right you can't just be like okay let the hero do their thing and then like run through the monsters and then repeat right because if the hero's slower that means more monsters need to get processed if the hero's faster the hero may need to take more turns so you need a way to be able to kind of interleave the way heroes and monsters are being processed by the game loop so that doesn't work my solution is a the game before the Gang of Four pattern is called the command pattern I'm calling it action here so it's the command pattern and what that is is it's basically an object that represents an operation back in the day this was like maybe a revolutionary concept most of us today are used to programming languages that have first-class functions so it's basically just a closure it's just an object that represents the thing that you can invoke in practice you can't use just a raw function for this - and practice it is kind of nice to define a class because you end up getting some other functionality and stuff in there so in my game this action class and an action is a you can think of it as a first class turn it represents a single step in the game that an actor can perform and the way that works then in the game loop is the game loop up here is doing kind of the you know the energy speed stuff where it's you know giving actors some energy based on how fast they move and when they cross a certain threshold they're allowed to take a turn and the way they take the turn is the game loop asks the actor just like give me your action and then the game loop performs the action this is super simplified in my game because there's like animated effects and stuff this performing the action is also this iterative process as you sort of percolate through the consequences of the action that so splitting things up like this does a couple of nice things for us so for example here's the here's the action class for taking a step it's kind of simplified down but there's a lot of stuff in here right like you walk into a door we want to open the door if you walk into another you know monster you try to hit it the actual details of this don't matter though I'm not gonna give you like a test on this code that'd be weird the important parts like it is a lot of code though right and now it's a lot of code that's not inside actor so it pulls all that stuff out and when you think about the scale of like the behavioral richness so these are the is the action classes in my game right now this is a lot of different stuff right it's a lot of code and all this gets pulled out of actor an actor basically it becomes kind of a just sort of a data structure and maybe a little bit of code to sort of maintain invariants like yeah your health is not allowed to go negative the really nice thing about this model is what I love about roguelikes is that you can do a lot of stuff so it makes the world feel more real and this model makes it easy for me to add new behaviors without feeling like the sort of quadratic complexity of the code base is getting worse right because these things are fairly decoupled from each other it's not like I'm pouring more code into a class that's getting bigger and bigger and uglier and uglier so it makes it feel like I can kind of just scale up the fun of my game without increasing the cognitive load of me trying to maintain that code uh-huh the other thing it does for us is it gives us an abstraction layer so one of the main differences between the hero and monsters is the hero is driven by the player and monsters are not so the hero is driven by user input and the way this works is when we ask the hero like what what turn do you want to do it uses user input and it produces an action and then when we ask monster what to do it runs the AI and the ai's job is also just just producing a in action so what's nice about that is if if the monster AI and the you know the the player driven code was sort of procedurally just driving behavior it gets harder to share behavior between those two things the end of just sort of doing the things directly and you you would end up with two implementations of here's how to take a step here's how to open a door here's how to do a mail a hit actions give us a nice abstraction layer for that so the a eyes job is just to construct an action object and yield it and then the rest of the game engine the game loop just kind of drives that and then we get to share all of the same behavioral code between AI controlled and player controlled characters makes it easy to you know share things like spell effects and stuff like that that makes me happy so uh the sort of the way I generalized this is if you find yourself getting stuck in your code and it feels like there's there's a missing set of flexibility one of the techniques has always worked really well for me is take some some verb that's in the program some of the thing that's being done and turn it into a noun create an actual object that represents that because that that gives you a way to you know it makes it first-class you can create it in one place use it in another place you can give you can start giving additional properties to it um it seems to generally be a nice technique and that's that's basically it there was a lot of code I apologize for the quantity of code especially right before lunch I realize that's a lot to take in so the super-sized summary is uh I don't use the the classic sort of split along domain boundaries ECS pattern and i don't think it's generally a good fit for any roguelike that is not super graphically rich or physically Richard you know if you're turn-based in tile based I don't think it's doing much for you but I do like using components in a sense that you split them along capabilities I think type objects even though it's a really just dead simple pattern is super useful I use it all the time and then the command pattern which isn't as well-known as a lot of the other design patterns that are out there it's not something you use all the time but when it when I run into situations where I do need it it's incredibly powerful if you've ever tried to like I don't know if any of you ever built like a level editor or something if you need to end implement undo in an editor if you use the command pattern it just works right so like each command also supports the ability to undo itself and then your undo system is just maintain a list of historical commands and you rewind them and it's great if you don't use the command pattern and you try to implement undo you basically just never want to be a software engineer again not that I'm trying to oversell it and that's what I got so this is actually the level generator for my game running live right now in the middle of a presentation which is probably not the smartest idea I've ever had but it looks kind of cool anyway thank you if you want to see you know learn more about my books I have a blog that's kind of the main URL that gets to all the other stuff I brought a couple of copies of my book that I've been schlepping around the Bay Area for the past couple of days and I totally don't want to schlep them around any more so if you would like a free copy of the book come up and say hi we don't have to interact I understand I'm introverted you're introverted we can we can you have to make eye contact you can kind of just take one it's okay and thanks enjoy your lunch I guess [Applause]
Info
Channel: Roguelike Celebration
Views: 100,164
Rating: undefined out of 5
Keywords: roguelike, Game Design, Video Games
Id: JxI3Eu5DPwE
Channel Id: undefined
Length: 23min 6sec (1386 seconds)
Published: Tue Oct 09 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.