# CS50 2D - Lecture 4 - Super Mario Bros.

## Метаданные

- **Канал:** CS50
- **YouTube:** https://www.youtube.com/watch?v=wjzaqNwZrPM
- **Дата:** 25.04.2026
- **Длительность:** 1:38:29
- **Просмотры:** 5,309
- **Источник:** https://ekstraktznaniy.ru/video/49635

## Описание

This is Lecture 4 of CS50 2D, in which you’ll learn how to build a Super Mario-style platformer with tile maps, animations, procedural level generation, collision detection, entities, and game objects in Lua.

To take the course for a certificate, register at cs50.edx.org/2d. 

***

This is CS50, Harvard University's introduction to the intellectual enterprises of computer science and the art of programming.

***

HOW TO SUBSCRIBE

http://www.youtube.com/subscription_center?add_user=cs50tv

HOW TO TAKE CS50

edX: https://cs50.edx.org/
Harvard Extension School: https://cs50.harvard.edu/extension
Harvard Summer School: https://cs50.harvard.edu/summer
OpenCourseWare: https://cs50.harvard.edu/x

HOW TO JOIN CS50 COMMUNITIES

Bluesky: https://bsky.app/profile/cs50.harvard.edu
Discord: https://discord.gg/cs50
Ed: https://cs50.edx.org/ed
Facebook Group: https://www.facebook.com/groups/cs50/
Faceboook Page: https://www.facebook.com/cs50/
GitHub: https://github.com/cs50
Gitter: https://gitter.i

## Транскрипт

### Segment 1 (00:00 - 05:00) []

Hello world. This is CS502D and this is lecture 4. Today we'll be looking at Super Mario Brothers, which is a very famous, possibly the most famous, it's arguable, uh, sort of franchise of games that we'll be looking at through this term. And I'm even joined on stage by a very fitting Mario plushy, which is very cool. And it fits sort of harmoniously with the slide here as well. Um, as you can see also by the slide, we won't be necessarily using the exact Mario assets today. We'll be looking at a really cool sort of uh Creative Commons version of the same platformer tile set, but we'll be exploring all the ideas that ultimately make up Mario. So, this is the OG Mario and also, you know, CS50 traditionally has had a history of using Mario in its own materials. And I think it's a great illustration for a number of reasons, but it's, you know, in a lot of ways a great transition to this idea of a virtual world in our game as opposed to sort of, you know, so far we've looked at things kind of abstractly. We have Pong, which is, you know, these rectangles moving through space. Flappy Birds, we looked at illusions, but there is no really a world representation per se. And even last week when we looked at match 3 and then breakout. These are relatively abstract, not so much worlds. But today, we'll be actually looking at how to model something similar to this with a few differences. But, you know, we see we have enemies here moving around. We have a ground point at some part. We have Mario jumping, which we have looked at jumping through Flappy Bird. So that part will be somewhat uh of a reminder from Flappy Bird. But you know, we have blocks, pipes, and we have a background. All these things. We feel like we're in a space, which is super cool. So this is the original Mario for NES. Then Mario 3 came out for the NES, which also was very sensational when it came out. An amazing game. I grew up playing more so Mario World for the Super Nintendo, which also has a tremendously positive reputation. And then nowadays, you know, Mario's doing all kinds of other stuff in 3D, and here he is with a dinosaur for Odyssey. Also a tremendously fun game. very different sort of the franchise has changed quite a bit but this the core is still there and we'll be taking an initial look at how to sort of start representing these virtual worlds in our games and start to really model things that are I think a lot of fun some of the things we'll be looking at today so we looked at tile maps last week with match three we're going to be looking at the same idea this week except instead of having a puzzle board now we can use the same idea of these sort of IDs mapping to these quads that we draw onto the screen as being tiles that then matter in terms of like are they collidable? What do they look like? So on and so forth. 2D animation's important. Mario, when he's jumping, he has a different pose than when he's walking. And sort of that also when he's walking, he's going frame by frame from one sort of stepping motion to another. He's not just a sprite moving around. We haven't really had this idea of sprites that actually change how they how their appearance is frame by frame as they move. So that'll be new. This week has one of the more sort of I think representative procedural level generation focuses where we actually will make all of our levels in code. We won't actually handcreate our levels, though you certainly could do this. And then we'll talk about how platformer physics differs from AAB and allows us to detect whether our sort of Mario avatar is hitting blocks when he's jumping or moving or so on and so forth. A very simple version of AI for the sort of moving gloombaesque creature we'll have today, which will be a snail. And then lastly, how to have things like powerups, which will tie into the problem set. So, our goal is going to be this representation on screen. I've taken a sort of range of screenshots to show where we'll be going. You see, we have the classic initial starting screen, but also all the levels here are just randomly generated. They've got different backgrounds, different tile sets, gaps, and chasms, and sort of different props in the background, and also blocks with powerups and things like that, gems that we can activate through jumping. And I think it'd be good to actually demonstrate what it looks like. So, if we could get a volunteer who'd be willing to come on stage somewhere. Yes. Yeah. Why don't you come on up? — Hey, this is Dane, right? — David. — Oh, David. Okay. Nice to meet you. Okay. So, I'm going to go ahead and hit this button here to get this started. — And now, feel free. — Okay. — Press enter. — Okay. And you are now controlling Mario. It'll be the arrow keys to move. You can press spacebar to jump. He hit the snail and caused it to uh caused it. So, some of the blocks won't have will have gems in them, some won't. But you, as you can see, by jumping onto a snail, you will hit it. You the terrain is collidable. So, you're walking on top of the terrain. Some blocks. So, this block, for example, doesn't have a gem in it, but if you were, it's random in terms of how this is implemented. Which one will have a gem in it, but if you keep moving forward through the level, we might get lucky and get a block that does have one inside of it. They all have different tiles, different a different frame chosen for them. So, that one doesn't either. We're getting unlucky.

### Segment 2 (05:00 - 10:00) [5:00]

— Oh, that one did. So now you can if you were to jump on top of it. — There you go. Or close enough to it. — This is my best game yet. — It's pretty impressive. — This is I spent a lot of hours playing this as a kid. — It shows. And so we sort of have all the pieces here. Even the, you know, you're walking and hitting these sort of pillars in the level, collision on these snails, you got this jump, you your frames are changing. If you were to theoretically maybe even uh pretend of course to uh fall down. — Oh, so you want me to — and we do trigger sort of like a sort of death state at the end of the level. But thank you so much. Yeah, that was Yeah. — So, as we can see, there was quite a lot of pieces there, a lot of new things and sort of we've taken a step forward in a way that we haven't really done before in that now we are thinking in terms of existing in a world. Flappy Bird was our initial step and it was doing similar sort of philosophically I guess spiritually it was making us feel as if we were somewhere with this illusion that we have this scrolling background. We've got these pipes that are moving. Flappy Bird is in the center of the screen and not really moving technically, but like you could jump and you could use gravity to try to avoid the pipes. And there was this feeling that we were somewhere in space. With Mario, we actually feel like we're moving. We have a camera sort of tracking Mario. We've got an offset into how we're rendering things. We have this tile map. And here, this is this slide sort of illustrates what tile maps are. But essentially, it's not all that different from what we looked at last week with the grid of tiles with match 3. Except last week, we were using them to render tiles that could we could swap and then calculate matches. This time, we'll be actually using them to draw these blocks on the screen that we can walk on. We can walk up against a butt. In the case of, for example, the blocks that we could hit, those were technically game objects, but they're similarly functioning in terms that they're just being rendered on the screen as a subset of that texture and they move around in space relative to the camera. And so just with this drawing of these little pieces of this texture and then putting them in a data structure that models some sort of level where maybe if you envision zero being space, one being the ground. In our example, it's slightly different. And it's five for the space and three for the ground. But you can essentially map that to a level that then we just calculate, okay, at x ycoordinate, there's a tile there. We should collide with it or not. Render it as a tile or not. And then we just sort of feel like we're in a space now all of a sudden magically. So in order to illustrate this, why don't we go ahead and step into some code with tile zero where we'll look at some static tiles. Go ahead and transition over to the source code here. I'm going to close the Mario example. Let's open up tile zero and I'm just going to run it so that folks can see what it looks like. But it's very simple. It's just a and this is just some very basic art that I've put together here just before we get to the actual nice tile set that we're using in this lecture which is a very impressive tile set and we'll show it. But we just have a background color being drawn here. And this is just a random color that I'm clearing the screen with. We used love. graphics. clear in the past. That's exactly what we're using here for the sky. And we're just not drawing tiles there. And then we are drawing tiles where the bricks are. And so if we envision just sort of this 2D sort of grid like we did last time, we can visualize how we do this in the same way that we did the match three grid. We'll look at the code here. The tile size, we're working with 16 pixel tiles. We've got two ids that we care about, sky and ground. We got a tiles array. This isn't all that different from the tiles or the sort of the match three tiles table that we used last time. And then we're just going to splice up our tile sheet, which in this case is just a two tile sheet, which is going to give us back just two frames that again we can index into and draw with our one texture. It's a very uh meager modest texture here. As you can see, this is a uh in this case, this one on the right here is just VS Code's way of saying this is transparent. There's technically nothing here on the right side of this texture, but here on the left is a brick texture that I've sort of created very primitively using a sprite or something. And then here we have this empty so that we can still index into it, treat it as if it's drawing a tile, but in truth it's just drawing opaque pixels. Nothing's getting drawn to the screen. So we have a background RGB that gets randomly selected. Here we're just populating again a 2D array, a 2D table of tiles, which are just going to be uh in this case ids. In this case, they're constants mapping to sky or ground. If we go down to the draw function here, we have a clear color that's happening and then literally just a two-dimensional loop that's going to iterate over this 2D table structure that we used similar to last week. And it's just going to draw it's going to grab the tile and it's just going to draw again indexing into our quads table to get the exact offset that rectangle from one to two. it'll get either the one or the two and then it's just going to draw it at the xy minus one to get coordinate space multiplied by tile size as the offset.

### Segment 3 (10:00 - 15:00) [10:00]

Very sim very simple very similar to what we did last time with the match three grid. Nothing is really new here. So if we go to tiles one I think the thing that really stands out is that we don't have you know a this genre at Mario is kind of like a scrolling platformer or 2D platformer. You've got like scrolling shooters, other types of genres, similar in similar vein to that in the sense that you have a background, sure, but it can't just be static. We want to be able to move around and feel like we're in a world. And to do that, we have to have some way of shifting this map around on the screen. And so, we have a handy way that we can start to do that with a function called love. graphics. transate. And so what this will do is rather than everything being drawn just with an assumption that it's at 0 love. graphics. transate will take an x and a y and then everything that you draw thereafter will essentially be offset by that x ycoordinate such that you can essentially mimic a camera. If you think that we have x 50 here, everything will be shifted 50 pixels forward. And so you can sort of think of it as an inverse sort of direction of the camera moving. So if we were to shift everything 50 pixels to the right, we're going to feel as if the camera has moved 50 pixels to the left and so on and so forth for every direction on every axis. And through this method, we now have a way to sort of envision a camera, sort of a couple of variables, an X and a Y. In particularly, we'll focus on X, but you can have an X and a Y value that model more or less how we can think of the world shifting in the way that we would think of a camera moving on our scene, which then gives us the ability to track a character as we could sort of anticipate and then move through our 2D world. So, with that, let's go ahead and look at the code for this. This is going to be a simple addition. Going to close tiles zero. We're going to open up tiles one. And then I'm going to go ahead and scroll right down here. We see a camera scroll speed. So, we're going to scroll. This is a test example where we're going to scroll. And actually, why don't we run it just so we can see what it looks like. So, it's the same example as last time. We have a color in the background that's random. So, that's why it's pink instead of green now. But, if I hit the arrow keys, you'll see that I am indeed scrolling the screen left to right. And all this is really doing is just drawing everything with an offset. So, rather than drawing everything, if we assume here more or less everything is at 0 0, then it's a very simple sort of way to see that. Okay, four tiles down at, you know, x * 0 with the first tile gets drawn x * 1 the next or tile size * x* 1, tile size 0, tile size* 3 * 2, whatever, they all get drawn sort of relative to the top left corner of the screen, which is 0 in love 2D's game space. But by using love. graphics. transate, if we scroll down here, notice that the camera scroll stays at zero. We're going to manipulate this variable using delta time just here like anything else that we've done just some variable that's going to change based on input here it's going to be a delta time multiplication on that constant and then if right before we do our actual drawing we just say okay I'm going to translate by the negative in this case we're rounding down with math floor just to keep in the event that you don't you have fractional sort of translations you can have artifacting and weird sort of lines especially using push and other Just in other domains, you want to keep things rounded down for rendering smoothly. But if we go ahead and just translate by our negative camera scroll, we end up actually seeing a shift as if we are moving left and right throughout our scene. If I hit left, if I hit uh the left arrow key, we do indeed move left. And if I hit right, we do indeed move right. We are negating, we are negating rather the variable because we want to make sure that when we press left, we think as if we're moving the camera left. But recall we have to move x positive in order to actually get that effect by moving the cam to move the camera left because everything gets drawn with love. graphics. transate with that offset. So we have to negate if we're thinking in terms of left and right in our input, we have to negate that in order for that to actually reflect in camera space. But that's essentially all we have to do. We just negate the camera scroll variable we're keeping track of. And now we all don't have to really manage anything in terms of our tiles. they will automatically look as if they're drawing in the right spot because we are adjusting the entire coordinate system effectively to account for this translation. So that's love. graphics. transate that takes care of the scrolling. So we have one very important one of the most important pieces tackled. But now we have to also factor in okay we have a world sure we can do that but we should also be able to have a character on the screen obviously that we can start to move around and we can sort of envision this scrolling these variables following the character you know the character should be in the center of the screen we can do all of that but to even get started we need to have some character on the screen to begin with so what we'll do is I'm going to go ahead and open up character zero and let's just demo that see what that looks

### Segment 4 (15:00 - 20:00) [15:00]

So, as you can see, very simple, very uh similar to what we saw last time, except now we're using our little Mario avatar, a little alien looking guy standing on top of the ground. So, positioned just so that his feet are touching the top of the ground. If I move I can still move the camera left and right. So, this isn't quite accomplishing our full intentional goal of having the camera follow the player, but you know, we can see that we're one step closer to our end goal. So, things are moving in a good direction. Let's go ahead and look at the code for this. So, there's a height and width of the character that we should factor in for positioning. And so in this particular sprite sheet, which if we open that up really quickly, this is what we'll be using throughout the remainder of these examples. This is our avatar, his sprite sheet, and it's a sort of set of similarly sized sprites all in one texture. This is just a single row and they're all 16x20. They've got different frames in here. You can see there's somewhere he's just standing still. He's got looking to the right or whatever this might be. This looks kind of like tiptoeing. On the right, very far right here, we have some walking frames. We've got jumping frames, which are these. We've got like climbing frames. We've got like a ducking one here. And then something that looks kind of like he's unhappy right here. We'll use a few of these in particular, the walking one, and the jumping one here in a little bit of time. But for right now, we only care about this very first frame. And it's not unlike what we've looked at previously for match three and even for this example with tiles. We're just slicing up this texture into these frames. And then we can just index into this array that we know we'll get back through generate quads for whatever this tile sheet is. And then if we want him to look as if he's moving, we can just set the ID of that frame to whatever this end is here, 11 or 12 or so. Or one will just be, you know, standing static. So if we go over to main. la, we're still using the constants there for the tiles. So we're going to go ahead and chop up the character sheet. we get its own set of quads here. We're going to set the character X and Y and we're going to offset basically put him right above. We're essentially setting it at seven uh tiles down right above tile 7. We want to set it minus the character height so that he's standing right above where the tiles the solid ground tiles begin in our array, our 2D tile, our 2D tile map rather, which is what's done here. And then if we come down here, we'll see that we have a draw call done just at sheet at quads one at character X character Y, which if we look up here again, character X and character Y are just being set. So there it's pretty much right in the middle of the screen positioned right above where our tiles are being drawn. So that's fine. Things are working pretty well for that simple example, but clearly we want the character to be able to move. And that's kind of the one of the important aspects of Mario. To win a level in Mario, you have to move from the left side of the screen to the right side of the screen. And then in the full game, there's a flag that you can jump on and pull down. And that ends up transitioning to the next level. In our example, that's probably part of the problem set is actually implementing that stage of it. But in order to even get towards that, we have to be able to move. So let's start to think about how we can do that. And let's look at character one. So I'm going to go ahead to character one. Let's run this to see what it looks like. So we have kind of the same thing again. These backgrounds are all random. So we can see we indeed do have a character that is moving. Now it's not moving the camera. So there's a couple of pieces here very clearly. We have one to move the character. So the character in the world is moving. But we also have to move the camera along with the character and we have to make sure the camera is sort of statically fastened to the character's position. So we'll look at how to do that in a second. I'm just going to go ahead and open up character 1's example so that we can see what that looks like. I'm going to close all these other examples. It'll be down here in our update function where we can see that we're just doing the same thing that we've always done historically. We're just changing the x value by delta time through some constant that we've defined as character move speed. All of these constants typically are tweakable with the exception of maybe constants that index into your tile sheets. And then we do all the same code as before. Pretty standard. We're not calling uh we are calling translate ultimately, but we've essentially replaced the actual logic that adjusted the camera scroll with the character's position. So now we have to essentially marry these two ideas together. So that brings us to character two, which will be the tracked hero, which allow us to actually start doing just that. So, let's go ahead and close this. And then I'm going to go ahead and open up character 2. And then if I move this character, we'll see indeed somehow the character is

### Segment 5 (20:00 - 25:00) [20:00]

remaining completely within the middle of the screen. But as we can see by the scrolling of the tiles and the fact that we're reaching the map's sort of end points, we are indeed moving our character in world space. So, how are we accomplishing this? I'm going to go ahead and scroll down. We are doing this in the update function. So here we are doing the same thing where we are moving the character's position because the character does need to move in the world. That's not something that needs to change. The difference is that now we need to essentially set the scroll to be equal to what's effectively the offset of the character to its left side effectively. So, we need to essentially take the whatever the character's x position is, subtract half the virtual width, which will put it approximately in the center of the screen, and then add the character's width divided by two because remember everything is drawn or everything in our games is relative to its top left corner. So, we need to shift halfway through the character's width to be properly centered. And then now the camera's scroll will always just be whatever that half screen width is away from the character. Meaning that no matter where our character moves, the camera's position effectively is going to track the character's position. And so here we can see we didn't really have to change anything here. The camera scroll is now just managed in addition to the character's own position being set. The c the character's position is now dictating with the relative with the offset of half the screen width where the camera gets drawn. So that's great. We have tracking now. Cool. There's a few pieces left though. Things are a little bit static. Things feel a little bit weird. The fact that we're just kind of moving this static sprite around feels unnatural. So, the first step is going to be let's animate the character. We've seen the sprite sheet. We've seen that we have this ability to do this. Let's actually do it. Let's actually look at what it means to animate something. Animate a character or any sprite uh over time. Going to go back to here. I think the thing I think I have a slide about this just to illustrate this, but we sort of talked about this briefly, but essentially there's a set of tiles that we have. This is the only one we're using. But if we imagine that over time this number, this quad index into this table of quads that we're generating could change and then maybe loop around to some point with some interval of time. We can imagine just like using delta time to either toggle a couple of sprites back and forth or a couple of frames in the in this texture back and forth or we could, you know, do any number of things with any number of frames just over some time. And we can see if at the very end we've got a couple of frames in particular. Those last two are going to be sort of like the toggles we could envision for walking. If we're looking to the left, looking to the right. Maybe we flip the texture cuz this is all, you know, folks might notice this is all just looking in one direction to the right. But if we're moving to the left, well, we want to also reflect that literally and just visually. So, we need to flip the sprite to look towards the left. So, we'll do that as well. And then, you know, essentially, you can just think of it like a flip book. Like if you have a bunch of images uh of if we envision those same images just on top of each other in a flip book repeated and just you flip the pages quickly, it'll look like he's walking at varying speeds. So we can take a look at that. So let's go ahead and look at character 4 or character three. I'm going to go ahead close this and let's just show what this looks like. So everything's the same, although uh slightly harsh with that green color. So let's actually re redo it so it looks slightly better. That looks a little bit easier contrast wise. So now if I hit left and right, you'll notice he's static right now, but as soon as I hit left, he looks to the left. He's walking. His legs are moving. I let go of the arrow key, he goes back to this idle state. He's no longer moving. He no longer has an animation. If I move to the right, same thing. It's except now they're looking to the right instead of looking to the left. and then back to center. So, we've done all the things that I've just alluded to. We've animating and we're also adjusting the position. Let's look and see how we're doing this. You'll notice that I have an animation. lua in here now. And this is the backbone of how our animation is going to work and also how in the dro. It's the same class here, but essentially it's all the things I just alluded to. We're going to have a set of frames. You think of an animation as a set of frames that should play over time and then typically you'll just loop back through the beginning of them. So if you imagine the walking animation, it's literally just two frames. It's one, it's the 11 and 12 idea wise or approximately there. If you imagine those sort of flipping back and forth or it's 11 12 and then looping back to 11, it's essentially a sort of flipping of two things. If your animation was five frames, 11 12 13 14 15, you'd play them in order and then loop back to 11. 11 12 13 14 15 probably assuming that your animation loops sort of elegantly smoothly which is an ideal characteristic of an animation that is intended to loop. It should look smooth in the way that our Flappy Bird parallax background was smooth and the same backgrounds in this dro are smooth. The animation should have an interval and

### Segment 6 (25:00 - 30:00) [25:00]

then often it's the case that an interval is the same but you could in theory have intervals that are different and so you just need you would need to keep track of each interval per frame what that is and then you could set an animation to change sort of differently over time. All of our animations for this D will be the same interval and it's usually some very small duration in seconds or half or fractions of seconds. In this case the walking animation is probably like 0. 25 25 or 0. 5 seconds and then a timer to manage that and then whatever the current frame is. We're going to be get this back whenever we call our animation. We're need to render the animation at whatever its frame is. So the key detail which I alluded to was just delta time here. Again, delta time is often the solution to these sorts of problems. So if there are more than one frame in the animation, then we're going to just update our timer and then assuming that we've crossed over that interval. So whatever that might be, we'll pass that in terms of a definition here, which is what this def parameter is to the constructor. We will then set the timer back to the modulo of that interval, which will wrap it back to zero and start it with whatever the fractional overlap was. And then we'll just set it to be no greater than one or rather the uh higher of one. So it can't be below one or in this case we're going to modulo if we take our current whatever our current frame is plus one and then modulo the number of frames plus one onto that. The reason that we're adding plus one here is so that when we get to the current frame being the end frame in our animation we still want that frame to finish over that interval. And if we were to just set it to current frame modulo self frames, it would end up actually just immediately skipping the last frame because that would be the length of the number of frames. It would be whatever our length of frames is going to be that last frame. So we're essentially modulating by and uh and adding to one frame past the total number of frames such that it's just overlaps loops back to one at that point and then we still get that last frame rendered on its full interval. And so as a result, you know, frame the first frame will go its interval, then frame two will happen, and then it'll loop back to one and then two, or if it 1 2 3, it would do all of that no matter how many frames, it'll always run the interval, and then modulo wrap itself back around to zero for the timer. And then ensure that we always go back to one when we modulo cuz modulo goes to zero. We want to make sure that we actually modulo to one instead of zero. We've done that. We've looked at animations, or actually, well, rather, we need to look and see where this is rendered. uh got ahead of myself slightly. So here we're going to define two animations in main. la. So we have just essentially we can still think of our idle position as an animation just a one frame animation and it's not going to actually do anything. We saw there was an if statement to check for more than one frame. So effectively this is just essentially for consistency for our character. But we do have a moving animation which has 10 and 11 as the two frames into our sprite sheet. Recall when we looked at the sprite sheet they were kind of at the end the 10 11 indices into our sprite sheet. The interval here we set as 02. So every 2 seconds the frame will tick and then we'll go to the next frame and then loop back around. We're going to set to idle animation. And then what we're going to do also want to keep track of the direction. If we're moving left and right, we're going to also want to flip our graphic left and right which is an important detail. And then let's go ahead and go down to update where we can see that we are indeed updating our animation because it does take DT. It does need to know how much time has passed by. So it gets an update just like anything else much like timer did last week. And then here we if we are pressing left or right we want to actually change the direction. And if we are uh in this case moving, we want to set that to be the moving animation or rather no matter what the case is, we want to set the current animation to the moving animation. And then if we are not doing either of those, then we can just set the current animation to the idle animation and then update it. The result being here, if we come down to love. graphics. draw, draw after the map gets drawn. When we draw our character, we're going to instead of drawing quads at just index one, which we were doing previously, we're going to whatever our current animation is, we're going to call get current frame, which if we look back here, it was literally just an index into self. frames at current frame. So, just a glorified getter basically. And then we're going to uh essentially set the width and height here by an offset because it's important when we're shifting things in space rather when we're flipping things left or right in either on the X or the Y- axis. A flip will if you're basing your origin on the top left of your character, which is what we do by default, it will actually flip around that origin point and end up having weird behavior. We want him to actually rotate in place. And so to do that, we have to actually set the ro the origin point of our character right in the center of the sprite instead of the

### Segment 7 (30:00 - 35:00) [30:00]

top left corner, which doesn't affect our calculations for physics, but it does affect where things get drawn, which is what we do here. We actually this last two arguments to love. graphics. draw after sort of all these other arguments that we've been used to seeing. If we set the origin here to character width divided by two, character height divided by two, what we're essentially doing is we're shifting in from the top left corner that origin to be slightly well to be directly in the center of the sprite such that transl scale operations now happen based off of that origin point and not this top left corner. And the scale operation is how we apply a flip. If you scale by negative one on the x or the y ais, that's effectively what's going to flip the axis around the origin point and it's going to trigger that sort of flipping aesthetic. It's a scale technically speaking. Um, and that applies to the origin. It's always going to be relative to the origin. And so we also in order to account for this to we have to draw essentially shift the characters X and Y by that value because now all drawing operations are going to occur based on the center point of the sprite which is going to result in the sprite looking as if it's shifted up towards where its 0 point was previously at its top left. So we're essentially shifting the origin in to its center. And then we can offset that by drawing inwards by that amount. And then we can now flip its axis of rotation or we can flip along its x-axis depending on whether it's moving left or right or whether we have left or right set. And so ultimately that results in being able to move in place while being able to still have the same collision and things draw just as if they were uh as they are expected to. And so we have therefore all that we need in order to draw a sort of flippable translation or flippable animation. Now a character that can rotate or not rotate but can flip on any axis Y or X axis and look as if it's moving left or right and they animate as well. So now they actually look as if they are sort of more lively interactive character which is great steps. The last thing to illustrate just the basics of the character before we move on to other topics is the ability to jump which is a very core part of Mario. Mario's sort of signature thing is jumping up and hitting blocks. And so it's only fitting that that's the last thing that we have Mario do in this example. And there's, you know, again, another, we saw the already alluded to the sprite that will allow us to do that. And what's cool is we already sort of looked at that in Flappy Bird. So let's go ahead and open up character 4. I'm going to close these examples. Open character 4. And then we have the same thing as before. I can move the character left, right. Everything works exactly the same. Now, if I hit spacebar, you'll notice that if I'm staying still, the character is just kind of jumping up and down in place. If I move right and jump, you'll see the animation is slightly different, but they are indeed jumping with their fist raised up, which is part of that animation. And then left, sort of the same thing. So, I'm essentially setting that frame to be whatever that jump frame was, probably nine based on my memory of the sprite sheet. And then if I'm moving left, it'll keep that frame. And if I'm moving right, it'll change to that frame. And that's essentially all we need now to feel like we're using a sort of very analogous Mario character with all the same basic features of at least Mario from the original Super Mario Brothers version. We can take a look at that here. It's not a whole lot more that's been added. We've just added a new animation for the jump animation, which is apparently frame three. I thought it was frame 9. Looks like it's frame three. And then if we are if we come down here just a little bit down into the love. key pressed if the key is equal to space and the dy is zero meaning that we aren't already jumping cuz remember this is what we did in Flappy Bird. they have some gravity amount the character can jump up to set their negative gravity their negative dy to some value like -300 if we assume like a 980 or 900 gravity on the positive y ais and then gravity will bring them back down but that's essentially what we need to check for here is are we already negative or are we already statically set on the ground such that we're not either jumping or falling down cuz we don't want to just allow the character to just keep jumping and going up and up. We did that in Flappy Bird. You could just spam spacebar and just keep moving up the screen, which is part of the game. But in Mario, you only get to jump when you're touching the ground. So that's what we do here. Then we can see here we apply gravity, very similar to what we did with Flappy Bird. And then in here, we have just a hard sort of like check for are we at where the tiles begin on the map, which is about tile 7 minus the character's height, so that when its feet touch the top of that tile, it won't go any deeper than that. And then we'll just update whatever the current animation is and then keep moving left and right. So on and so forth. The same thing should still apply. We should still be able to move left and right and then still apply a transformation to whatever we're jumping at with current animation. Nothing changes. Even if we're jumping, it's

### Segment 8 (35:00 - 40:00) [35:00]

still going to be the same thing here. But now our animation can now just be jumping instead of move instead of being static or uh moving to the left and right depending on whether we've pressed spacebar. And we are indeed jumping. And that ultimately wraps up all of the character examples, which now we can begin to start to take a look at another key aspect of this DRO, which is the level generation. So to sort of bridge us to the DRO, then what we're going to want to take a look at, start taking a look at is procedural level generation, which is something that's pretty cool. I'm I've always been kind of a fan of it. It can be something that's, you know, overly done and sort of too relied on sometimes. But in the case of today's example, the benefit is that we can get some really nice looking levels without actually having to by hand create all of them, giving us sort of this infinite replayability, which we we've explored this idea in concept, at least in terms of Breakout, for example, where we have all of these different types of grids of bricks that can be generated that can be broken. And then in match three, the grids have to be randomized in a similar but simpler way. But in today's example, we can sort of start to think about levels similarly in that you essentially have a obviously a grid of what we represent as numbers so far that we've looked at. And these have some sort of semantic meaning in terms of how they should be placed together. For example, a ground should be at some baseline level probably. You know, so far we've been doing it about six tiles down, seven tiles down into the game space. And then you can start to think about, okay, well, that's just a flat ground that's infinite in sort of the way that it feels and pretty simple and not much of a level. But if you start to say, okay, instead of having, you know, all tiles throughout that whole space from seven down on the y-axis all the way to the end of the x axis, all of them always being filled, we can start to imagine, okay, well, what if we instead just decide to introduce gaps in there sometimes or we put pillars or we draw pyramids or we do any number of things. We start to add new props, new features instead of tiles. Maybe there's switches. Maybe there's blocks we can jump up and hit. Maybe there's sort of decorations like bushes and clouds and various things. Maybe there's coins. Any sort of thing you can think of and you start to place these around and you sort of have rules that get more and more elaborate. Now you've got this system in place that allows you to create these infinite levels. And the sky's is the limit ultimately for what's possible. As long as your logic is such that you never overly put the player into a position that's unwinable, which is difficult in its own way, uh you end up having a pretty robust system for certainly for testing and even maybe even for playing games that are fun enough at least to play for some people. So we'll be looking this week. We have a quite nice tile sheet that's creative commons. an artist named Kenny did the original version of this which then had a sort of uh 16 x6 version done by somebody else which is really neat and it has just a plethora of tiles here as we can see and these are all tile sets. We will only use a couple of these throughout today's lecture in particular the empty and the full just square version but you can see there's slopes and rounded versions and all sorts of fun things you can do with that. I encourage people to explore with the codebase. But in addition to that, we not only have tiles, but we also have toppers for them. So this is a way to sort of add these don't really functionally mean anything in terms of gameplay, but they do offer a way to differentiate your levels visually in a way that allows us to get the sense that okay, we're not just in some random plane. Maybe we're in like a candy themed place or like this icy area or this foresty place. And so this is just a simple change really. Ultimately, it's just drawing a texture on top of another texture, which we'll see. But it does offer a tremendous amount of visual variety that gives your game a lot of pop and makes it feel just fun to play. In addition, the sprite sheet includes a lot of these other tiles and whatnot that we can use. There's some hills and some blocks. We'll use these blocks for sure. We'll use a uh we'll use the um there's a flag here for actual DRO for folks to use for the problem set. And then there's some other various examples here. You can see that there's even a couple of creature sheets here. We'll be using this. This is where we got our character from. But there's also like an enemy here, a snail enemy, and a bunch of other things. The gems here for the powerups, keys, and locks for the actual uh problem set as well. Here's the tiles again, so you can see what it looks like just isolated. I've gone ahead and isolated most of these as well. So rather than having them be just in one big texture, which is what this is by default, you get instead we have this. We can sort of now if everything's symmetrical, recall, we can splice it up programmatically a lot more easily. We don't have to hard code offsets and do things that are too crazy to figure out where we are. Same thing for the toppers. These are now isolated. These are now uh programmatically slicable in the same way. And then with just these visual elements, we now have, as you can see, I try to take a representative

### Segment 9 (40:00 - 45:00) [40:00]

selection of levels. But just in and I'll do some live illustrations because I have a key uh bind set up to generate the level textures uh over and over again. But you can see we have not only are the visuals varied in terms of the we have the topper here, right? We got like this green texture on top of this darker texture. We've got this white texture here on top of this yellow. All of these kind of have a different version of that. We not only have that the backgrounds are randomly chosen. There's only three of those. But you can see we've got like gaps in the levels here. Got gaps here. We've got here we've got a little bush. grass looking thing. And then we've got these pillars as well that stick up. And every sort of thing if we imagine sort of thinking of our level as this set of tiles a that we can place in a line going left to right, top to bottom. You can sort of envision that, you know, maybe we want to say, okay, on this tile, I decide that I don't want any tiles this particular column. And then maybe on the next one, it's like, okay, this one I want to just set the ground at normal. We'll just define some sort of normal ground elevation. Okay, draw ground from the top to bottom. And then whatever the first tile is, put a topper on that tile. Mark it with a flag to say, hey, let's have that one be a topper tile because we don't want to draw it on these ones below here. It'll look like a glitch. We do want to draw it on whatever the topmost tile is. Notice, for example, looking at here, we can see this one has a topper. It's the surface. And this one here doesn't where these same elevations are. So, you can do a lot of these sort of calculations looking as if we have like this marker going across the screen placing tiles in sort of that direction. You can think of your game in multiple different ways, your level in multiple different ways. You could have multiple passes throughout your level to place things after the fact. Once you've laid your ground, for example, that's an excellent time to decide, okay, now I want coins here and like an arc or something, or I want to put like a block here that you can jump on to get something. And there's multiple ways you can conceive of level generation. We'll take a light look at a lot of these ideas. And we'll start with level zero. So, let's just get a basic version up. This is essentially the same thing that we did previously, except we're going to start to use our new graphics here. Let's go ahead and I'm going to close out character 4. We'll go to level zero. And then I'm just going to run this. See what it looks like. We do indeed see this is random. So whatever it picks is just going to be a random tile with a random topper. The background is just going to be random color. And then if I press R, you'll see that I'm shifting through all these different graphics. And so you can sort of see lots of really cool combinations here. If I do just all of these and all it's doing that looks like Sonic. All it's doing is essentially just taking the tiles that we're rendering. And so now we're instead of drawing this very primitive t brick texture with a transparency layer for the sky that it was doing before. It's now deciding to randomly take we're going to splice up all of our graphics from the tile set. toppers. And then we're just going to pick a random number between the number of tile sets and the number of toppers. Grab that block of tiles or toppers and then just index into it at whatever that ID is for the tile and then just put the same topper on top of it. So let's go ahead and enter our source code here. We'll see in we've got a bunch of constants here. These are just the number of tile sets wide and tall all of our toppers and tiles are. There were more toppers than there were tile sets. We won't spend too much time looking at necessarily all of the code for chopping it up. It's essentially just a calculating a set of offsets into our tile set. So in the case of our uh we're going to chop up all of our tile sets based on generate quads. But then we're going to pass that into here. We're going to then just essentially divide up that whole 1D array into a set of 2D or into a 2D array which is going to have each block of tiles within that particular tile set. If we look at, for example, the tiles here, you can see we have, you know, a block of tiles here that are all going to be one array. A block of tiles here that are going to be one array. Block of tiles here. And if we zoom in a little bit, if we look at, for example, this, we'll look at this one as an example. You can see if we count in 16 blocks of tiles, it's a little bit blurry, so you can get a little bit crisper, but you can see if we in fact factor if we figure each of these sort of blocks here as VS Code is rendering it as these four tiles together is one tile, one 16 pixel x 16 pixel tile. This would be tile one. two. This would be tile three, then four, and then five. And so we can see just looking at this that for example, tile three here is our solid tile that we're going to use for all generation. And then tile five is just our empty tile. It's just transparent. There's no pixels being rendered there. If folks wanted to, they

### Segment 10 (45:00 - 50:00) [45:00]

could choose to use some of these tiles here for sort of slopes if they wanted to implement slopes and offsets, vertical offsets for walking up slopes. Or if you wanted rounded corners, you could choose to use these for the bottoms of certain things. A lot of these other tiles are the same tile essentially as this solid tile, but put together in a way that makes it easy to visualize sort of what the tile set looks like in a representative example of what the terrain ought to look like. So it's really neat and we can essentially just index into this with some ID3 and five. Three being solid, five being sky essentially or ground and sky. And we can do the same thing for toppers, which I've called tile tops, which is laid out in the exact same way by design. And if we just zoom in a little bit, we'll see that they align such that. Whoops. If we go to where the purple begins here, we can see we have one, two, three. The topper would perfectly align with that tile. four and then five for transparency. So things line up perfectly between the tile sets. And then it's a matter of just deciding, okay, if I have a tile set and a topper set, we call them topper sets, I can just draw the tile. And then if I have a flag on that tile that says, okay, you should have a topper, just draw a topper as well on right afterwards on top of that tile. And now we get this sort of layered effect of this top of the tile having this uh element on top of it. So, if we look down here, I'm just going to go ahead and scroll down just a little bit here. We can see we have our we're going to choose a random tile set and topper set which we can index into. I'm going to come down to the draw function here, which is going to be where we have our 2D iteration over all of our tiles. And so, when we get our tile, we're going to do our usual draw, which has been previously what we did essentially, which is just draw whatever that index into the tile set is. cuz we were just using the two tile set before, but we're going to actually have whatever the tile set that we randomly choose. These are just going to be two random numbers. And then whatever the ID is at that tile, we're going to store the ID just on the tile. Now, instead of it being a number, we'll store the tile ID because we want to flag whether it has a topper, which is what this is does right here. So topper is just a flag. It's just a boolean. And this basically going to be okay, is this tile like essentially touching? Is this the top surface of the ground essentially when we do our when we create our level? And if it's true, then we should draw the topper. Else that means it's going to be below somewhere under the ground under that first tile. That gets done in our when we actually generate the level here at the might have put it into a function. Let's go ahead and scroll down just a little bit. generate level. Well, now instead of just being a tile ID that goes into our table, it's going to actually set topper equal to whether Y is equal to 7, meaning that that's the point at which we begin to draw solid tiles. And so we do this turnary expression in Lua again. So if Y is seven, then it'll be set to true. Else it'll be false. And then through that we can then check that topper flag in the future and determine whether we should draw a topper at the time we draw the regular tile. And that's essentially how we can draw now varying tile sets and topper sets to give our levels a cool visual variety. And you could semantically flag these. You could set constants equal to okay this particular topper is icy themed or it's fire themed or something. And then you could limit sort of what the actual topper set is that you can choose for your level and have there be meaning there. We're just running the entire texture in this case. And we're just deciding that it doesn't matter. None of that matters at all. It's just completely random. And so you never know what you're going to end up getting. So that's level zero. We got flat levels. So we talked about this a little bit in terms of how we can start to vary up the terrain. But if we think about the first step being well instead of just completely flat levels, what if we had some of the terrain actually jutting up from the ground? In this example, we won't do anything more complicated than just a few tiles up. But if you sort of imagine, okay, right now we just essentially do a 2D loop, which just at the time that y is equal to 7 in our columns, we start to draw tiles. If we instead just decide per column iterating on the x-axis through our table, we'd say, okay, there's a one in whatever chance that instead of starting the terrain at seven, we'll start it at four. And then so that's going to result in tile four getting to be solid and then all of the tiles below it being ground and then tile four there getting set to top or equals true instead of seven. And then now that's going to stick up. And then we just if we make it a certain percentage chance then some of them will have pillars and some of them won't. Let's take a look at what that looks like here. I'm going to go ahead

### Segment 11 (50:00 - 55:00) [50:00]

and close these and then run. Sorry, level one, not level zero. And you can see here we do indeed have our character on the screen. We can move around a little bit. We can clip through the walls right now. So we're not actually doing any collision at all. But we can see that we're sort of essentially just there's a random chance between one and some relatively low number that instead of starting at seven, the ground just decides to start at four. And so we have just some visual variety. It's not exciting. It's not appe uh appealing. You occasionally get these sort of cool two block thick uh pillars. And you could make some code, some logic in your code actually decide, okay, I'm going to draw this column and the next column. for example, at four so that we guarantee a three block or whatever block with pillar. But right now, we're just essentially relying on pure randomness. And as a result, excuse me, sometimes you will get these sort of groupings of these pillars. We'll go into main uh of level one here, and then we're going to go down to our way down to our generation function here, which is where this is all going to take place. This all gets moved into levelmaker. la Lua in our actual Mario distribution. But we can see here that what we're going to do is we just create our entire level with sky tiles to start with. We just completely populate the whole thing with sky. Then we're going to start from the left going all the way to the right of the screen. Vice versa for camera. And what we're going to do is essentially create a 1 in5 chance to spawn a pillar. And then if we do have that then we're going to from four to six go ahead and create that as ground. And then we're going to set topper equal to pillar is equal to four. And then if uh rather if pillar is equal to four we'll set topper to true on that tile. And then always default or fall back to the ground. So then no matter whether we draw a pillar or not, we'll always still be drawing the ground below it. And then in the event that we did spawn a pillar, uh we don't want to also set the topper on seven. But if we didn't spawn a pillar, we do want to set it to true if we are at seven. And as a result, we now have pillars throughout our level space, which is great. Things are varying up a little bit. Things are different. But um you know, in Mario, one of the big perils is falling into chasms. And I think that that's also a sim very similar idea that we could explore. So let's go ahead and take a look at what that means in level two. Going to go and open up level two. Close this. Again, this is going to be done in our generate function at the very bottom of the program. Everything is sort of condensed here. And we're going to do the same thing. We're going to populate everything with sky first. And then we're going to essentially a chasm is just essentially not drawing any tiles at all in that particular part of the level. And so really, it's as simple as just choosing not to do the whole bit of logic that we just added in there. We're just going to do this continue sort of go to way that Lua does continue. We're just going to say, okay, we're just going to go to continue. If math. random 7 is equal to one and continue is this label here at the very bottom of this block below where we actually add tiles. And so we if we just skip the tiles, they'll remain prepopulated as sky. And so now we just get chasms for free by just simply not generating anything. And as a result, if we run this, we do indeed have some chasms here. Now, it looks like there's a bug with the toppers on this particular example, which I missed. But as you can see, we do have chasms here that are being skipped over by just the pen in our code is essentially saying, okay, one in seven chance, then we're going to show a we're just going to not generate those tiles. and then you know do as we were doing before. But it's a little ugly looking I would argue and you what you could do besides the fact that the texture is glitched it's ugly in the sense that the actual width of the uh chasms is so narrow. Typically in a game like this you might decide okay if I want a chasm I probably want it to be at least 2 or 3 pixels wide. So you could elect to decide to draw to skip over from if you're at X is equal to 5 for example as your chasm, you can just skip right to X is equal to 8 deliberately and then shift over a whole three or four tiles and then get a larger chasm as part of your design and then just never have single chasm blocks. It's up to you. You could certainly do that in this. I would argue that that's probably good. Although it's also arguable whether you might like the fact that there's multiple gaps here. You know, it's sort of intimidating sometimes to have a bunch of gaps in your level that you can just you can sort of psychologically jump across them, but you might, you know, lose your nerve a little bit, so

### Segment 12 (55:00 - 60:00) [55:00]

to speak, uh, in the process. And so that's ultimately going to be a matter of taste for whether or not you want to how you want to design your levels. So that's level two. So, chasm levels and as folks noticed, you know, we had the ability to walk through our level and the levels generated just fine. But collision is a little bit weird. It's a little bit different. We're not quite colliding with the level at all except for the ground. And that's essentially a hack in that we're essentially hard coding where we stop on the ground. We're actually performing any collision detection. So, here we'll take a second to start explore how Mario handles its collision detection. So to begin exploring collision detection, I think it's important to begin to talk about how it's different with a tile map versus how it might have been done previously. At least it can be different for a tile map. You could certainly use AAB for a tile map and it works fine enough, but there are, I think, benefits to being able to simplify the collision for something like this given that there are so many tiles in the space of the game and there's complexity involved in actually determining if you were to try to shrink down the tiles that are relevant uh in terms of figuring out what Mario ought to be testing for a collision with. So, it's easy actually if we know that we're in a tile map and all the tiles are static in their placement, we can essentially just determine at a given pixel where a what particular tile is there based on Mario's position or a Mario avatar and determine, oh, okay, that's a ground tile. Okay, then that means that is a collidable tile. I should shift Mario to be, you know, to the right of that or left of that tile or whatever relation the character has to that tile. and it's moving, we can bump it to the outside edge of that tile versus having to do AAB for maybe every tile in the whole screen, let alone the entire map, which is just, you know, especially when you have other entities that maybe are trying to collide with things like the snails, for example, that we'll be adding. There's benefit to being able to just isolate your checks to wherever you're moving, whether you're walking, whatever you're doing, jumping to just the immediately surrounded tiles based on the character's position. we can get a lot of shortcuts and a lot of processing advantage by doing that. So we won't be using aabb. We'll be using a function called point to tile which we'll add to the tile map which allows us to just say okay at this x ycoordinate we're going to divide by the map's sort of tile size and then whatever tile is there at that index in our tile map at yx we can determine based on it's whether it's collidable or not that we have actually collided with a tile a physical tile a ground tile a whatever tile might be that we might use. There's benefits to this for sure. And when we test for collision in this new model, so we're not no longer now taking rectangles and comparing our rectangle with those. We are still doing that for what are called game objects, which we'll see in a second. Things like the bricks that we can jump up and hit or things like the gems that come out of those if you were to add coins. If you were to think about, you know, also other entities, for example, we do still do AAB throughout the code, but in different circumstances. But for our tile map, which has hundreds, maybe thousands of tiles, we're only going to essentially use a sort of conversion between our characters point, our characters, our characters coordinates to whatever the tiles are in the world. And so, for example, if we're colliding with something that's above our head, which is typically only something we're going to do when we're jumping, we can say, okay, at the characters essentially left top left coordinate and top right coordinate, we'll check whatever the tiles exist that are there. And so, that'll accommodate any particular we don't want to just check the top left because if there's a tile here and we're only checking for top left, well, we' clip through this right tile essentially. So, we want to check both edges of the of our character to ensure because we can be between two tiles. We want to essentially ensure that we don't collide with whatever both edges are. Same thing for below us if we're falling down or if we're walking or doing anything. We want to check the bottom left and bottom right of our character's avatar so that we don't just isolate collision checks to this edge and end up actually clipping through, for example, this tile. We want to check both sides of the character's bottom. And then here we have on the left side, if we're moving to the left, we want to essentially do the exact same thing. Characters top left, characters bottom left, and then the characters moving to the right, characters top right, and then the bottom right. And these will get applied in the walking state and then the falling jumping state because we can move left and right when you're jumping and falling and walking. And so with those, assuming that you detect or you calculate, okay, whatever this XY is, and then divide by the maps tile size, you'll arrive at the X and Y indices

### Segment 13 (60:00 - 65:00) [1:00:00]

into your table in order to actually calculate, actually figure out what the tile is that's there. And then you can then say, oh, is that a ground tile? Is it a sky tile? Sky tiles aren't collidable. Ground tiles are collidable. And then you're only ever checking pretty much depending on your direction two to four tiles at any one time instead of maybe hundreds or thousands of tiles. It's a very sort of like o of one type of operation. Um and then we have also a discussion about entities that we'll have to talk about. We can briefly look at the collision before we maybe talk about the entities because that's a whole other subject. Entities being what will be for example the player and the snail and various other things. Let's take a look just at what the collision how the collision is implemented in the codebase and we'll be overall probably skimming over some details and encouraging folks to read some of this because the codebase is quite large but we'll be looking at some of the important things and I'll point to and explain some of the important aspects here. The player is essentially in Mario the gist of the has a lot of the underlying behavior and functions needed for our actual character. So you'll see that he inherits from entity, which is sort of like a base class that has an X and a Y and just a collision function. It's kind of just the overall representation for something that should move around and interact in your game. And some game engines and frameworks might use entity to be just about everything or game object any anything. In this particular case, we'll think of it as sort of like moving things that can be agents. And you'll see that there are a set of collision detection functions. For example, we have check left collisions. We have check right object collisions. So, there are also game objects in our world. Again, I alluded to this being, for example, the blocks that you can jump up and hit or the gems that you can collect if you were to hit one of those and they spawned a gem. We want the game objects and tiles to be different because the game objects have behavior that is particular to them that is special sort of and you could also therefore put them anywhere you want in your game world and they're not beholden to this static coordinate system that you have in your space and also they can be drawn on top of a tile. For example, you might have a brick in your world that's a game object that you might want to draw on top of sky. And that's not really possible unless you have like multiple tile maps, which is introduces complexity. But you can't really have that unless your game objects are kind of separate from your tiles. Like your tiles, if you're using this sort of model we're using today, and with the advantages that it has with collision and whatnot, you're kind of stuck using a static tilebased system. And then you have to layer on top of that to achieve some of the effects that you want. But essentially we have to check object collision separately from tiles because the objects using AABB collision are sort of like separate entities. And you see this collides function here. We're sort of using the same AABB that we've used in the past. And all of these things have those same functions implemented for them. But this left and right collision detection function is important because it has this self. map. every the player maintains a reference to the game's map in our codebase and then this point to tile function. So what we're doing is we're essentially saying, okay, I'm going to take whatever our X and Y is plus one. And we're doing that just to sort of shrink our hitbox just a little bit to give us a little bit of uh wiggle room flexibility with how we move around and don't overly I've noticed that if you have it be too firmly set to your boundaries in a game like this collision is a little bit it feels too constrained. It feels too you hit things when you don't feel like you should. It's better to just kind of it feels take a couple pixels off in the form of our X Y and then our width and height. And so what we can do is essentially say if we're checking for left collisions, we'll say, okay, what's the tile to the top left and the bottom left of me, which we saw in our di our diagram before, we do that by taking our X and our Y and then our bottom left for the top left. And the bottom left is essentially our X and then our Y plus height. And then we can use this operation called point to tile which if I open up the tile is just essentially there's a bounds check here just to ensure that our we actually can be within our map. We don't want to index outside of our array of tiles. But what we essentially do is we just divide y by tile size, add one because everything is one indexed in Lua. Same thing with our X and then we therefore can just directly translate pixel space into tile space into our 2D array or 2D array. Nothing is really too fancy here. Just a width and height in the tiles array. Much like we saw in our other examples, it gets populated. There's other places where it's going to actually get generated in our level maker, but as you can see, none of what we see here is all that different than what we've done before. But now we can essentially just check any particular pixel in our world, convert that to whatever the tile is

### Segment 14 (65:00 - 70:00) [1:05:00]

there with adding one to account for Lewis tile based indices. And then now, okay, is the tile there solid? Is it not? So, if we're moving left, let's check whatever the tiles are to our left. Or we're moving to the right, let's check whatever they are to the right. If we're moving up, check whatever is above us. If we're going down, check whatever is below us. That's effectively the gist behind uh getting collision detection to work. We want to, you know, reset our X value if we end up actually colliding with something, which is what we do here. And then we're going to do a little trick here to avoid checking for object collisions. uh we're going to actually shift our Y position up so that if we're walking and we're moving left and right. If we shift our Y position up by one pixel just temporarily check for collisions and then move our Y position back to normal, we can still walk because if we're walking, we're going to be right above that object and not colliding with it. But this allows us to just check for collisions in the event that we are, for example, jumping or falling and make sure that we don't actually clip into something. And then if we actually did end up colliding with something not in that view of that we're walking on top of a block, then we can actually just essentially perform a reset on whatever our walk speed by delta time was moving left and right, which is how we do all of our left right movement here. And so that's check left collisions, check right collisions. This will end up getting called. So, if I open up the play state, we have a play state and a start state as normal. Folks might notice we have an entity states folder, which we'll get to in a second. And this is important. This is where we're going to start to tie in some of the earlier ideas we talked about with animations and the like. But here we are preserving. We have a level in our play state, which is levelmaker. generate 100 to 10. That's the width and the height of the level. Folks in part of the assignment will be encouraged to increase the level, increase the size of the level. Every time the level gets incre every time that they make it to the end of the level, which they have to implement the flag for, they're going to want to increase the size of the level maker generated by the level maker rather. But skipping all these states and whatnot, we can go ahead. We see that there's a an update camera function here which just has a camera X and a background X which manipulates the again this is the overall sort of love. graphics. transate offset that we saw previously. And then same thing for the background X as well. This is just essentially taking whatever the x is divided by 3 mod 256 so that it it's going to end up slowly sort of moving uh to the right as the duration of the uh as they keep moving the camera x throughout the level and it's going to lead to that slow sort of like parallax effect that we saw previously. Um we spawn all the enemies here. the actual collision takes place in all of the actual individual player states which then get called. So if we actually go to player walking state for example. So this is a little bit of a look forward in terms of where things are actually uh we're going to talk about entities and entity states in just a moment but this is just to kind of illustrate where the collision code is actually taking place. There's going to be some amount of the left and right collisions going to be checked and certain things are going to happen depending on which state we're in. But ultimately these defer to the checked left collisions and check right collisions if we're moving left and right because these check left and check right collisions will occur if we're walking, if we're jumping, if we're falling. they kind of are like they transcend any individual entity state or yeah any individual entity state. And so here is where they end up getting called where ultimately this walking state gets updated within the play state and then as a result we can sort of like isolate our entity behavior a little bit but have general functions that allow us to do calculations and collisions that are going to occur across multiple states. And so that's sort of what's happening here. Now, I'm sort of getting ahead of myself a little bit and so I think now it's a good time to maybe look at, for example, what an entity state is and what we've been sort of alluding to. And what that essentially is, we have to sort of talk about what entities are to begin with. And so, an entity is just something that has an XY. It does behavior. It interacts in your world. People have different definitions of what this is. And the benefit of having entities is that they can be sort of like generalizable in a sense that you can isolate certain types of underlying shared behavior and ideas. For example, every entity has an x and a y. Every entity has a width and a height. Every entity has some sort of uh collision detection capability therefore with another entity assuming that everything is axis aligned. And these depending on

### Segment 15 (70:00 - 75:00) [1:10:00]

your game, this might be uh even more behavior than this. But then you can instantiate things that inherit from those behaviors sort of just classical object-oriented inheritance in our model and then have these sort of like more specific types of entities. For example, a player is an entity that we just happen to be able to steer. A snail is an entity that is going to be able to target the player with its own states. And what's cool is that we can also think we can build on this idea and think of entities as a container for sort of these behaviors that now we can visualize in the same way that we visualized game states as being these containers for what it means to be in a particular part of the game. We can envision the entities as being their own state machines that are engaged in a particular behavior which we actually had seen in previous lectures. We saw the overall diagram of like Mario jumping and doing all these different things. And that's essentially what an entity can do is it it'll have a maybe an idle state, for example, in the case of our character, which then if you were to move left or right, this is going to transition you to walking. You're now walking and you're now in that state. But if you were to press spacebar, you could go from that state to jumping. idle to jumping as well. So now you can sort of go to that state through two different mechanisms. And then by just un letting go of input for example while walking you go back to idle. If you get hit maybe you go into a death animation or something. And this is in the same way that game state can be taken from these sorts of like convoluted if statements and whatnot as this sort of branching behavior. You can just encapsulate these in a state machine and then on your entity on your player on whatever entity just store a state machine that holds all of your states and then all of these states can then be updated one at a time and then rendered and you know interacted with in the same way that we evolved our implementation of the game state machine. We'll do the same thing with entities. And then uh one other piece that we should look at is game objects. Real quickly let's look at what the entities look like before we do that. So again, the entity if we transition here is just essentially a wrapper class. So I'm going to go ahead close all of these. Game object is also a rather a wrapper class. But we have an entity folder for states. But if I were to go to just the base folder, we have just this entity which takes in a defaf. You'll see it has a position and a velocity. Most any entity we've ever interacted with in our games all have an X and a Y and a velocity and a width and a height. They'll all have a texture. state machine. They're going to have a set of states, a direction, a reference to the map so that they can actually see other aspects of the map and the tiles and whatnot they want to interact with. and then um the level itself which is going to have the uh we're going to have a game level object which contains all of the game objects, entities and the tile map. So through that we can then see a few functions here. We've got a change state function which is very similar to that state machine that we had previously. um the same type of function. Actually, we're using literally the same state machine class for this except that now we are instead of having our game state objects that are classes that we're implementing with update, render, enter, exit, and all those things. We'll just do that for every entity we want to model. If we want to model a snail, a snail is going to have an update, an idle state, a chase state, the player's going to have a jump state, so on and so forth. you get to have ultimately quite a lot of different states uh depending on what your game how many creatures and whatnot you have modeled in your world. And so there are ways you might seek to try to reduce the amount of code explicit code for that and use what might be called like datadriven design to do this which we might explore a little bit as we get to the Pokemon lecture for example and even Zelda's lecture. But for right now we can model these in code with classes. the snail, the player, whatever creature you could imagine adding. And then as a result, if we think of these in terms of states, much like we saw previously with the game states, we can just create a new folder. In this case, just entity. So, we've separated our states sort of semantically between game states and entity states. within the entity states. We could have probably put this in a a player folder, but I just put that in the base folder because this is sort of like probably the more important for the sake of lecture set of states. But we also have a snail folder here to separate the snail states from the player states. But you can see, you know, the player walking state is going to encapsulate all the behavior of what it means to be walking. So if you're uh holding left and right, you're going to want to check your left and right collisions while you're walking. And um ultimately it's a it's pretty simple if you're the one key thing is that like if you're not pressing left and right but you're in walking again we need a way to move between states. So we're going to want to change our state here to idle instead

### Segment 16 (75:00 - 80:00) [1:15:00]

of to instead of keeping us in um the walking state. Um and then also if we press jump we want to go into the jump state because you can jump while you're walking. You don't you're not just limited to jumping while you're idle. If we go into the idle state, you'll see that if we press left or right, we'll go into walking and then if we press space, we'll jump. So, kind of the same idea if we're in the falling state. So, falling just means that gravity is positive and our y and we're not like on the ground. So, if we end up going here, we'll see if our if we're in the falling state um and then we actually collided with the ground is what this particular thing is checking for. We're going to want to immediately go into walking else go to idle. So, essentially if we're falling and we hit the ground, we're going to when again we check for point to tile below us, set our Y position up just a little bit. So, we're we're static set our dy to zero. And then we can just immediately change to whether we're walking or idle depending on whether we're already holding left or right. If you don't do it, if you're not doing this check here and you jump and you're holding left or right, there'll be like a frame where your character is in the idle state and it's just it feels weird. It's clunky. So, you want to just go right into left or right or idle. And then in this particular instance too, like this is where we actually have the death sort of condition for the game. Like if you in our particular game, there's really only one way well there's two ways to get killed and that's to fall below the edge of the ground or to let a snail touch you while you're on the ground. And so this is one of those instances which is that the Gstate machine will change to start which again this is our for our global state machine. This is our game state machine. We're going to check to see whether our y is greater than virtual height, meaning that we're below the map. We've sort of arranged the map such that it's right. Uh we're always rendering it above where that virtual height limit is. So if we just essentially go below the map falling into a chasm, the game will go back to the start state, which is at the very beginning uh of the game that we saw. And then here we also have some consumable code. So whenever we're falling or we're walking or we're jumping, we can collide with consumables in the world which are game objects. We've looked at game objects. Why don't we transition to that and just have a little brief talk about what game objects are. So game objects are you could in theory put these together with entities in a way just mark entities that are non a they're not agents. They're not moving around. and they're not doing anything that's sort of representative of some sort of sentient thing. But in the case of our game, we're going to say that game objects are this class of item or object that is interactive in the sense that you can collide with it or you can hit it and it will maybe do something for you, but it's not an entity. So, it doesn't have states. It doesn't do all of those things. And so, game objects are perfect for things that are relatively static, but should have some behavior that triggers when you interact with them or touch them or do whatever. And in this case, the gems here are game objects, as are the blocks when you actually jump up and hit those because those have a behavior. That behavior being that when you hit the block, a gem has a chance to spawn up and come up and then be claimable, which is itself a game object. So the block can spawn a game object itself. It's a is an ob game object that can spawn a game object. And so we've differentiated between these and you'll see these commonly depending on which environment you're in, which engine you're in. Some engines will use game object to be literally anything including an entity. Some will separate them. Some will just have entities and they will use what's called an entity component system or ECS where literally you just have everything is an entity and then it contains its behavior not through inheriting from some base class and then getting more specific but rather just having some sort of component within it that determines its behavior. And then in order for all entities to interact, they just iterate through all of their components and then just their behavior is sort of emergent as a combination of all of their interacting components. Those are called entity component system. Unity the engine is an entity component system. Um as an illustrative example. So that brings us I think also to the topic of powerups which is essentially just a game object that you could think of as just affecting some sort of state on the player. So in this particular example there we use a gem but you could decide to implement a power up for example that the gem in our example just improves your score but you could decide to increase the size of your character or to turn your character invincible which will be part of the problem set. So this will be relevant for the problem set. Have a star or have some sort of object that the player can touch. Maybe it has a chance to spawn from the brick instead of the gem. And then particle effect should happen. maybe a rainbow effect on your character and then right now when you walk into a snail you will just die if you're not jumping if you're

### Segment 17 (80:00 - 85:00) [1:20:00]

not falling rather but you should ideally if you were invincible be able to just touch the snail at that point which is very similar to what Mario gets to do in the game when he gets a superstar and then uh defeat the snails that way. So let's take a look now at how game objects are implemented in our code. If we go ahead and transition over here, this is the perfect place to show it. So in our world, we have a game level as sort of this container for because we want to have game objects and a tile map and entities all together as sort of this trifecta of things that interact together. We're going to put those into what's called a game level, which is this container for all three. So, if I open up game level here, you'll see it literally just has entities, objects, and then a tile map. And then what this does is we can just say, okay, we can just iterate through all the objects if we need to or the entities to see which things collided with each other. The tile map, again, we're doing point to tile operations for collision, so we don't have to worry so much about that. But all the objects can therefore when we do collision code for depending on which state we're in with the player, we can iterate over each object and determine okay are we colliding with it? If we are, is it consumable? Meaning did we give it this flag called consumable? And then a function maybe that can trigger an anonymous function as we've seen that will do some behavior that affects the player or the map or so on and so forth. If we go to game object, we just take a look at what that actually looks like. A game object is essentially a shell in the same way that an entity is, except a game object expects a set of things that will trigger uh depending on how it's interacted with in our world. And we're sort of arbitrarily choosing how to differentiate and which things it can have and which flags it can have. But this is similar more or less to what you might expect to occur in uh in an engine where you have an on collide or on consume call back or function that will trigger when your entity or when some entity that is flagged to be capable of consuming those objects indeed does consume those objects. For example, the snail isn't set up such that it can collide with or consume gems or power-ups or whatnot, but you can envision a world where maybe you do have enemies that are vying for resources or vying for power-ups. And then maybe they can also collide or with interact with or consume these items. And so we have that ability should we choose to do that through the functions defined on each entity state. But as you can see, the game object is a very simple function. It just has an ability to render itself effectively and collide with another target which is assumed to have an axis align bounding box. And it doesn't even have anything set up and update because we don't have any objects currently that should update or any logic for that. But you could very easily extend this to some kind of an updatable thing. You could have maybe game objects that animate or game objects that move around and do various things. Technically, a coin, for example, is going to have some sort of a it typically in a game, it's going to have some sort of like oscillating Y position, maybe some sort of sinosoidal uh movement in that way. And you could put that in update if you wanted to something like that. Maybe that's based on a flag that it has set in the game object. What we're mostly concerned about with is its collidable, consumable, on collide, and on consume flags, which if we go into the level maker, this is where all of that is going to end up being created. So, recall that we showed a very simple version of a level, how to create a level previously in our example. So, level zero, level one, where we just had chasms and whatnot. But when we're doing a full proper level here, we're going to want to not only maybe add some props, which will be game objects that are just not collidable, right? Which will just render them wherever we place them, but we're going to want, for example, gems that are consumable and collidable for our score. We're going to want the blocks, so when we jump up and hit them, they might spawn a game object or that is a gem that then is uh consumable. And so all of those things need to be determined sort of in advance. And we do that through a iteration of the level maker class which is sort of in spirit to similar to what we did in breakout. We had a sort of like level creator there as well. You'll notice that in some ways it's going to look very similar to the level maker that we were using in our previous examples and just the test code or sort of the leadup code, but we're actually now doing quite a bit more. If we scroll down here, we're doing all the usual chance to be empty, chance to lay out space, chance to be pillars and whatnot. But when we generate pillars, we can, for example, generate a bush on a pillar, which will just be, you know, we even saw an example of that in the screenshots where we have this objects list. This is going to be returned. We're going to return a game level from this level maker. And recall it has the objects, the entities, the

### Segment 18 (85:00 - 90:00) [1:25:00]

tile map. So objects are going to be separate from tiles. So we have a chance to generate one and eight on top of a pillar. Just if we did generate a pillar, the texture, the game object expects a texture. So the game object will know to index into whatever the right texture is. It'll know which quad. Therefore, we should by quad will be it'll be the frame key here. And it'll just be a random in this case just a random bush in this case from the list of possible bushes that are there. The spreadsheet has a ton of different varieties and colors and things. So wherever possible we've added some randomization but essentially it will just be uh offset based on the top of wherever we are in our list of remember the pill it generates that index four so we want to shift it minus one so it's on top of that fourth uh that fourth tile but then they have it just has a width and a height it's going to be able to draw itself it's not collidable it's collidable's false so our character will just like when it queries all the game objects that it can collide with it will just bypass checking collision on it. So, it'll just go in front of it. And then this is just an example of like a static game object that just essentially is a glorified sprite renderer. But if we come down, for example, past this other bush example to the jump block example, which is the more complicated game object in the DRO, you'll see it in a lot of ways is very similar to everything else. It's just it has a texture, an X and a Y, a width and a height, a frame. It just can be a random jump block. We saw in our example, there were lots of different objects, lots of different game, lots of different jump block tiles for it to choose. But notice that we have collidable is true. So, it will perform collision detection when we check, you know, check object collisions in our player class as the function. Um, it's going to have a hit flag that's false. So, if it's been hit already, we don't want it to generate a gem or do its chance to generate a gem, for example. And you might render this with a different texture in theory. We didn't go through the paint to do that in this, but you could if, for example, in Mario, the actual game, if you hit a block that's got a coin in it, typically what happens is it starts off yellow and then it goes into some sort of like faded color after you've hit it to show that you've hit it. Um, so solid is true, meaning that it actually has collision detection and pushes us outside of it. You can be collidable and technically not be solid and still like the collision will trigger, but you won't actually get pushed outside of it. on collide is the actual function that's going to trigger when we hit it. And so what this means is that essentially if we imagine that on collide gets called at the moment in this case it's when we jump and we hit it. So on collide will only trigger in our code. We're going to specifically trigger it when we actually jump. If the object has not been hit, we're going to have a random chance to then spawn. Notice another game object we're actually triggering. And this is all also by the way in an in a function an anonymous function. It's getting assigned the label on collide. So I guess it's not completely anonymous, but it's effectively is anonymous in that we can we could set this to something else later if we wanted to and swap this out. But on collide is essentially this function that's going to trigger later. We'll trigger on collide. It's going to one in 20 or 20% chance generate a new game object. And now it's the gem. So it's going to be the gem. It's going to end up setting itself up to above the actual block. um which we do here. We actually take the gem itself. We're using timer tween which we saw last week over 0. 1 seconds. We'll set its y to be equal to the block height minus 2 * tile size which will end up shifting it up above the actual block height. And then we're going to play a power up sound. And so this is going to this is a sort of way to tie in one of the things we saw last week which is this idea of smooth movement. So when we actually hit it, we're going to actually spawn the block with a sort of like interpolated Y movement so that it's like the smooth movement versus just like instantly, oh, there's the gem. Um, notice it's got a consumable is true. It's got an on consume function call back here, which assumes that it takes a player and an object. We'll play a sound here when we do that to show that we've picked it up. And then we're going to increase our score by 100. And then in our actual check object collision function is where these sorts of things get handled in our player. If we go back to player here, if we go to check object collisions, this gets called across various different states across our jump state, our fall state, our walking state. Because all of those different states, you can consume a gem or whatnot in all of them. You can be jumping, you can be falling, you can be moving. just not idle is typically where you would not be consuming unless you had movable consumables. Then you would want to add that to your idle state because it could move into your hitbox in theory. But as you can see, every time we call this check object collisions function, we're going to do a check for whether we've collided with it, which is just the AAB collision detection. If it's solid, we're going to

### Segment 19 (90:00 - 95:00) [1:30:00]

insert it as being like a collided object that we've collided with. And then if it's consumable, what we're going to instead do is we're going to actually call that on consume function. Object. consume. We're going to pass our self as the first argument, then object itself. And then we're going to remove from the actual objects inside of the um inside of level. objects. We maintain a reference to self. le. objects as the player. So the player can do these sorts of operations. We're going to then remove it from the table so that it immediately gets taken out of the world. so that we no longer see that anymore. When it does the loop to render over all of our game objects, it'll no longer be there. It'll be completely gone. And again, this occurs throughout all of the various player states that we can and potentially uh consume a game object or collide with a game object within. I think the last thing that I'd like to look at uh in order to illustrate sort of what some of the stuff we've talked about is the snail states which we haven't looked at. The snail sort of being this entity that exists sort of that gets spawned at the level maker creation time again based on a random chance just like the game objects are spawned. It's going to have an X and a Y. It's going to be placed on a particular tile. But these have their own states. So notice that it has an idle state. So in the idle state, what's kind of cool is that it has its own animation. It's just a single frame of it being in the shell, which is kind of just a cute thing. And then its behavior is such that essentially it has a wait timer or wait period where it will decide, okay, after that wait period, I'm going to change my state to be either moving or chasing. And chasing is just essentially if the difference between the player's X and the snail's X is five tiles or less, it will set to be chasing the player, which just means wherever the player's X is, go towards that X, which we can see here, snail chasing state. These states, by the way, are instantiated and they take references to in the case of the snail, the snail needs to have a reference to the player always. So when these states get defined initially, they all get the player as a reference to them. So this is important if you have any operation that you need to take place within your world between your entities or the player. Often the most expeditious thing to do is to just pass in a reference to the player or the map assuming that you don't have just this gargantuan list of things and to try to have these sort of highle relationships between things. you in an ideal sense you want to limit as you know don't do too much of this between things that don't need to talk to each other but sometimes the cleanest easiest way to actually get things to talk to each other is to have them passed as references and held and shared between them so here we have a chasing state it just always will be looking to see basically is the difference between our X and the players X five tiles and if so like keep moving in that direction otherwise there's a chance for it to actually or keep essentially moving left or right based on where the difference is, whether it's left or right, or change it to be just moving, meaning that the moving state essentially just chooses a random destination. So, if we go to here to the moving state, we'll see that the it has a moving timer, which is just going to be, you know, here it's just flipping a coin left or right to this initial its initial moving direction, but it'll always be doing that essentially every time we end up deciding that we should choose a new direction. It'll be based on just the same kind of timer as the wait timer was where we just decide okay after x period of time I've been moving to the right let's move to the left instead or let's keep moving to the right or you know whatever the coin flip decides and then there's a one in4 chance for it instead to just go idle for a little bit just to mix up behavior and this is essentially how you can get some interesting sort of like lifelike you know sort of like fake lifelike behavior or some interesting interaction in your game without going too crazy. That's the gist behind overall most of the objectives that people will need for the assignment. Um, if we transition here, essentially assignment 4 is that we should first off, I don't think we had a chance to even see that it happened in our testing, but right now there isn't really a cap on the player falling. Um, or rather there isn't really a an assurance that there's going to be solid ground beneath the player when they fall. So, you're going to want to do that just as an easy initial thing to also get people used to the level maker code. The key and lock are in the sprite sheet, and these are cool. They're different colors. Folks will have to choose between one color of the key and the same color of a lock, which will open up the end goal. So, this is just to get people used to using game objects. So the key will basically enable the player to interact with the lock such that it'll unlock the goal existing at the end of the game level. Meaning that the flag should then spawn that or folks can choose if they want to spawn the flag just in a state that is empty or not functional. Either way, the lock has to open the flag has to make it accessible.

### Segment 20 (95:00 - 98:00) [1:35:00]

And then once they do that, once the player touches the actual flag at the end of the level, they should generate the next level. So right now there isn't really a way to transition between levels, but take the level that you're in right now and then just generate a new level and then make sure that it's just a little bit bigger than the level that you're currently in. Additionally, folks might have noticed on the actual sprite sheet there's a animation for the character to climb up a ladder and there is a ladder as well. So the next part would be to generate pillars that are higher than four tiles tall. So something that the player cannot jump over. Currently, they can just jump over pillars, but we're going to make it such that maybe jump maybe generate it one or two tiles taller such that you can't actually generate or jump over it and then add a ladder to the side of it so that you can climb the ladder instead get to the top of the pillar and then jump down or fall down. This implying that you'll need of course a new state, a ladder climbing state in order to and therefore model the relation model the ways you get out of that state uh in order to allow this to be possible and therefore allow you to sort of climb on the yaxis sort of more arbitrarily. And then also the last thing would be which we alluded to briefly a power up. So right now we just have gems. You can interact with a gem to increase your score, but folks should choose like a star or something else that will allow you to become invincible and then therefore interact with a snail if you're just walking, for example, and have some sort of particle effect. Folks maybe can even add music if they want to, which is what Mario does. But add a particle effect with the likes that when you're walking into a snail, then with that new invincibility mode on, the superstar mode on, you defeat the snail without having to jump on top of it and it does not affect you. You are invincible. So that was it for Mario. Next time we're going to do a few things in like a kind of a similar era which is Zelda which was one of the you know the first game that I remember playing which was the Super Nintendo Legend Zelda Link to the Past. U in this case we'll do a simple example. We even included the hearts in there but you know so far we haven't really done a top down perspective type game. So we'll explore how to do that movement in two axes therefore left and right. A little bit different than platformers with gravity but in some ways simpler. And we'll be looking at a few other cool tricks like stenciling. Triggers and events will be important. As folks might be able to see, there's a switch on the ground there. Bunch of enemies also all moving around and interacting. We we're taking a look at that sort of basic AI type stuff which is kind of an extension of what we did today. But there's a sort of set of doors around the side of the level there. Folks can hit the switch that will trigger an event which will then open the doors. Those will be listening to an event uh that gets fired that then will get handled through a function that will open the doors and then folks can then move through the dungeon and then go to another randomly generated room with switches and doors. And then also uh really key folks will be able to actually swing a sword and inflict damage. And that's going to require the use of a hurt box. And then lastly, in particular, in relation to the problem set, we'll be exploring the idea of inventory and how to model that in order to show how to, for example, acquire and use a boomerang to attack enemies from a distance. So, that was Mario. It was probably one of my at least more favorite lectures in terms of I really like the idea of these virtual worlds that we get to explore and use tile maps to represent that. There's a lot of things we just introduced, game objects and entities and all these things, but I think it sets the stage for a lot of really cool things coming forward. So, we look forward to that next time. This is CS52D Super Mario. We'll see you next time.
