The Entity Component System (ECS) is a data-oriented architecture for game engines that separates entities (containers), components (data attributes), and systems (logic processors) to achieve better cache performance and flexibility compared to traditional object-oriented programming. Unlike OOP where objects bundle all data and methods together, ECS stores all positions, velocities, and other data types in contiguous memory arrays, reducing cache misses from approximately 40,000 to 2,500 for 10,000 entities. This design enables easier runtime modifications like adding flying enemies without code changes, as systems can query entities by component combinations rather than class hierarchies.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
010 Introduction to Game EnginesAdded:
Good morning, good day wherever you happen to be.
It's Wancho here again. Uh let's get into this. Let's talk a little bit about kind of the main topic of this course.
By the end of this lecture, uh we're going to cover the historical evolution of games kind of from assembler up into the modern day. uh we will have a a definition of what a game engine is. As you'll see, it's not really clear. There isn't a specific formal definition, but there is uh nowadays there's an idea of what a game engine is of, what a game is. Uh what I'd like you to do also is to uh understand what the entities, component, and system uh are in particular. A lot of you have taken object-oriented programming courses. Uh and some of those courses uh leads you to believe that object-oriented programming is the only way to build stuff, but actually it isn't. Um and then uh we'll talk about how the game loop integrates with ECS systems. Uh and then we're going to build and test a red square game which actually does nothing. Uh and then uh I'll introduce you a little bit to the catch two unit testing framework to validate the ECS component data.
So let's dive into the history of the game engines. So there's kind of three four there's four uh epochs uh for game engines. Uh first ones was the 70s and 80s where the machines were very limited. Uh the machines had uh very little bit of memory. Uh they were a lot of them were 8bit CPUs. Um so in order to make a game uh you had to run it in assembly. Uh some of the very early games actually were written in machine language. Uh the very very very early ones were written in like by actually making the chip. Um then uh in the 80s uh the computers got a little bit more uh powerful and uh we were able to have some higher level languages and by higher level languages we mean C and Pascals. We introduced Moa functions and modularity. Uh at the end of that we switched over to object-oriented programming because that's kind of what uh the industry was going. Uh that uh got us some u some uh benefits and this was kind of the first time that we started to see game engines come out. Uh and then in the early 2000s uh people realized that the object-oriented uh approach wasn't that uh powerful. So uh we went so let's uh kind of step through these transitions.
uh the assembly language. Um, Space Invaders, Pac-Man, Donkey Kong, uh I would recommend uh that you go and uh kind of read up on them. Uh Space Invaders is a classic uh game. Uh you were controlling the CPU control directly. There was no other uh software running on your computer. There was no operating system essentially. Uh and there was no um network, no web. Uh so you built every game from scratch. Uh typically it was teams of three to four people uh working over six to eight weeks and they get these things. Some many people consider this the golden age of gaming because everything was being invented brand new. I don't think it's the golden age at all but some people do. Uh and then we were able to uh program in C and high higher level languages. Uh, and so this meant that we could we weren't so limited to the processors anymore. So if you had written a game for a Z80 um computer, a Z80 is a type of CPU, you could also port it over to an 8080, which is a similar CPU but from a different uh provider. The um kind of examples of that are Commander Keen in 1990 and Wolfign 3D. uh Wolfenstein 3D. I uh uh spent a lot of time uh working with that uh and I kind of hacked it a bit, but that's a different story. But this is where we were starting to think about what are the systems that need to go into a game, what are levels, etc., etc. So, the games became a little bit more richer and people started to think about systems. And this is where kind of some uh initial thinking about game engines came in.
Uh just back to Space Invaders. Uh this is a classic Space Invaders uh screenshot. Uh it's a very simple game.
The objects go back and forth. You've probably seen versions of that. U if you're ever in an arcade and they have one of these running, I'd encourage you to play it. It's kind of fun. Um okay, so now we go to object-oriented revolution. Uh now we have C++. C++ was great. It's basically C with object-oriented encapsulation, inheritance, and polymorphism. Uh, and so you could now have uh systems that separated the reusable rendering code like rendering and uh some maybe you had a a peer-to-peer game, so some networking code in there. And then you had the game specific content, which was, you know, the levels and and how you walk through the levels. Quake Engine was the kind of the first that came out of that. They wrote the game Quake and then they were able to separate out the Quake Engine. But basically u what happened there was that uh the ID company licensed the Quake engine and the deal was that you couldn't write Quake. So you had to kind of pull it apart yourself. Uh HalfLife Solder of uh Fortune and American McGee Alice uh all used this. Uh then in 1998 uh Unreal um the the Unreal Engine was released by Epic Games. Uh and they've been working on that uh for a significant amount of time. Unreal Tournament, Dosax and Splinter Cell. Uh so this was a significant advancement. I mean I started off by saying that object-oriented programming has uh we've grown past it, but we actually grew into it. Object-oriented programming it organizes complexity. We're now looking at, you know, uh, hundreds of files, uh, code reuse, uh, testing, uh, all of these accessibilities, and the engines were able to be, um, licensed. Uh, the game that was written with that particular, uh, engine may have been the the demo game. uh or the person who was licensing the engine could pull out all of the components of the game which is their IP property and then release that to other people.
So there we are. We've got object oriented programming. I remember I was working at Electronic Arts when we were transitioning from C to C++. We were rewriting thousands of lines of code, but we started to run into trouble uh with some of the things that we'll talk about later. The diamond inheritance uh cache miss uh because of object because of the way the object-oriented languages are designed. They are focusing more on giving you objects rather than where the objects are. um an object-oriented program doesn't necessarily know where its data is. Um we had the the blob entity antiattern.
We had rigid hierarchies which became very expensive to manage. Uh the core issue is that we were trying to think of game engines as or game engine objects as this is a and this is a but uh in many cases what we really need to do is have a game uh entity that has a whole bunch of things. So you could if you're a red square that's bouncing around the screen, you would have a position and you would have a velocity and then that that's how you have to do it. Uh and if you are um another element of this game, you may be just a position and have no velocity. Uh and we'll see how that all plays out a second.
So the entity component system uh came out. If you remember at the end of uh your object-oriented um course, we talked a little bit about composition versus inheritance. Well, this is exactly what's happened in the in the game industry is that the object-oriented programming wall got hit. Uh, and then there was some uh talk and Scott Bas talked about Dungeon Siege, their component architecture. So, people were trying to make these games work faster. Um, in general, uh, you can provide your game designers with more freedom to build rich worlds if you can give them more objects and the ability to, uh, have more objects that the user interacts with. This is kind of fun. Um, so, uh, ECS emerged because it was cache friendly. Uh, we'll talk about this in a second. uh you can mix and ma mix and match components uh and it's all data oriented games a game engine is essentially a data processing engine uh primarily and then there's some systems that that may update that data u and a lot of uh this is now seen in modern ECS engines uh we're not going to be using any of these engines feel free to go ahead and research them uh in particular when you're looking at the research presentation for this course uh and they're listed there.
So the paradigm shift was that let's just have a look at how we would define some things within a um game. So we're going to have an entity. So every every thing that you see in a game, we're going to call it an entity. Uh then we're going to have a player uh and we're going to say that that inherits from the public entity. And then we're going to have an enemy uh that has uh so so you can see the inheritance happening. Um and then you may have multiple players. So uh you may have like a if you have a a soccer game, you may have a goalie versus a striker versus a left wing. So those could all be inherited from player. Now what we are going to be doing in the ECS approach is that we're going to uh create entities and entities are uh containers that contain components. Remember entity component uh and so what we're going to say is that in this particular case uh a player has a position uh a player has velocity and a player adds health. So we are defining a player as that has a position, has a velocity, and has a health. If that health gets to zero, the player dies, the game is over, etc., etc. So, that's kind of a looking into the future. So, what is a game engine?
Okay, we're back. What is a game engine?
Um, game engine is software framework that provides the foundational systems and tools needed to create video games. Um, think of it as the tools that you would have to build if you were starting from scratch in order to get the game out the door. Um, and we'll talk through them as talk a little bit about that here. The game engine is not a game. You can't get a game engine and play it. Uh, you have to add some things and every game has different ways of you adding content and behaviors etc etc. Uh we are going to be talking in this course about ECS games. Uh but basically uh think of an engine as uh a car. Think of a a game like this is a car. So uh a Ford F50 electric an electric Ford F50 Lightning and a Mache have very similar underpinnings in terms of the motor and the electrical system. uh but they have very different bodies and so that's what makes it useful and so you know a lot of car companies have kind of a car engine which is kind of the foundational um the chassis and the motor and where the gas goes and on top of that they build the car and so you get that you get the car but you don't have the chassis.
So let's talk about some of the more current prevalent uh game engines in practice. Uh Unity is a game that's built in C++. It was built uh quite a few years ago. Uh it provides rendering, physics, input, audio and animation system. Uh and you may have heard that uh people use C or Mono in this. Some of the examples of the games that have been built on top of this are Hollow Knight uh and uh Cuphead and thousands more.
There's the thousands of thousands of games that have been released on Unity and some of you have indicated in your comments to me that you've already played with Unity. Um what does this show that once you have an engine you can build completely different games? Uh for example, Team Cherry built Hollow Knight entirely in unit. Uh so this is a screenshot from that.
Unreal is the other big competitor at this point. Uh this focuses on AAA games. AAA means big budget, big gamble, big bet. Um and so in this one they have focused a lot on the rendering, uh 3D rendering, physics, networking, uh high performance. uh the you know and the the people that use this game are like Fortnite, Gears of War, uh some of the big engines that the games that you may have played are all based on uh this engine and again one engine very very very different experiences.
So the core engine responsibilities now remember I said think of the engine as the things you would have to build in order to make a game. So uh you have to start in order to you know video games are by very definition need a rendering system. Uh the rendering system is responsible for taking uh the game status where all the players are and all of that stuff and rendering it to the screen and for doing that 60 times a second or 120 times a second if you're running it very high frequency. Uh the other thing that you need to do with a game is you need to process the input.
You need to get the input from the user.
It's an interactive experience where the uh every 60th of a second you take input from the player uh from the network and you you process that. Um physics uh phys physics is something that you really don't want to build for yourself. Uh there's a lot of research that went into that.
Um but uh adding physics to your game uh is a very expensive component. So, you should probably get uh either one of the open- source versions or if you're like a E team, you would go and uh license something like Havoc uh and then you'd have solid uh uh physics. Now, if you were building a car racing game, you probably want to build your own physics simulation because car racing games really really depend on particular physics of the tires with the ground. uh audio system. Playing music on a computer is actually uh surprisingly difficult. You know, every you you basically have to set up a thread that is taking the data from your audio and you're playing it. You're pushing it out on a channel. The good thing is that that's that problem has been solved a long time ago. Uh and so now you can you can uh use with your audio system. You can have multi- channels u coming in. you can mix kind of the the battle sounds, the background music and the reward sound, all of this stuff. Uh, and then scene management. One of the uh once you get into building uh scenes, which we'll get towards the end of this course and having kind of more complex game and in particular in your in your uh final project, you'll probably want to have some sort of scene management and stuff like that. We'll talk about that. Now, the the key part about all of this stuff is that you can reuse these things. You know, all of these uh things like the the rendering and the input and the physics simulation, the audio system, the scene management, that's all you're packaging that up into a piece of software that a team can then add their content on top of that.
Okay, welcome back. uh why are we choosing entity component systems over object-oriented programming? Uh this is what we're going to talk about now. Let's see. So the traditional object-oriented programming uh was a standard in the from the 1990s to 2000.
One of my first jobs at Electronic Arts was to work on trying to refactor our C code into C++ code. Um there's four fundamental limitations that come up with this. One is the diamond inheritance. Um you've all seen this example in your object-oriented uh programming course. We'll we'll kind of go through it here. Um that's one. The biggest one is that when you use an object such as Java uh or or kind of an object-oriented approach, what you care about is that the object has all of the data and the methods encapsulated into one place in memory. You don't care where that is.
That's the whole point of object-oriented programming. But if you are trying to process thousands of objects, you have to go through and get them from memory. Uh because you're going all over the memory, you're going to lose a 20 5 to 20% 5 to 20x performance uh degradation. Uh the the blob anti- entity antiattern, you know, you end up having a base class that has a whole bunch of uh base classes that methods that they don't use and has a whole bunch of data. Um and then rigid hierarchies it just becomes as as your game grows uh the uh uh hierarchies become large and unmanageable. Uh the core issue is that uh when you're trying to deal with a whole bunch of data has a is a better way of thinking about it. So there's a data centric approach rather than an object method kind of encapsulated approach.
So u this is the example that we use whenever we teach object-oriented programming. We say okay you have an entity. So we have a game and we have an entity and that and we're going to uh instantiate an an enemy from that. So enemy inherits from the entity. So you you play the game then all of a sudden the the the game producer comes and says we need flying enemies. So, you've built a whole bunch of data around this enemy. The enemy has health. It has the weapon damage that it can do. It can say whether it's range damage or close damage. All of the stuff that you've added to your enemy class now, and but we always assume that the enemy was walking around on the on the field.
Then the producer comes, we need a flying enemy. They go, "Oh, okay. So how do we do this? So uh flying has some properties that the enemy doesn't have.
So I'm going to instantiate another class. Then I'm going to make a flying enemy class that inherits all of the stuff from enemy and the flying stuff.
But enemy had movement classes methods.
Flying now has different movement methods. You can go a lot faster. It's not bound by walls. There's all sorts of differences there. So, how are you going to manage this? This is the problem. Um, when you uh when you um look for your flying enemy, are you updating X from the enemy or X from the flying enemy? Uh you can't you can't uh it it just becomes very difficult. uh you either have to go back and uh say okay well enemies can be walking or flying so we're going to uh extend the enemy thing or the entity thing uh it just becomes very difficult.
So here's kind of drawing out uh my entity uh my character is so an entity has a position and health and my character adds movement. This is how we do the the hierarchy. And then I'm going to have a player that has input handling. Then I'm going to have an enemy that does AI damage. Okay. So now if I look at this particular part of my hierarchy, it kind of makes sense. But this is where the producer came in and said, "Oh, I want a flying enemy." Oh, okay. So I need to add fly speed and I need altitude because uh if I'm over 100 meters, the player can't shoot me. But if I'm then I can't So, so but I'm also an enemy and there's things also a character. So, all of the character stuff here we need to come in here.
Okay. So, the character stuff is going to come through enemy is going to come in here.
The movement stuff, what we have here, this is this is where the movement was defined. But we need to override it with this fly speed so that we have a multi.
Oh, okay. Um, you know, ground enemies use pathf finding. And as as you can see, this this just becomes very difficult to manage. And then, you know, if you're doing something like uh Grand Theft Auto 4, at some point, oh, wait a minute. We need a a floating enemy because we want to go into a boat chase.
So, then you'd have float over here, floating and boats. it just gets all messy and and this this be this um uh became very painful.
U so what we're going to do is we're going to um have these components that are just data components and we're going to create entities that have this. Remember we talked about these spreadsheets. We have rows. So, we're going to look at what a row looks like for a ground enemy, and we're going to look at what a row looks like for a flying enemy. Okay? So, here's how it goes for our enemies. So, you know, when you when you think about back here, you know, down here, we have we need our enemies need position and health. Uh they need some sort of movement. Uh and then they need uh flying here, right? So this is kind of the the this is where we got in trouble here. Uh so let's see if we get in trouble when we do this with ECS.
U I have in my game I'm going to define all of these components. A position, velocity, health, enemy AI, and flying.
Right? Those are all of the components.
Uh my ground enemy is going to be this one here. I'm going to uh create the enemy. Then I'm going to add uh the position.
I'm going to add the velocity. Uh, I'm going to This adds the position to the ground enemy. This adds the velocity to the ground enemy. This adds health to the ground enemy. And this adds the enemy AI to the ground enemy. Okay, so we're good there. Uh, now here's our flying enemy. We take our position, we take our velocity, we take our health, we add the enemy AI. Okay, we're good.
Now we just add flying here. So now we have one row. So the the person the the the person or the system that is dealing with enemy updates uh we have a system that deals with ground enemies. Okay.
And then that comes in and it looks for all of the enemies that have position, velocity, health, and enemy AI. Okay, these are the things I need to deal with. And if it sees uh a flying enemy, then it doesn't do anything because we're going to say that the ground enemy is the things that have exactly these four things. If it has a flying enemy, we're not going to deal with it. So, we might have to make a slight tweak to our ground enemy system. But our flying enemy system now comes in and says, if you are if you have a ground enemy and you're flying, I'm going to process you.
So now I can write uh a simple piece of code for the ground enemy processing uh and I can write a simple system for the flying enemy uh processing and they don't conflict with each other. And you'll see that uh we've done that. And by adding flying enemy to our game, we have not destroyed the code. We haven't made the code for the ground enemy more complicated.
Um, so we did have to change one thing. We have to say to the ground, the person that processes ground enemies says when you check for these four components, check that it doesn't have the flying enemy. If it has a flying enemy, you don't touch it. If you don't know what to do with it, just go, I don't know what to do with it. Throw your hands up. Leave it alone. And then the flying enemy is going to come in and say right here, oh, this is a I know what to do with this. I know how to fly enemies and I know what to do with this.
All of this stuff. Uh, and because I know someone else is taking care of these, right? So, we've been able to add that uh the flying enemy by adding a new component and a new system and and slightly tweaking the old system to say don't touch ones that have flying.
That's it. That's all you need to do.
And so rather than making that uh the ground enemy code more complex and well, if you're flying or you're walking, we say, "Okay, you know what? We're just going to add a flying behavior to this and that's going to take care of it.
So, um the other part that we talked about was cash miss penalties. Uh why memory layout matters? Um, modern CPUs are fast at computation, but it takes a lot of time to get memory from me uh the data from the memory into the um into the into the the CPU. So uh we have uh the L1 cache is the last uh that's what from uh it's basically getting uh memory getting data from uh the uh L1 cache is very very fast about four cycles of the CPU from the L2 cache it's uh three to four uh and then you know from the main RAM it's about 100 times so you can see that this is u going to be uh important.
Okay. Uh so when you organize your data in a different way, this is going to cost you penalty.
Okay, so we're back. Let's talk about the cache hierarchy and the memory layout. Uh this is uh important uh for you to understand um in particular with high performance um uh applications uh game engines uh and actually LLMs are actually this important for them as well. So um this is basically how your um CPU looks like. You have a CPU core here. So your CPU or your central processing um unit has the ability to fetch data and so it needs it's it's trying to execute uh um an instruction that needs some data and what we do is we have these staged uh these layers of data. So there there's something here in in the data in in memory. Uh and then the the CPU does we the the systems our modern computers they fetch the data in in passes right.
So instead of just getting like one word uh we we grab whole bunches of memory and then we pull it into the L3 cache, the L2 cache and L1. L3 means level three, level two, level one. The smaller the number, the closer to the CPU. As you could see from here, the smaller the number, the faster it is. Okay.
So, um, if we look at our object-oriented programming, uh, if let's say we wanted to deal with all of the entities that have position and velocity, right? We want to do we we want to process the movement, right? The way that we've laid out our memory is because we have objects that have position and velocity. Uh you'll see that we have all of these data. These are all kind of separate in memory. So they're all over the place memory. If I look at it from a ECS perspective, my positions are all in the same place in memory. My velocities are close. So I can grab my positions. I can grab my velocities and I can very quickly update them.
uh the typical performance difference between object-oriented programming is is actually real. So uh with object-oriented programming you could get something with like 40,000 cache misses for 10,000 entities about four per entity. With ECS uh you're going to hit about 2500 cash misses. These are kind of uh thumballing the u numbers.
Basically, if you have a lot of things, let's say you have a a game where there's a lot of uh uh enemy players or you have a game with has a lot of like uh background uh crowds and stuff like that.
uh you're going to th those cash misses are going to uh hit you really hard. Uh on the on the on the PlayStation 2 where I wrote some of the initial code for the EI game engine. Um getting uh data from our CPU costs one cycle.
A cache miss cost us 150 cycles. Right? So, that was a huge uh huge amount of time that our CPU can't do anything. We're just trying to we're trying to update all of the all of the objects in the in the world, but we can't because we're just waiting.
You know, we say, "Hey, give me this data." And the hardware says, "Oh, the data is not in the L1 cache. The data is not in the L2 cache. The data is not in the L3 cache. So, I have to go all the way to memory and fetch it." And that takes me a 100 cycles. uh whereas if that data had been in the L1 cache, I would have waited one cycle.
See, you go back here. Uh so here's a scenario. So uh the let's say I'm trying to update all of the positions, right?
And uh if if my positions are all like close to each other, uh then maybe they're all in the L2 cache, right?
So uh in order to process each one of those uh caches maybe uh three of them are in the L1 cache. So that cost me one cycle uh when I put this together and nanocycles equivalent to cycle. or four cycles uh and or if I could do 12 cycles, right? And so um if you see this uh if I have to go to RAM for every single piece of data, then I'm going to be waiting 300 cycles or 100 nconds and for a thousand uh objects that's a lot of time that you're wasting. You want your CPU to be working as fast as possible so you can get as much through.
Now, why does this matter? Because if we're running your game at 60 frames per second, which is now the industry standard, you have 16.67 milliseconds to do everything.
So, if you spend 4 milliseconds running all of your data from memory into the CPU, then you have lost four milliseconds of processing. You can't do anything during that four milliseconds. So the amount of compute time that you have has gone from 16 milliseconds to 12 milliseconds uh which is a significant decrease.
So uh Unity is using this and other EC implementations. Uh so the unity dots go and read up on the unity dots uh and their performance improvement when they went from uh O to uh ECS is pretty impressive for you to look up. Okay, in our next section we'll talk about the game loop fundamentals.
Thank you.
Okay, welcome back. Uh we've talked about uh the memory layout. We've talked about ECS versus uh OO. Um, we've talked about, let's see, what have we talked about? Uh, let's remind ourselves where we've been. So, we talked about the uh ECS. We talked about the idea of why uh what it is. We've compared it with uh object-oriented programming and we've seen some of the limitations of object-oriented programming just from a design perspective.
uh and uh we saw that you can have very simple enhancements to the game in ECS uh which in uh object-oriented programming it doesn't work. So the flying enemy is a real problem for object-oriented programming it's just another system for ECS it's perfectly easy straightforward doesn't break in a thing. uh then we talked about uh the what this means in terms of memory layout. So ECS is a way of making sure that our memory is done and the reason we talked about uh the L1, L2 and L3 caches, we talked about laying out our data such that if all of our positions are together, they end up in the CPU faster uh than with object-oriented programming. Then we talked through some numbers about uh kind of what does it mean for cash misses and how does that impact? Now let's go back to kind of more of the architectural point of view. Let's have a look at the game loop fundamentals. So what is a game loop? Um it's the heartbeat of your game. um when you are sitting there with Visual Studio Code or when you're sitting there with Microsoft Word, uh the program is waiting for you to give inputs, but it doesn't do stuff for you unless you do something, right?
When you type a character, it adds something to the Word document, but it's not making stuff up. It's not doing it.
Uh the um a game is an application that is providing you an interactive view of a simulated world.
So the game needs to show you that world in a continuous basis, right? Uh and the even when you're not doing something like one of my favorite games was Mario 64. Uh, it had this this delightful little feature where if you, you know, you're running around with Mario, you're picking up the gold coins and stuff like that. If you put the controller down, after a little bit of time, uh, Mario just kind of looks at you and s waves and says, "Hey, I'm here." Uh, it's still giving feedback to the player. Um, there's no enemy interaction while that's happening. uh but there is this sense of oh the the world is live and I'm interacting with it. So the the the key difference between a traditional application and kind of most of the applications that we have on our computer is that there's stuff happening within the game that needs to be updated, right?
And so what we do is we decide that every 60th of a second or every you know every 60th or some of the more modern game engines 120th of a second is really really we we go and look at our data and we update it. Now we just decided that we're going to use this approach. We're going to take our data. We're going to store it like this. So, every frame I'm going to go across all of the positions, all all of these positions here, uh, all of these things here. I'm going to take all of this stuff here, uh, and I'm going to update those things, right?
All of this stuff.
Then I can do the animations. Then I'm going to do my physics simulations. uh if I have uh things running around, the AI needs to make decisions. It could very well be that if you stop interacting as a player, the AI is going to come out and thump you on the head and you lose that. I have particles. Maybe I I shot someone or something and there's there's particle effects going around. Uh and I have background elements. If I'm playing a soccer game, I have people in the stadium who are waving their hands back and forth. uh this is what we need to do and this is what makes the game engine development uh so critical.
Um traditional applications you only react when the user does something.
Sometimes you might react if something comes in from the network but in general you don't do anything until the user. So when you get up from working on your term paper and you go get a coffee or you go out for lunch you come back and looking the same thing. Uh, it's perfect for productivity apps. It's it's doesn't use a lot of resources. If if you're if you're in Microsoft Word, it has rendered the last view of your page. It doesn't have to do any updates while you're gone. Um, the game requirements, if you stop playing, the AI will come in and thump you on the head. Uh, and you may lose your life and you may have to restart again. Uh, physics simulations will happen. There's AI behavior. Uh there's all sorts of things that are happening uh that give games that that beautiful visual engaging space that you want to go spend your time in. Uh they invite you into the game, if you will.
So, how do we accomplish this? Well, the it's really, really, really simple. Uh this is the game loop. This is it. This is all there is to it. Um, I start the game. I have my game state. So, we go through and we create all of our entities. We we we create that that spreadsheet with the rows and every row has some columns in it and that defines the things that are in um in the game, right? So, uh while the game is running, I will process my input and I will then update the game state. Now, update the game set means run all of your systems.
So, if I have a movement system, I will go look for all of those rows that have position and velocity and I'll update them. And the consequence of that is if even if there is no player input, if there's a if there's a a a game entity that has a position and velocity, I'm going to see it move on the screen.
Right?
If there is no uh if none of the objects if the systems don't change any of the objects then the render frame uh which displays all of the images to the user doesn't change this right. Um so we need to process the input. So we're going to read the input from the keyboard and the mouse. Uh in this particular course probably going to focus mostly on the keyboard and the mouse. Um then based on everything that happens uh I uh I run all my systems based on the elapsed time and then I render the render frame. It just goes and looks for all of the things that have uh that need to be rendered and it goes there. So um we will talk about um the the how we do timing. You want to have a rendered frame every 60th of a second and we'll talk about that uh in an upcoming lecture. So we can we can you know we always have to have u um flowcharts start the game. If the game is running process the input update the game state render the frame if not clean up and exit. Right. Uh unfortunately the the background hides the lines here. Uh but you can you can think uh you can think about where the lines go here. Uh this the the way that mermaid rendered this particular flowchart. It put the cleanup and the exit inside the loop uh that goes here. So the lines go from start game to here. Uh the yes for this one goes here. Yes. Process input up to game state render frame back up here to this game running. Right. So so this is your main game loop. Uh, unfortunately, Mermaid rendered this here so that I have my clean up and my exit in here.
But hey, whatever. It forces you to think about the lines, right? So, this is your game loop. This is what this is what we're going to be doing this course. But in particular, this here up game update game save. This is what we're going to be doing in our course.
But we won't be focusing a lot on this because it's a 2D uh uh top down game, but this is where you this is where all of our focus for this course is.
And so how does again so let's let's blow out the update game system. Uh so uh in in the particular games that we've talked about so far uh you calculate delta time. So if uh if we're running at 60 frames a second, then the delta time is uh 160th, right? 1 over 60. Um and then we call all of our systems uh to update. So the movement system updates based on delta time, uh the physics system, all of so so this is how that time component comes into the game.
You'll see here that all of these systems have a delta time. So they they they're going to uh update something. So the movement system moves any object, you know, that spreadsheet where you have the position, the velocity, it updates it. Uh the physical system, you may have objects that are falling, colliding with each other. Your animation system, you have you may have like canned animations of, you know, people dying or enemies dying or whatever. This is this is where you do it.
And then the render system, you'll notice that the render system doesn't take a delta time. So the render system just says take a snapshot of the world, render it, and then we render the frame.
So the this this render system prepares everything to be sent to the the um to the hardware.
Okay, that was uh section five. Uh next uh with section six we're going to go into the the core concepts of ECS. Be back in a second.
Okay, let's dig into this uh ECS thing a bit more. Uh the implementation is uh quite detailed. Uh the important part is uh for you to kind of understand that we're actually this is data driven not object-oriented driven, right? So, we're going to go through this again. Um, so um we'll go back and we'll think about this uh idea of uh an entity is a uh row, right? So, basically uh when you when you create an entity, an entity is just an integer. It's just a row. Uh that's it. uh when you call create entity the entity manager hands you the next available ID you don't care which one it is right you just care that you get an ID uh this does not happen no memory is allocated now it's slightly different in our kind of analogy of the spreadsheet but just think of this as you have an infinite spreadsheet with rows and we're just going to pick the one uh the next one that we're going to use but we're not actually going to add any components to it right so a row is basically an ID with no memory associated with it. No components are created. We don't add anything to that row. Nothing is drawn. The entity is just a number sitting in the manager.
That's it. Intentional. It's cheap to create and it's cheap to track. It's just an integer. On the right hand side over here on the destruction and the reuse, um the ID goes back into the available entities. Right. Uh what we're going to do is with that row, we're going to mark that row as ignore everything in that row because it no longer exists, right? And so we can we'll talk a little bit about cleaning up all of the stuff that was in there, but basically entities are just integers that get allocated or destroyed. They're very straightforward to uh add and destroy, right? There's no memory allocation that's going with it. uh you're not allocating and deallocating uh heap memory every time. You're just recycling integers. That's all you're doing.
So, here's the key uh that makes ECS fast. In in traditional object-oriented programming, each object owns all of its data, its position, its velocity, and its health. They're all bundled together into the same object, but they're scattered all over memory. Right? Now, if you wanted to go through all of the entities that have position, you'd have to go and get all of the data for entity one. And it may be a lot larger. You may have textures, you may have audio, you may have scripts attached with it. So, the the the number of components that are attached to an entity may be quite large. So, with traditional uh OOP, uh this becomes large, which means that there's absolutely no way that this position is anywhere close to this position in memory. So when you're iterating over all of the positions or over all of the entities, you're fetching everything from memory for entity one, you're fetching everything from memory for entity two, etc. Now, what we're going to do is we're going to say, okay, uh we know we have positions, right? We we we're going to have these positions. Uh they belong to entities.
They're somewhere. I don't care. Uh but what we're going to do is we're going to store all of our positions together.
We're going to store all of our velocities together. We're going to store all of our health together. So, if you wanted to get all of the data for your uh entity, for your positions and velocities, uh it's very quickly to go through and say, okay, here's all the positions. They're all contiguous in in memory. Uh and then the velocity, same thing, right? Uh and so that's that's uh that's what we're going after. This is what this particular architecture is. um is implementing.
Now let's have a look at the uh component implementation.
Uh we're going to use an unordered map which is an a map from an entity ID to a component. It's not the most cache optimal way. Flat array index by entity will be faster but it's the purpose of this course is not is for you to understand the ECS implementation not to write the most optimal one. Right. And so what we're going to uh do is we'll have a template that adds a component.
Uh the template methods add component and get component let the compiler generate the right code for each component type. Get component returns uh standard optional. The caller must check for null before using the value. This is a safety contract. So in C++ you can um you can use std optional which says you either have a pointer or null. So when you call it, you just have to check for null.
So the system query patterns, uh what we're going to do now is we're going to uh have a way of of accessing the data.
Right? So we've got all these entities and we've attached components to the entities. So let's have a look at the render system and see what happens here.
U the the formatting here is a little bit off because of the two column.
Apologies about that. But uh basically uh I have a storage which allows me to query my entity. Uh so my storage entities with components uh gives me all of the entities that have the position the the position component right that gives me a list that I can iterate over uh query iterate and check right. Uh and so um I do a final check, right?
If pause. Uh so I get my component. Uh I I try to uh I basically go ask for all of the entities that have um a position.
So uh notice here I'm iterating over all of the entities and then I'm asking for the position.
Now um as we saw here we are using the type uh standard optional which allows us to return a null pointer.
And so here when I say uh for every entity uh give me the position I ask the storage to give me the position the component position on the entity E. uh if there is a component position for associated with E then it will return that position otherwise it will um it will um return null and so I can check for that fairly quickly here uh so I'm doing this here so if I want to do a multicomponent um where we go uh if I so now I want to do uh a query for position and velocity.
Uh, basically every iteration I go, okay, so on my movement system, I'm going to go through here and I'm going to ask for all of the entities that have a component position.
Uh, and then um, so entities are all of the entities in component position. And then for each one of those, I'm going to ask for the velocity.
Um again this returns a pointer that's either null or has a value. So I can check here um I basically say get the component uh here I get the position and here I get the velocity right. Um this is kind this one's a bit redundant here because we know we have a position here but this is just there for showing the code how it works. Uh and so here I have if I have a position and a velocity then I'm going to update uh my my uh pos my my position by adding the velocity times the delta t. So the delta t is passed into that again. So so systems aren't called with parameters. You don't call a system with a list of all of the entities.
uh you a system goes to the the shared data space and says give me all of the things that have this pattern. Uh if we go back to the spreadsheet analogy, you're basically saying look at all of the rows that have these values. Okay, that's what we're going to do.
Okay, so we have defined these query patterns.
We have these entities.
Uh now let's look at this particular set of components. Uh and a note that we're talking about components that don't exist yet for this class. Uh but we this will help you understand it. If I have a player uh I'm going to have player that has a position has a velocity. So they're standing still at this point.
They have a size. They have a health which is this is my current health. This is the maximum and I have some sort of input here where I'm going to read from the input the um this is becoming a complicated object.
Uh I would have to uh make a fairly complicated hierarchy to represent this.
Now I would have a so I would have a movable object or a living entity and a playing character. Here I just pick it. So what if an enemy can be controlled to by a player? What if uh somehow uh you have uh an enemy character that uh transfers that you want to uh transfer the the control to uh or like in FIFA uh where you control a player but you want to go to the other player. What you do is you just remove the component from that first player and you add it to the second player and instantly your systems are going to be go, "Oh, that's the player that I need to control."
That's how simple these things work.
Now, think about it for a second. If you're if you've got a bunch of players in a FIFA game uh and you have the player input on only one player, then the other ones all are being uh moved by AI.
Uh when one of the the things about FIFA is that you can switch from player to player. Basically, it automatically switches to be the player that's controlling the ball because that's that's the fun player. uh you just take this component, you remove it from the player that just kicked the ball and you add it to the player that's going to receive the ball. Uh and then there you go. You you've then transferred it. Try to do that with object-oriented programming. You'll see that it becomes a lot more complicated.
Now let's have a look at um what the enemy looks like. So, um, my enemy entity, uh, is here. I have my position, my velocity, my size, my health, and my enemy AI.
Now, what's the difference between my player? My player has player input, and my enemy has enemy AI.
Um, you know, sometimes you may want to let your player do things by themselves.
What you could do is you could switch out the component for player input to AI assist and then the AI could help you there. So all of these things are uh very very beautiful because the the the object can be your your entities can be changed at runtime without recompiling your code just by changing what components are added to that.
So a static object so this is an object that has velocity and position. This is a size this is an object that doesn't have movement and doesn't have health, but it's something that we need to decide whether we've hit or not. So, what we're going to do, and again, this is a a a a component for the future, but you can start to see the power of this thing is that we're going to have a position of size, and we're going to call it collidable, right? Uh and if I wanted to uh uh display it, I would give it a color, etc., et. But that's essentially how that's going to work.
So the data oriented uh design uh uh benefits is that one big class with everything you you make a game object and you end up with all of these you know a game object has to be anything right so you have position you have all of this stuff you have all sorts of stuff you may end up with 50 uh data types which you're not using in one of the inheritance. Uh large objects, hundreds of bytes, virtual function overhead, um cache misses, un you know objects, uh your wall object has a velocity um field that is always zero. So the wall never moves. Um your um you know your your flying your your enemy object has a flying object features. If you decided not to do the diamond inheritance, um it's all fine.
Now with ECS, you have your tiny components which are small number of bytes, no virtual functions. Uh you don't have to decide at runtime, am I running the function for the parent class, you know, the dynamic dispatch thing, uh or the diamond inheritance, uh am I am I using the code from the enemy or am I using the code from the flying enemy? This is uh what we're doing now.
We're we're trying to build something that goes really really really fast to process the data and that's why it works so well in this context.
Um modern CPUs uh lay out uh the uh data in u in data.
So let's make this concrete. A cache line is 64 bytes. That's the smallest unit a CPU can fetch. Uh so in OOP one object may fill that cache line to process it and discard the line and the next entity's line. Uh in ECS because we put the data together um we can have eight positions in that cache line. So remember we we said that that fetch from memory is going to cost us 300 cycles. Uh well for entity one 300 cycles for entity two 300 cycles. Uh if we're just processing the positions for our entities the first eight entities can be fetched in one shot. Right? So instead of having uh 8 * 300 cycles wasted uh we're wasting about four to five we're you know we're waiting four cycles per one of these entities. So our wait time has gone down. that means our processing time has increased. Uh so we can have way more data coming through the CPU. Uh the purpose of a game engine the purpose of modern game engines when in fact the purpose of all game architectures whether they were old game engine or not was to get as much performance out of the CPU as possible.
So the just uh kind of showing uh how this works. Uh this is um on OOP to drive home the point again your memory looks like the left hand side.
You've got entities that are in different positions in memory and in particular if I'm looking for the position of all of the entities they are all over the place. So, it's going to take me a long time to get them with ECS. They're all kind of tied together.
In the next uh section, we're going to talk through the very very simple game that we're going to be building uh for this particular class. See you soon.
Okay, we've done a lot of theory. Uh it's time for us to kind of uh think about a real world. Um the payoff actual running code that demonstrates all three letters of VCS at once. Uh the red game uh red square game is intentionally minimal.
There's one entity, three components, one system. That's it. The point isn't to build something impressive. It's to see the entire ECS system running kind of together.
Um when you um ask yourself when you look at this code which is you know what you're doing this week as you're reading this code is I want you to ask yourself where is the entity where is the component data and where is the logic that uses this data. uh you may be uh new to C++ uh so use uh LLM to explain the code to you uh so you can go through and understand how it's all built. Uh so let's uh go through uh and see what's happening here.
The um red square game is going to create a single entity. Uh it's going to be a red square. Um that's what the entity is going to be. Not notice uh you'll see in the code that we are going to uh create a single entity. We're going to add three components position, size, and color. Uh we're going to use the render system to draw the square. Uh then we're going to run the game loop at 60 frames per second. Uh and then this gives you an opportunity, a very simple game to understand how this all goes through. So the the files that I want you to look at are CPP game main.cpp and then there's the render system which is render system.cpp.
So pulling code out of uh the the code uh in main cpp. This is how we create our square. Right? So I've built an entity manager. Go read the code for the entity manager. Uh please kind of dig through that. You're going to the entity manager is going to largely remain unchanged for the remainder of this course. Uh we'll add stuff to it and look at the component storage. So we need two of these things here. Uh and then what we're going to do is we're going to create a renderer, right? So we we have a render system. Uh and go and read the code and understand what this means. Um this is where we're using SDL3 etc etc. Now we create our entity, right? This is it right here. We create our entity and we print out the ID, right? So if you're not familiar with C++, um you have the um here's how you print out to the the stream, the output stream. Uh and what we're doing is we're just uh printing that uh we have created an entity At this point, we don't have uh any components.
So, what are we doing there? We're using the entity manager. Uh go read the entity manager, understand how it does.
Use your LLM to ask, explain this to me.
Explain this to me. Then go read the component system. Um again the key component here is that create entity creates an integer for the uh returns an integer. Creates an entity but there is no data. So it's like a virtual row. Uh and so now let's uh let's go back to our code and have a look and see what else we do. Um what we're going to do again out of main uh I'm going to do component storage. So, I'm going to use the component storage manager and I'm going to add to the red square component a position of 350 250. That's what we're doing. And I'm going to add a component to the red square of size 100.
And I'm going to start make it red.
Right. So, the position of my square is 350 uh positions. And uh so 350 uh pixels over and 250 pixels from the bottom. Uh in this I' I've made the game engine such that the lower left hand corner is 0 0. Uh now the square that I'm uh play and the window is uh 600 by 400. So um I I set my pixel here uh to be so but sorry the window is 800 by 600. Um, and I want my my my pixel to be in the middle. Uh, I want the middle, sorry, I want the middle of my square to be in the middle of the screen. So, I'm offsetting it by 50 and 50. Um, we'll see that. Uh, but anyway, so what we do here is that I set the color. So, this is what I'm doing. I'm setting up a square of 100 by 100 that's going to be exactly in the center of my screen. That means I've offset it by uh the center is 400 minus 50 which is half the half the width of the square. Uh that centers it in x and this one centers it in y and I set the color and my components are defined here. Go find them in the code and go understand how this is all working. Okay.
Now, the render system, which is the first system you're going to look at, uh it's it's the one system that doesn't use time, but basically we don't need time here because nothing's happening.
Uh what we're going to do is we're going to go and ask for all of the positions that have the type, all of the entities that have the component position. Uh and then I'm going to iterate over all of those things. Uh I'm going to do a double uh you know, just make sure that it has if it if my entity doesn't have size or it doesn't have color. So, it's going to skip all of the other entities.
It's okay. We only have one right now.
Then I get the position, then I get the size, then I get the color, and I draw the entity right there. That's this is my render system. Notice, uh, I could have thousands of objects, but only one that has size and color, and I'm only going to get that one object. So the pattern is query, filter, and process.
That's a that's a system pattern that we're going to see time and time again.
Uh as I said, the coordinate uh the the coordinates in this game 0 is at the bottom left here and it goes up there.
Um this matches the mathematical view of the world. um the consistent with physics and traditional uh the STL renderer uh actually I I do a conversion such that because STL has 000 up here.
So what I have to do buried in the code you'll see somewhere that I I do this I take the window height minus the position of Y minus the size height and I get the rectangle Y.
So my game loop now looks like this. Um I set my running to be true. I set my STL event there. I'm not using it. Oh, I use it just to get rid of uh to escape.
So I can uh quit the window. Uh and so while running uh do this and then I basically here uh phase 2 update. Uh so now we have a loop. Uh and so what we're going to do now is u sorry.
So basically um this this for uh while running um I process all of my events is what we're doing here. Uh if the if this event Q is empty, nothing happens here.
Then I go uh to the next step which is to um uh process all of my other systems. But we don't have any update systems. We only have a render system here. Uh, so we've processed our input here and we process all of our systems that move stuff. Uh, and then here I'm going to go through and I'm going to update it. So, uh, I my render system. I clear the background. I render with my component storage uh, which basically goes and takes all of my components. Uh, and then I, uh, present it. And then I delay by 16 milliseconds. Um we are going to uh talk about uh the game loop uh in a future lecture. So don't worry about it for now. It gets it's a little bit more complicated than that. Uh but I want you to understand how that works.
And that's it for uh the game loop and the red square game. Uh you will be changing that uh and your submission will require that you submit a screenshot of that change uh in a way that's described in the exercises.
Thank you. I'll be back in a second.
We're back. Section eight. Here we go.
Um, we want to do unit testing on all of these things. The whole purpose of decomposing uh our um engine this way is that these systems can actually be tested. So, uh what are we going to do?
We're using a system called catch 2. Um you've all done unit testing in Java and Python etc etc. Uh we built a game. So we've built a game and we can see the red square on the screen. So why do we need tests? Um the visual um verification breaks the moment you change something, right? Tests don't. Uh so you you could end up in a situation where the um the the the up the the update mechanism is not updating your things right now. This is a very simple thing. Uh but what we want to do is um do our testing. Um unit tests are going to use the same ECS code that your um that your uh code test. So our unit test here is going to create an entity. So let's go through uh this particular test here. Um I'm going to take a component storage. I'm going to create an entity.
I'm going to add a component position and that then I'm going to get a position. I'm going to get the component from that entity. So then I'm going to require that. So this is my require part here. And so let's just kind of go through this step by step. I have an entity manager and I have a component manager, component storage. This is the same code that our engine does. And what we're going to do here is we're just going to test that when I create an entity and I add a component to it and then I get a component from that entity, if I get that the the position component from that entity, then I'm going to get uh the same value out that I got there, right? And so here 350 350 350 250.
That's basic unit testing.
So, here's another example with catch uh two. Uh my I want to make sure that my I'm going to do a red square game validation. Uh and so I'm going to have an ent entity manager, component storage, and entity manager square. So, I'm going to add all three components to my entity. Now, I'm going to add my position. I'm going to add the size. And I'm going to add the color. Uh and then I'm going to have the position equals component storage. I'm going to ask for the position and I'm going to make sure that this is there. So, what I'm testing here is that when I add size and color, I don't destroy my position.
So, the way that you run the tests, uh you can run them this way with C test minus test directory. Uh and then, uh when you um it's the same as all of the testing. Uh when the test fail, cache two shows the the file and the line number of the failure and it tells you what the require is.
The unit test uh output looks like this.
Uh you have your test project, path to build, etc. Uh start one and it prints out a whole bunch of things, right? Get used to using uh the unit tests.
Uh it'll become uh very useful uh in the future as you're starting to go through that. uh I'm using it as a mechanism to make sure that when I update the engine, I'm working with the large language model to build the engine. Uh and one of the requirements is I've that I've got a whole bunch of tests in place such that in future development uh the tests don't fail, right? And so that's uh you can see the the the the failure here.
Uh if you want to if you want to see what a failure looks like uh you can go back here uh and change this number to 351 uh which obviously is not going to work because we know it's passes at 350 and run your test and you'll see how the test fail. Okay.
And this okay so there we go. Uh the unit test structure is very similar.
unit test uh uh frameworks are all fairly similar. Uh we're using catch 2.
Um and so this is what we're going to be doing here. You have your test case, you have your setup, and then you have some sort of section to that. And then you have uh some more setup, and then you do your requires something. You run it like that.
Uh this is uh how you run all of your test functions. Um there's a catch 22 macros test case section. uh and require and require false. Um basically that's pretty much all you're doing. Uh and then that's it. Now let's talk about the uh optional component and system antiatterns.
So um we have made it very very very clear that we don't want to have objects, we want to have data structures, right? the the the whole point of what we're doing is to make sure that things that the data is clean and that the systems are the ones that are going to be processing it. So if you're ever tempted in this course to write something that has like code in it, don't. That's just don't do that.
That's not what we're going to do. Um the other one is that you can create you go, "Oh, I don't want to have all of these components. I'm going to create this large component. Uh it's going to be a player. I'm going to have a position, a velocity, a health, and a score. And you can't reuse this, right?
So, this means that you have to have an update uh that your update system has to look for something that has a player, but what if your enemy has it, you know? So, so, so keep it as simple as possible, right?
Um, the other one, the other kind of bad component is similar to this one. This is a player. Uh, here's the god component. It's like, okay, in that spreadsheet, we're going to have 50 more fields. Uh, and it's just your your system code becomes really really really complicated. Um, if you Yeah, and you you'll experience this at some point. Um, you want your uh components to be as small as possible.
Uh, components with logic, we talked about that before. We don't have any logic. Uh, it's just data, data, data, data. Components are only data. Just keep saying that to yourself. Again, uh, and don't have components that depend on each other. Um, then mutable shared space um, the global state. So gravity is a global uh sorry this the screen width uh should not be part of a uh shared space uh multiple systems uh we're going to create a singleton and we'll talk about the game state later uh but basically uh components should be things that belong to an entity. If there's such a thing as screen height and screen width that belongs somewhere else in the game that belongs in a global uh piece of data that all of the systems access the god system. Uh again your SIS so we we we've made a case for making our components very simple and our systems should also be very simple uh you know so the update game system uh which does everything together not exactly the right thing you do or just a long uh long piece of code that does everything uh no the single responsibility principle that you learned in object-oriented programming applies to systems. your system is going to have a particular single item to do. And I've been working really hard in the development of this engine to illustrate that. Sometimes I've had to stop my uh my partner, the LLM programmer, and say that's that system's too big. Break it up. Uh break it up.
If your systems your systems should only uh modify a particular component, uh this can get very uh uh this this can get very complicated and messy. Uh but basically uh a system um you know for example the the render system should not update the position. The render system updates the current position and it displays it. And there may be another system out there maybe an AI system or a movement system or something like that but render systems read only for components. So that's the that there uh don't have hidden dependencies uh the implicit execution uh fragile um you know for entity you know the physics system u you assume that the phys movement already ran uh so the order matters. Sometimes you actually do have to put the systems in the right order.
Um but in general you want to have your systems run in parallel if possible. Uh so if you do have to if you do need a dependency to be introduced uh introduce it. Uh the other thing is remember that your systems run every 60th of a second.
So if you decide if you think that system A should run before system B, you can actually flip them because system A will run and then the next frame system B will run and it'll have that effect.
Right? Remember that this this data is is permanent over time. Uh not per it's it doesn't go away every frame. So you can actually uh put these things in in an order that makes sense.
Um if you uh if your system is querying too much data u then that is also going to be um uh difficult.
Um okay so let's talk about the workshop overview and the summary. Let's just see what we're going to do. Um we've uh created a very you're going to create a variant of the red game. Um I want you to build the game using CMake.
I want you to run the unit test to verify the implementation. And then I want you to uh create a blue square.
Don't change it to green, change it to blue. This is a type is RGB. Uh this is so we're going to do a red square to blue. Uh this green here is a typo.
Ignore that. uh I will fix that and push it uh so that the data is correct in the presentation that you have and I want you to move the position to there and then I want you to write a unit test work blue square CPP to validate your changes and then I want you to take a screenshot of your running game.
Uh basically I want you to look at the simplest possible ECS game. Uh and I want you to go and look at the component data and then run the unit tests and how to coordinate the system in practice.
Right? Uh so the unit test file, the test output and the screenshet will go into the submission directory and you will also run the LLM self-evaluation. So what did we cover today? Wow, we went through a lot. This is the foundation for our course. We we talked about the ECS architecture, entities, components, and systems. We talked about why we're doing ECS's.
There's a whole bunch of problems with object-oriented programming for dealing with large amounts of data. We talked about the game loop. We talked about the data oriented design. We talked about the red square example. And then we talked about the unit testing. And then uh you are going to build a blue square, right? uh experiment with the components. Uh you can try adding velocity or something like that, but for now just just do the blue square. Uh it'll get more complicated, get settled with running it. Uh there's also some Python scripts there that you can use to run uh this instead of having to run CMake. Uh open the Python files up and see what they do. Um start reading the codebase, read the entity manager component search. uh I'm um I'm uh relying on your curiosity to drive into this. So in uh next class we'll talk uh we'll expand our ACS implementation uh and we'll keep going.
Right. So here's a whole bunch of reading. It's easier to read this on the on the course website. Um and that's it.
Thank you. And let's have fun this first week of the class where we build the most boring game ever written. Thank you. Have a great week. Bye.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Introduction to Problem Solving Part - 1 | Lecture 1 | Intermediate DSA
ascensionix
107 views•2026-05-29
🚀 BCS613C Compiler Design | Module 1 to 5 Schema Evaluation 🔥 | VTU 6th Sem 💯 #VTU #bcs613c #exam
Pranavaa-y4y
104 views•2026-06-02











