1 00:00:00,000 --> 00:00:02,976 [MUSIC PLAYING] 2 00:00:02,976 --> 00:00:16,097 3 00:00:16,097 --> 00:00:17,430 COLTON OGDEN: Hey, good evening. 4 00:00:17,430 --> 00:00:19,970 Welcome to GD50 lecture four. 5 00:00:19,970 --> 00:00:22,140 This is Super Mario Bros. 6 00:00:22,140 --> 00:00:23,990 As seen on the slides here, though, we're 7 00:00:23,990 --> 00:00:26,220 not using the actual Super Mario Bros. sprite sheet. 8 00:00:26,220 --> 00:00:27,260 This is sort of like a rip off. 9 00:00:27,260 --> 00:00:28,968 But I found a really awesome sprite sheet 10 00:00:28,968 --> 00:00:33,590 that has all the basic tiles that we need to get this thing working. 11 00:00:33,590 --> 00:00:36,782 There's a link in the distro as to where you can find it online. 12 00:00:36,782 --> 00:00:38,240 I had a lot of fun playing with it. 13 00:00:38,240 --> 00:00:40,040 So hopefully, maybe if you're curious, you 14 00:00:40,040 --> 00:00:43,100 can use some of the sprites in there to go off and do your own thing. 15 00:00:43,100 --> 00:00:48,740 But Super Mario Bros.-- the actual game which this lecture and assignment are 16 00:00:48,740 --> 00:00:51,822 based off of-- is the game shown here. 17 00:00:51,822 --> 00:00:53,280 I think everybody knows what it is. 18 00:00:53,280 --> 00:00:55,238 It's probably the most famous game of all time. 19 00:00:55,238 --> 00:00:57,260 But this game came out in 1985-- 20 00:00:57,260 --> 00:00:59,720 sort of revolutionized the gaming industry. 21 00:00:59,720 --> 00:01:03,890 It was the game that brought the gaming industry from a crash 22 00:01:03,890 --> 00:01:08,530 in the '70s thanks to a lot of poor game making policies 23 00:01:08,530 --> 00:01:11,630 and companies and low QA standards. 24 00:01:11,630 --> 00:01:15,200 It basically took the gaming crash of the late '70s, early '80s 25 00:01:15,200 --> 00:01:19,340 and brought games really back to the forefront of people's consciousness. 26 00:01:19,340 --> 00:01:22,700 This and games like Legend of Zelda and a lot of other NES titles 27 00:01:22,700 --> 00:01:25,280 made Nintendo basically the dominator of the video games 28 00:01:25,280 --> 00:01:27,346 industry in the '80s and '90s. 29 00:01:27,346 --> 00:01:29,470 And even today, with games like Breath of the Wild, 30 00:01:29,470 --> 00:01:31,670 they're still doing their thing. 31 00:01:31,670 --> 00:01:32,900 This is Super Mario Bros. 32 00:01:32,900 --> 00:01:34,670 It's a 2D platformer. 33 00:01:34,670 --> 00:01:38,270 And what this basically means is you control Mario, who's a plumber. 34 00:01:38,270 --> 00:01:41,570 He goes around, walks sort of looking at him from the side. 35 00:01:41,570 --> 00:01:42,800 He walks left to right. 36 00:01:42,800 --> 00:01:44,130 He can jump up and down. 37 00:01:44,130 --> 00:01:45,240 He's affected by gravity. 38 00:01:45,240 --> 00:01:46,280 He can hit blocks. 39 00:01:46,280 --> 00:01:47,420 He can jump on enemies. 40 00:01:47,420 --> 00:01:50,450 He can go down pipes, and there's a bunch of levels. 41 00:01:50,450 --> 00:01:53,350 It was, for its time, quite a complicated game, 42 00:01:53,350 --> 00:01:58,660 and it spawned numerous offshoots and rip offs and other good quality 43 00:01:58,660 --> 00:02:00,580 platformers. 44 00:02:00,580 --> 00:02:02,550 While we talk about Super Mario Bros. today, 45 00:02:02,550 --> 00:02:05,840 some of the topics we'll actually be talking about are tile maps-- 46 00:02:05,840 --> 00:02:10,130 so how we can take basically a series of numbers-- 47 00:02:10,130 --> 00:02:13,640 tile IDs-- and turn that into a game world. 48 00:02:13,640 --> 00:02:20,260 As you can see here, the game is broken up into blocks of 16 by 16 tiles. 49 00:02:20,260 --> 00:02:22,460 You can see the bricks and the question mark blocks, 50 00:02:22,460 --> 00:02:26,630 and the pipes are even all composed of simple tiles. 51 00:02:26,630 --> 00:02:28,610 And they map to IDs. 52 00:02:28,610 --> 00:02:34,100 And when you take a 2D table or array and you just iterate over all of it 53 00:02:34,100 --> 00:02:37,550 and render the appropriate tile at the appropriate x, y, 54 00:02:37,550 --> 00:02:40,190 you get the appearance of existing in some game world, 55 00:02:40,190 --> 00:02:43,769 even though it's just composed of a bunch of tiny little blocks. 56 00:02:43,769 --> 00:02:45,560 2D animation is something we'll talk about. 57 00:02:45,560 --> 00:02:47,393 So far, we haven't really done any animation 58 00:02:47,393 --> 00:02:50,790 at all in terms of at least characters. 59 00:02:50,790 --> 00:02:52,250 We'll do that with Mario. 60 00:02:52,250 --> 00:02:55,610 He'll have-- our version of Mario, an alien-- 61 00:02:55,610 --> 00:02:58,006 when he's moving, he'll have two frames of animation. 62 00:02:58,006 --> 00:03:00,380 The frames of animation-- that's sort of like a flip book 63 00:03:00,380 --> 00:03:03,005 if you've ever used one, where you can see individual pictures. 64 00:03:03,005 --> 00:03:05,180 And when you display them rapidly back to back, 65 00:03:05,180 --> 00:03:07,190 you get the appearance of animation. 66 00:03:07,190 --> 00:03:08,480 We'll be talking about that. 67 00:03:08,480 --> 00:03:11,300 Procedural level generation-- we'll be making all of our levels 68 00:03:11,300 --> 00:03:12,570 generate randomly. 69 00:03:12,570 --> 00:03:14,900 So every time we play the game from the beginning, 70 00:03:14,900 --> 00:03:16,566 everything will be completely different. 71 00:03:16,566 --> 00:03:19,370 We don't have to hard code a finite set of levels. 72 00:03:19,370 --> 00:03:23,180 Everything will be dynamic and also interesting, in my opinion. 73 00:03:23,180 --> 00:03:25,820 We'll talk about the basics of platformer physics 74 00:03:25,820 --> 00:03:27,740 and how we can apply that to our game world 75 00:03:27,740 --> 00:03:34,310 here, because we are just using a table of tiles, 76 00:03:34,310 --> 00:03:37,730 each with an x, y that's hard coded in the game world space. 77 00:03:37,730 --> 00:03:41,330 All we have to really do is take an x, y of Mario, for example, 78 00:03:41,330 --> 00:03:44,150 and then just divide that by the tile size. 79 00:03:44,150 --> 00:03:46,670 And then we get basically what tile that is 80 00:03:46,670 --> 00:03:48,960 in our array at that point in the world. 81 00:03:48,960 --> 00:03:53,750 And so it's really easy to do arbitrary collision detection based 82 00:03:53,750 --> 00:03:56,210 on what direction you're going and not have to iterate over 83 00:03:56,210 --> 00:03:57,860 every single tile in your world. 84 00:03:57,860 --> 00:04:01,430 Because it's just a simple mathematical operation to get the exact tile 85 00:04:01,430 --> 00:04:05,000 given two coordinates, since the world is in a fixed space. 86 00:04:05,000 --> 00:04:07,250 We'll have a little snail in our game that 87 00:04:07,250 --> 00:04:09,680 walks around and does a couple of random animations 88 00:04:09,680 --> 00:04:13,070 and will go after the player, sort of like a little basic intro to AI. 89 00:04:13,070 --> 00:04:16,250 And then lastly, we'll touch on things like power ups and game objects 90 00:04:16,250 --> 00:04:19,200 and how we might be able to influence Mario and pick those up 91 00:04:19,200 --> 00:04:21,079 and that sort of thing. 92 00:04:21,079 --> 00:04:22,370 So first though, a demo. 93 00:04:22,370 --> 00:04:24,560 So if anybody is willing to come up on stage 94 00:04:24,560 --> 00:04:29,660 to test out my implementation of Mario, that would be awesome. 95 00:04:29,660 --> 00:04:31,440 James? 96 00:04:31,440 --> 00:04:35,970 I'm going to go ahead into here, so we should be all ready to go. 97 00:04:35,970 --> 00:04:39,360 So as soon as you're ready, go ahead and hit Return there, 98 00:04:39,360 --> 00:04:42,510 and you should be up and running. 99 00:04:42,510 --> 00:04:45,380 So as a part of having random levels-- 100 00:04:45,380 --> 00:04:49,160 so currently, we have a green alien. 101 00:04:49,160 --> 00:04:53,270 The blocks have a random chance in this case to spawn a gem. 102 00:04:53,270 --> 00:04:56,469 And so once they do, you can pick the gem up. 103 00:04:56,469 --> 00:04:58,010 Either they have a gem or they don't. 104 00:04:58,010 --> 00:05:00,330 You can pick it up, and you get 100 points. 105 00:05:00,330 --> 00:05:02,180 As we can see, the world is sort of shifting 106 00:05:02,180 --> 00:05:07,537 based on where James's avatar is, so it tracks the character. 107 00:05:07,537 --> 00:05:08,870 We have some notion of a camera. 108 00:05:08,870 --> 00:05:11,430 You're getting unlucky with the blocks so far. 109 00:05:11,430 --> 00:05:15,810 So you can fall down through the spaces, so you probably want to avoid that. 110 00:05:15,810 --> 00:05:18,314 But if you want to demonstrate doing it-- 111 00:05:18,314 --> 00:05:20,730 so in that case, we collided with the two blocks below it. 112 00:05:20,730 --> 00:05:23,070 The one on the right had the gem. 113 00:05:23,070 --> 00:05:26,160 So go ahead and just fall down so we can demonstrate. 114 00:05:26,160 --> 00:05:28,350 So when we fall down, we detect whether the player 115 00:05:28,350 --> 00:05:31,312 has gone below the world limit, and then we start him back 116 00:05:31,312 --> 00:05:32,520 at the beginning of the game. 117 00:05:32,520 --> 00:05:35,220 You can press Enter, it should regenerate a brand new world. 118 00:05:35,220 --> 00:05:38,400 Notice how we have random holes in the ground. 119 00:05:38,400 --> 00:05:39,960 We have random tiles. 120 00:05:39,960 --> 00:05:41,697 We have random toppers for them. 121 00:05:41,697 --> 00:05:42,780 All the blocks are random. 122 00:05:42,780 --> 00:05:44,220 We have snails now. 123 00:05:44,220 --> 00:05:45,960 They're sort of chasing after James. 124 00:05:45,960 --> 00:05:47,469 He can jump on top of them. 125 00:05:47,469 --> 00:05:50,010 There's a lot of little moving pieces here, but a lot of them 126 00:05:50,010 --> 00:05:51,270 are actually pretty simple. 127 00:05:51,270 --> 00:05:54,610 And I'll show you very shortly. 128 00:05:54,610 --> 00:05:55,970 JAMES: Should I stop? 129 00:05:55,970 --> 00:05:56,490 COLTON OGDEN: Yeah, sure. 130 00:05:56,490 --> 00:05:57,390 That would be a great point. 131 00:05:57,390 --> 00:05:58,098 So thanks, James. 132 00:05:58,098 --> 00:05:59,610 Appreciate it. 133 00:05:59,610 --> 00:06:04,470 Currently, there is no notion of a level ending. 134 00:06:04,470 --> 00:06:06,300 That's part of the piece that actually will 135 00:06:06,300 --> 00:06:09,600 spawn an object that the player can interact with to just sort of retrigger 136 00:06:09,600 --> 00:06:11,490 a new level, basically. 137 00:06:11,490 --> 00:06:18,670 But the whole engine behind this basic platformer is there, and it all works. 138 00:06:18,670 --> 00:06:22,410 And so our goal is seen here. 139 00:06:22,410 --> 00:06:24,300 Our goal in this lecture is to demonstrate 140 00:06:24,300 --> 00:06:27,960 how we can get things like a character that moves around on a screen, 141 00:06:27,960 --> 00:06:32,520 and a camera that tracks their position, and tiles that are randomized. 142 00:06:32,520 --> 00:06:35,220 And maybe there are pillars in the ground, holes in the ground. 143 00:06:35,220 --> 00:06:36,640 All of this, again-- 144 00:06:36,640 --> 00:06:38,640 at least the tiles-- are stored as just numbers. 145 00:06:38,640 --> 00:06:42,960 So all we really need to do is perform a transformation on a series of numbers. 146 00:06:42,960 --> 00:06:48,150 Maybe 1 is equal to a tile being there, 0 is equal to empty space. 147 00:06:48,150 --> 00:06:51,360 And so just by looking at it, we'll see we go column by column. 148 00:06:51,360 --> 00:06:55,140 We can say, oh, maybe there's a chance to not spawn any tiles along the y 149 00:06:55,140 --> 00:06:58,890 column on this x of the world map. 150 00:06:58,890 --> 00:07:02,670 Or on this particular y, maybe instead of spawning the ground level, 151 00:07:02,670 --> 00:07:05,972 we spawn a couple above it and down so that we get a pillar 152 00:07:05,972 --> 00:07:06,930 and so on and so forth. 153 00:07:06,930 --> 00:07:10,080 And it's just this summation of these randomizations 154 00:07:10,080 --> 00:07:14,620 equals a nice little variety of game levels. 155 00:07:14,620 --> 00:07:18,300 So the first thing we should talk about really is what a tile map is. 156 00:07:18,300 --> 00:07:21,720 And what I've alluded to so far is you can really think of a tile map 157 00:07:21,720 --> 00:07:27,030 as being effectively a 2D array or a table of numbers. 158 00:07:27,030 --> 00:07:30,180 And it's a little more complicated than that depending on how complex 159 00:07:30,180 --> 00:07:35,610 your platformer is, because some numbers are equal to tiles that are solid 160 00:07:35,610 --> 00:07:36,670 or not. 161 00:07:36,670 --> 00:07:39,840 So you should be able to check whether a tile is collidable, 162 00:07:39,840 --> 00:07:42,930 meaning that the player or whatever entity you want to check for 163 00:07:42,930 --> 00:07:44,930 can actually collide with it or 164 00:07:44,930 --> 00:07:45,430 Not. 165 00:07:45,430 --> 00:07:48,400 So obviously, we don't want to trigger a collision on empty tiles. 166 00:07:48,400 --> 00:07:50,400 We want the player to move freely through those. 167 00:07:50,400 --> 00:07:53,280 But if they run up against a wall or if gravity is affecting them, 168 00:07:53,280 --> 00:07:56,970 and they hit tiles below them or above them, we want to detect a collision 169 00:07:56,970 --> 00:07:59,627 and then stop them based on which direction they're moving. 170 00:07:59,627 --> 00:08:02,460 And depending on how complicated you get with your platformer, maybe 171 00:08:02,460 --> 00:08:04,422 you have animated tiles, for instance. 172 00:08:04,422 --> 00:08:07,380 So if a tile's animated, it will display a different frame of animation 173 00:08:07,380 --> 00:08:09,330 based on what timer you're on. 174 00:08:09,330 --> 00:08:10,590 Really, the sky's the limit. 175 00:08:10,590 --> 00:08:12,630 In this case, we'll be fairly simple. 176 00:08:12,630 --> 00:08:17,520 Our tiles will mostly just be numbers with a couple of other traits, which 177 00:08:17,520 --> 00:08:19,920 we'll see later on. 178 00:08:19,920 --> 00:08:22,840 And this is just an example here of a very simple map-- just a colored 179 00:08:22,840 --> 00:08:23,340 background. 180 00:08:23,340 --> 00:08:28,320 We have our character, and then we can sort of visualize all of those tiles 181 00:08:28,320 --> 00:08:34,307 as being just for the sake of theory 0s or 1s. 182 00:08:34,307 --> 00:08:37,140 So tiles0-- so I'll actually get into a little bit of implementation 183 00:08:37,140 --> 00:08:41,789 here as to how we can get drawing some very simple tiles. 184 00:08:41,789 --> 00:08:46,050 So if you're looking at the distro, in tiles0 185 00:08:46,050 --> 00:08:47,990 is going to be where we start off here. 186 00:08:47,990 --> 00:08:53,200 And I'm going to go ahead and run tiles0 so we can see what that looks like. 187 00:08:53,200 --> 00:08:54,330 So this is just tiles0. 188 00:08:54,330 --> 00:08:58,560 It's a much simpler program than what we just saw, but all we're doing here 189 00:08:58,560 --> 00:09:01,560 is just a color in the background and then tiles. 190 00:09:01,560 --> 00:09:05,567 191 00:09:05,567 --> 00:09:08,400 Off the gate, anybody have any ideas as to what the first step would 192 00:09:08,400 --> 00:09:10,520 be if we wanted to implement this? 193 00:09:10,520 --> 00:09:14,504 194 00:09:14,504 --> 00:09:19,304 AUDIENCE: Just put the tiles in a loop, draw them, and then have a background? 195 00:09:19,304 --> 00:09:21,470 COLTON OGDEN: So put the tiles in a loop, draw them, 196 00:09:21,470 --> 00:09:22,580 and then have a background. 197 00:09:22,580 --> 00:09:23,079 Yes. 198 00:09:23,079 --> 00:09:29,600 So basically, if this is main.lua in our tiles0, 199 00:09:29,600 --> 00:09:33,590 first thing we're going to need is a tiles table to store our-- 200 00:09:33,590 --> 00:09:35,990 we're not going to be storing just flat numbers. 201 00:09:35,990 --> 00:09:39,350 We'll be storing little mini tables that have a number in them and ID, 202 00:09:39,350 --> 00:09:45,432 so we can say tile.ID if we have a 2D table. 203 00:09:45,432 --> 00:09:46,640 Here, we have an empty table. 204 00:09:46,640 --> 00:09:47,848 We're going to populate that. 205 00:09:47,848 --> 00:09:50,780 206 00:09:50,780 --> 00:09:54,470 If we're going to draw our tiles, we are going to need a sprite of some kind. 207 00:09:54,470 --> 00:09:57,980 And what I did was I just chopped out a little segment here. 208 00:09:57,980 --> 00:10:00,230 So this is tiles.png. 209 00:10:00,230 --> 00:10:04,076 It's just literally one tile from the main sprite 210 00:10:04,076 --> 00:10:05,450 sheet that comes with the distro. 211 00:10:05,450 --> 00:10:12,710 And then on the right side is just transparent so that we can offset-- 212 00:10:12,710 --> 00:10:19,370 maybe tile ID 1 is equal to solid block, and then tile ID 2 is equal to empty. 213 00:10:19,370 --> 00:10:24,050 And so if we recall generate quads, we can split up a sprite sheet 214 00:10:24,050 --> 00:10:26,560 into however many quads we want to. 215 00:10:26,560 --> 00:10:29,300 Let's say this is 16 tiles tall-- 216 00:10:29,300 --> 00:10:33,080 each tile-- and then the whole thing are two tiles wide. 217 00:10:33,080 --> 00:10:36,835 So it needs to be split into two separate tiles. 218 00:10:36,835 --> 00:10:41,330 We'll just generate quads, and then we'll have, recall, a table. 219 00:10:41,330 --> 00:10:45,290 Each of the indexes of that table will be 220 00:10:45,290 --> 00:10:47,560 a quad that maps to one of these tiles. 221 00:10:47,560 --> 00:10:50,270 So number 1 will be this tile here, number 2 222 00:10:50,270 --> 00:10:53,120 will be the transparent bit over here, and then 223 00:10:53,120 --> 00:10:55,929 that's how effectively our IDs are going to map 224 00:10:55,929 --> 00:10:57,470 into what gets drawn onto the screen. 225 00:10:57,470 --> 00:11:02,130 The ID is the index into our quad table. 226 00:11:02,130 --> 00:11:08,890 So going back into tiles0, we have here just a map width and height. 227 00:11:08,890 --> 00:11:13,880 We're just going to say generate a map 20 by 20. 228 00:11:13,880 --> 00:11:15,860 RBG-- we're just going to make it random, 229 00:11:15,860 --> 00:11:19,280 so we're going to clear the screen with a random color. 230 00:11:19,280 --> 00:11:24,020 And then this is the quads = GenerateQuads. 231 00:11:24,020 --> 00:11:26,114 And notice that we're passing in tile size here. 232 00:11:26,114 --> 00:11:28,530 It's good practice just to make your tile size a constant. 233 00:11:28,530 --> 00:11:31,940 So our tile size in this entire lecture-- 234 00:11:31,940 --> 00:11:33,710 they're all going to be 16 by 16. 235 00:11:33,710 --> 00:11:36,950 And so since they're symmetrical, we just pass in tile size TILE_SIZE. 236 00:11:36,950 --> 00:11:40,130 And then here is where we actually end up spawning the map-- 237 00:11:40,130 --> 00:11:42,960 so nested for loop. 238 00:11:42,960 --> 00:11:47,290 y gets 1 to map height, x gets 1 to map width. 239 00:11:47,290 --> 00:11:50,920 Remember, we have to insert a blank table into the base table that's 240 00:11:50,920 --> 00:11:52,700 going to act as our current row. 241 00:11:52,700 --> 00:11:56,570 And then in that row, we're going to add a small at tiles y, 242 00:11:56,570 --> 00:12:01,280 because y is going to be up here-- our current row and ID. 243 00:12:01,280 --> 00:12:07,790 And so what we're doing here is if y is less than 5-- 244 00:12:07,790 --> 00:12:11,150 meaning we'll just set an arbitrary point for the ground, basically. 245 00:12:11,150 --> 00:12:15,810 If it's less than 5 tiles from the top, then just make it the sky. 246 00:12:15,810 --> 00:12:19,340 And so sky-- up here on line 24, 25-- we just 247 00:12:19,340 --> 00:12:20,900 set two tile IDs, as I said before. 248 00:12:20,900 --> 00:12:23,772 Sky is 2, so it's going to be on the right side of the sheet. 249 00:12:23,772 --> 00:12:24,730 And then ground is one. 250 00:12:24,730 --> 00:12:28,230 It's going to be the very first quad generated in the sheet. 251 00:12:28,230 --> 00:12:33,200 So if y is less than 5, that ID should be equal to sky else 252 00:12:33,200 --> 00:12:35,420 it should be equal to ground. 253 00:12:35,420 --> 00:12:38,850 And so down here is where that comes into play. 254 00:12:38,850 --> 00:12:41,690 We're going to clear the screen with our random color. 255 00:12:41,690 --> 00:12:44,690 We're going to iterate over the loop, as James said. 256 00:12:44,690 --> 00:12:47,990 We're going to get the tile at tiles y x, 257 00:12:47,990 --> 00:12:53,630 and then we're just going to draw the sheet and the quads at that tiles ID. 258 00:12:53,630 --> 00:12:58,850 And then recall, since tables are 1 indexed but coordinates are 0 indexed, 259 00:12:58,850 --> 00:13:01,640 we take the x and the y, subtract 1 from them, 260 00:13:01,640 --> 00:13:03,680 and then we just multiply them by tile size. 261 00:13:03,680 --> 00:13:07,280 And that has the effect of drawing each of those tiles 262 00:13:07,280 --> 00:13:10,190 at their respective point in the world and making 263 00:13:10,190 --> 00:13:14,060 it seem as if we have this world-- this bunch of bricks 264 00:13:14,060 --> 00:13:17,010 with a random background every time. 265 00:13:17,010 --> 00:13:20,550 Which isn't all that interesting, but just a little bit more variety. 266 00:13:20,550 --> 00:13:22,910 And so that's the very basic gist behind it. 267 00:13:22,910 --> 00:13:26,118 I mean, it's essentially almost the same thing as what we did in match three, 268 00:13:26,118 --> 00:13:29,210 where we just mapped the individual tiles that 269 00:13:29,210 --> 00:13:38,090 were in the grid to indexes in the tile sheet based on the color and variety. 270 00:13:38,090 --> 00:13:41,370 Only this time, they're always going to be in the exact same place, 271 00:13:41,370 --> 00:13:43,760 so we don't have to worry about whether their x and y are 272 00:13:43,760 --> 00:13:45,440 different from their grid y and grid x. 273 00:13:45,440 --> 00:13:48,710 We're not maintaining a reference to those. 274 00:13:48,710 --> 00:13:49,999 And so that's static tiles. 275 00:13:49,999 --> 00:13:52,040 Does anybody have any questions about how we just 276 00:13:52,040 --> 00:13:53,769 draw static tiles to the screen? 277 00:13:53,769 --> 00:13:54,560 Pretty basic stuff. 278 00:13:54,560 --> 00:13:58,440 279 00:13:58,440 --> 00:14:02,330 The whole name behind side scrolling game 280 00:14:02,330 --> 00:14:06,742 is that the tiles scroll based on what we're doing in the game. 281 00:14:06,742 --> 00:14:08,450 It can be an auto scroller, in which case 282 00:14:08,450 --> 00:14:10,802 maybe you're an airplane that's sort of going 283 00:14:10,802 --> 00:14:12,760 through a level that's scrolling automatically, 284 00:14:12,760 --> 00:14:14,080 and you're shooting things. 285 00:14:14,080 --> 00:14:16,121 And you're not really in control of where you go. 286 00:14:16,121 --> 00:14:18,549 Or it can be like Mario, where you control an avatar. 287 00:14:18,549 --> 00:14:20,340 And you can walk around and jump and stuff, 288 00:14:20,340 --> 00:14:22,560 and the camera will always be fixed on you. 289 00:14:22,560 --> 00:14:27,180 And so the scrolling is just relative to where your character's x and y are. 290 00:14:27,180 --> 00:14:30,480 So I'm going to show you guys an example of how we can 291 00:14:30,480 --> 00:14:32,357 get scrolling implemented in our game. 292 00:14:32,357 --> 00:14:35,190 And to do that, the function that we're really going to be looking-- 293 00:14:35,190 --> 00:14:37,290 at a new function-- 294 00:14:37,290 --> 00:14:41,250 is love.graphics.translate(x, y). 295 00:14:41,250 --> 00:14:45,240 And so what that does is effectively just translates 296 00:14:45,240 --> 00:14:48,840 Love2D's coordinate system so that whenever we draw something, 297 00:14:48,840 --> 00:14:52,190 it gets automatically shifted by x, y. 298 00:14:52,190 --> 00:14:58,770 And so that has the effect of the everything being sort of skewed 299 00:14:58,770 --> 00:15:00,630 based on the x, y that we pass it. 300 00:15:00,630 --> 00:15:05,100 And so if we maintain a reference to where the character is, 301 00:15:05,100 --> 00:15:09,480 we can just shift where everything gets drawn on the screen. 302 00:15:09,480 --> 00:15:13,020 And that will have the effect of it being a camera, but it's not. 303 00:15:13,020 --> 00:15:15,390 All we're doing is just shifting the coordinate system 304 00:15:15,390 --> 00:15:16,720 based on some offset-- 305 00:15:16,720 --> 00:15:19,914 x being in this case where the players is effectively. 306 00:15:19,914 --> 00:15:22,080 AUDIENCE: So it changes the whole coordinate system? 307 00:15:22,080 --> 00:15:22,996 COLTON OGDEN: It does. 308 00:15:22,996 --> 00:15:27,210 It shifts everything in the coordinate system that you draw by the x and y. 309 00:15:27,210 --> 00:15:31,170 And so that will basically affect what's getting rendered 310 00:15:31,170 --> 00:15:32,820 into the active window at that time. 311 00:15:32,820 --> 00:15:35,710 312 00:15:35,710 --> 00:15:38,580 So I'm going to go ahead and pull up tiles1 here 313 00:15:38,580 --> 00:15:40,696 so we can see how this works. 314 00:15:40,696 --> 00:15:44,290 Let me go ahead and first run the program. 315 00:15:44,290 --> 00:15:47,970 So if we're going into tiles1 in the distro, 316 00:15:47,970 --> 00:15:49,980 currently it looks almost identical. 317 00:15:49,980 --> 00:15:53,610 But I can move it if I just press left or right. 318 00:15:53,610 --> 00:15:57,810 And so we can see here, this is where the 2D array of tiles 319 00:15:57,810 --> 00:15:59,140 gets cut off here. 320 00:15:59,140 --> 00:16:02,550 And then it also cuts off, because we're only generating a 20 by 20 level. 321 00:16:02,550 --> 00:16:05,560 It also gets cut off at the very right side as well. 322 00:16:05,560 --> 00:16:08,310 And these are details you would normally hide from the user 323 00:16:08,310 --> 00:16:13,740 by just clamping the x between 0 and the right side 324 00:16:13,740 --> 00:16:17,064 of the map minus VIRTUAL_WIDTH. 325 00:16:17,064 --> 00:16:19,730 And that will have the effect of whenever you get to this point, 326 00:16:19,730 --> 00:16:22,599 it won't let you go right anymore, and same thing for the left side. 327 00:16:22,599 --> 00:16:24,390 Well, all we're doing right now-- we're not 328 00:16:24,390 --> 00:16:26,170 doing it based on the character at all. 329 00:16:26,170 --> 00:16:29,590 We're just using keyboard input. 330 00:16:29,590 --> 00:16:32,100 So let's go ahead into tiles1. 331 00:16:32,100 --> 00:16:35,280 332 00:16:35,280 --> 00:16:41,520 And so the important thing that we're going to look at is-- 333 00:16:41,520 --> 00:16:45,090 as I just alluded to, we're calling love.graphics.translate 334 00:16:45,090 --> 00:16:47,460 on some value called cameraScroll. 335 00:16:47,460 --> 00:16:53,010 336 00:16:53,010 --> 00:16:56,930 It has to be a negative value, because if we're moving to the right 337 00:16:56,930 --> 00:16:59,246 up here or to the left-- 338 00:16:59,246 --> 00:17:02,370 if we're moving to the left, camera scroll basically is going to decrement, 339 00:17:02,370 --> 00:17:03,453 so it's going to get less. 340 00:17:03,453 --> 00:17:05,339 So we can say the camera scroll when we're 341 00:17:05,339 --> 00:17:08,470 going left is going to be 0 or less if we're starting at 0, 342 00:17:08,470 --> 00:17:10,170 or it's going to decrement. 343 00:17:10,170 --> 00:17:14,760 If we press right, camera roll should increase. 344 00:17:14,760 --> 00:17:19,480 If we want the appearance of moving to the right or moving to the left, 345 00:17:19,480 --> 00:17:22,829 you actually have to translate by the opposite direction. 346 00:17:22,829 --> 00:17:34,840 Because if we look at this, and if we call love.graphics.translate positive, 347 00:17:34,840 --> 00:17:36,810 all of this is going to get moved to the right. 348 00:17:36,810 --> 00:17:39,330 So it's going to have the appearance of us moving left. 349 00:17:39,330 --> 00:17:41,690 And if we translate it to the left by a negative amount, 350 00:17:41,690 --> 00:17:44,370 it's going to have the appearance of us moving right. 351 00:17:44,370 --> 00:17:48,570 So if our scroll is positive and we want to move to the right, 352 00:17:48,570 --> 00:17:52,330 we actually have to translate by a negative amount. 353 00:17:52,330 --> 00:18:00,660 And so that's why I'm calling negative math.floor(cameraScroll). 354 00:18:00,660 --> 00:18:04,710 Does anybody know why we're calling math.floor on cameraScroll 355 00:18:04,710 --> 00:18:09,980 instead of just calling negative camera scroll? 356 00:18:09,980 --> 00:18:13,950 Does anybody remember what math.floor does? 357 00:18:13,950 --> 00:18:17,560 So math.floor will return the-- 358 00:18:17,560 --> 00:18:21,450 it'll basically truncate the number down to the lowest integer. 359 00:18:21,450 --> 00:18:24,330 It will basically take off the floating point value. 360 00:18:24,330 --> 00:18:26,400 Because we're rendering to a virtual resolution 361 00:18:26,400 --> 00:18:32,640 with push, if we basically offset the translation by a fractional amount, 362 00:18:32,640 --> 00:18:34,020 you'll get artifacting. 363 00:18:34,020 --> 00:18:37,470 Because it's taking your window and just condensing 364 00:18:37,470 --> 00:18:42,280 your image onto a virtual canvas, you'll get weird blur and stuff like that. 365 00:18:42,280 --> 00:18:48,330 So whenever you draw something and you have a fractional number for something, 366 00:18:48,330 --> 00:18:51,840 and you're drawing it to a virtual canvas that's been magnified 367 00:18:51,840 --> 00:18:55,410 or it's being condensed, just make sure to math.floor it 368 00:18:55,410 --> 00:18:58,710 so you don't get any weird blur artifacting. 369 00:18:58,710 --> 00:19:02,056 If you take this out and experiment around, or even if in the distro 370 00:19:02,056 --> 00:19:04,680 you take it out of the player's position, you'll see the player 371 00:19:04,680 --> 00:19:07,560 will get weird blurry artifacting and stuff like that. 372 00:19:07,560 --> 00:19:10,912 So that's why that's there, in case you're curious. 373 00:19:10,912 --> 00:19:12,870 And so all we're doing here-- we're just saying 374 00:19:12,870 --> 00:19:16,580 if it's equal to left, scroll the camera left, scroll it right, 375 00:19:16,580 --> 00:19:22,910 or basically decrement our camera scroll and then increment or camera scroll. 376 00:19:22,910 --> 00:19:25,970 And then just use the negative version of that here. 377 00:19:25,970 --> 00:19:30,344 You could also just assign camera scroll equal to positive 378 00:19:30,344 --> 00:19:32,510 when you move left and negative when you move right, 379 00:19:32,510 --> 00:19:35,690 and then you could give it the regular camera scroll here. 380 00:19:35,690 --> 00:19:39,020 But it's sort of mentally flipped in terms of this part. 381 00:19:39,020 --> 00:19:44,480 So I just made the decision to decrement it here when we're pressing left, 382 00:19:44,480 --> 00:19:51,050 because we're going less on the x and then more on the x when we press right. 383 00:19:51,050 --> 00:19:52,010 Does this make sense? 384 00:19:52,010 --> 00:19:52,665 Anybody have question? 385 00:19:52,665 --> 00:19:53,831 AUDIENCE: I have a question. 386 00:19:53,831 --> 00:19:56,222 Is there a corresponding function in JavaScript 387 00:19:56,222 --> 00:20:01,672 and in other languages like this where you can shift a whole coordinate? 388 00:20:01,672 --> 00:20:04,130 COLTON OGDEN: Is there an equivalent function in JavaScript 389 00:20:04,130 --> 00:20:06,640 where you can shift the whole coordinate system? 390 00:20:06,640 --> 00:20:09,020 Not in base JavaScript, probably. 391 00:20:09,020 --> 00:20:10,760 I'm not too familiar with CSS. 392 00:20:10,760 --> 00:20:14,040 There might be a CSS function that does it. 393 00:20:14,040 --> 00:20:18,120 In a lot of 2D game engines, yes, I would say. 394 00:20:18,120 --> 00:20:22,310 And a lot of actual 2D game engines will have a camera object, which 395 00:20:22,310 --> 00:20:24,110 sort of encapsulates this behavior. 396 00:20:24,110 --> 00:20:26,480 Love2D doesn't have a camera, so this is sort 397 00:20:26,480 --> 00:20:30,200 of why we're doing this-- is because it's kind of a lower level game 398 00:20:30,200 --> 00:20:30,952 framework, Love2D. 399 00:20:30,952 --> 00:20:32,660 It doesn't really give you as many things 400 00:20:32,660 --> 00:20:36,800 right out the gate, which makes it great for teaching these concepts. 401 00:20:36,800 --> 00:20:41,180 But a more robust solution like Unity or Phaser 402 00:20:41,180 --> 00:20:45,350 or a lot of other game frameworks is that they'll just have a camera object. 403 00:20:45,350 --> 00:20:49,270 And you just basically give that your x, and then you just move that. 404 00:20:49,270 --> 00:20:51,170 You basically tell that to track the player-- 405 00:20:51,170 --> 00:20:55,985 like camera.trackPlayer or trackEntityPlayer-- 406 00:20:55,985 --> 00:20:57,360 and that'll have the same effect. 407 00:20:57,360 --> 00:20:58,693 It's a little bit more abstract. 408 00:20:58,693 --> 00:21:00,800 It's a higher level than what we're doing, 409 00:21:00,800 --> 00:21:04,427 but it's the same exact principle underlying. 410 00:21:04,427 --> 00:21:06,260 So any other questions as to how this works? 411 00:21:06,260 --> 00:21:09,780 412 00:21:09,780 --> 00:21:11,090 All right, cool. 413 00:21:11,090 --> 00:21:13,610 So that's all we're effectively doing. 414 00:21:13,610 --> 00:21:17,180 We're just getting a camera scroll, decrementing it and incrementing it. 415 00:21:17,180 --> 00:21:19,970 And then just every frame, we're translating everything 416 00:21:19,970 --> 00:21:21,350 before we draw everything. 417 00:21:21,350 --> 00:21:25,010 You have to do the translation before you draw, because everything that you 418 00:21:25,010 --> 00:21:30,260 draw after the translation gets affected by the new coordinate system change. 419 00:21:30,260 --> 00:21:33,290 So that's scrolling. 420 00:21:33,290 --> 00:21:37,220 Let's get to actually talking about drawing a person-- an avatar-- 421 00:21:37,220 --> 00:21:46,280 more than just a set of tiles, since that's what the game revolves around. 422 00:21:46,280 --> 00:21:53,270 If we look at character0, this would be our first example here. 423 00:21:53,270 --> 00:21:57,350 This is just going to be a very simple example-- 424 00:21:57,350 --> 00:21:57,960 charactr0. 425 00:21:57,960 --> 00:22:00,800 426 00:22:00,800 --> 00:22:02,990 You guys probably know how this works already. 427 00:22:02,990 --> 00:22:07,120 All we're doing is just drawing a sprite to the screen-- 428 00:22:07,120 --> 00:22:09,680 so just love.graphics.draw. 429 00:22:09,680 --> 00:22:13,070 We're getting quads from a tile sheet. 430 00:22:13,070 --> 00:22:15,470 I believe it's in the slides. 431 00:22:15,470 --> 00:22:18,260 The actual sheet is here. 432 00:22:18,260 --> 00:22:21,680 So we have this little guy-- 433 00:22:21,680 --> 00:22:22,970 several frames of animation. 434 00:22:22,970 --> 00:22:26,080 It's 16 wide, 20 tall, and we just take a quad. 435 00:22:26,080 --> 00:22:27,420 We split it up into quads first. 436 00:22:27,420 --> 00:22:32,240 So we know that it's 16 wide by 20 tall, so we just generate quads 437 00:22:32,240 --> 00:22:35,690 on this image by 16 and 20. 438 00:22:35,690 --> 00:22:38,000 And then in this example, all we're doing 439 00:22:38,000 --> 00:22:42,796 is taking the first frame, which is quads1, and just drawing that. 440 00:22:42,796 --> 00:22:45,170 As you can see here, we have a bunch of different things. 441 00:22:45,170 --> 00:22:47,000 We have like a crouching state, and we'll 442 00:22:47,000 --> 00:22:50,100 get to more about animations in a little bit. 443 00:22:50,100 --> 00:22:53,160 But here, we have him climbing up a ladder. 444 00:22:53,160 --> 00:22:55,290 But you can see all these different frames. 445 00:22:55,290 --> 00:22:57,800 We'll end up showing how you can play them back to back 446 00:22:57,800 --> 00:22:59,430 and get different animations. 447 00:22:59,430 --> 00:23:01,970 But for the sake of this basic example, all we're doing 448 00:23:01,970 --> 00:23:05,360 is just rendering the very first frame. 449 00:23:05,360 --> 00:23:08,200 And we can see that-- 450 00:23:08,200 --> 00:23:11,240 let me make sure I'm in the right file, which I am-- 451 00:23:11,240 --> 00:23:17,790 we are getting the character sheet here on line 43 and 44. 452 00:23:17,790 --> 00:23:21,820 And then we have to give him an x, So characterX, characterY. 453 00:23:21,820 --> 00:23:24,360 In this case, we're just setting him above tile 7, 454 00:23:24,360 --> 00:23:29,840 so we do 7 minus 1 times TILE_SIZE because tiles are 1 indexed 455 00:23:29,840 --> 00:23:31,909 but coordinates are 0 indexed. 456 00:23:31,909 --> 00:23:33,950 And then we just subtract the height so that he's 457 00:23:33,950 --> 00:23:38,450 right above the tile instead of right at the tile. 458 00:23:38,450 --> 00:23:44,120 And then down here, we do a love.graphics.draw 459 00:23:44,120 --> 00:23:46,460 on, as I said before, just characterQuads 1. 460 00:23:46,460 --> 00:23:49,660 Just a very basic hard coded example. 461 00:23:49,660 --> 00:23:51,410 Any questions at all as to how this works? 462 00:23:51,410 --> 00:23:58,270 463 00:23:58,270 --> 00:24:00,514 So now let's say we want him to move. 464 00:24:00,514 --> 00:24:01,180 What do we need? 465 00:24:01,180 --> 00:24:03,304 What's the next step if we just wanted him to move? 466 00:24:03,304 --> 00:24:08,984 467 00:24:08,984 --> 00:24:11,970 AUDIENCE: Give him an x and y? 468 00:24:11,970 --> 00:24:13,890 COLTON OGDEN: Yes, give him an x and y. 469 00:24:13,890 --> 00:24:17,350 So yes, so he does have an x and y already. 470 00:24:17,350 --> 00:24:19,750 So if you look at-- am I in the right one? 471 00:24:19,750 --> 00:24:23,179 So if you go to character0, this is character0 still. 472 00:24:23,179 --> 00:24:26,220 We have given him an x and y already, but there needs to be another step. 473 00:24:26,220 --> 00:24:27,511 What's the other step involved? 474 00:24:27,511 --> 00:24:32,930 475 00:24:32,930 --> 00:24:36,620 So if we wanted to move, we need to check for keyboard input. 476 00:24:36,620 --> 00:24:39,320 And then we need to take his x-- 477 00:24:39,320 --> 00:24:42,140 we're just going to move him on the x-axis for now. 478 00:24:42,140 --> 00:24:47,390 We basically need to take his characterX variable up here, 479 00:24:47,390 --> 00:24:49,040 and we need to modify that. 480 00:24:49,040 --> 00:24:55,260 We can basically do the same thing that we did down here in love.update. 481 00:24:55,260 --> 00:24:58,040 Previously, it was on the coordinate system-- 482 00:24:58,040 --> 00:24:59,130 love.graphics.translate. 483 00:24:59,130 --> 00:25:01,220 We modified the camera scroll. 484 00:25:01,220 --> 00:25:05,900 We set that equal to scroll speed times delta time. 485 00:25:05,900 --> 00:25:07,130 We subtracted or added it. 486 00:25:07,130 --> 00:25:09,755 In this case, what we're doing is we have a new constant called 487 00:25:09,755 --> 00:25:13,250 CHARACTER_MOVE_SPEED, and we're just doing that exact same operation 488 00:25:13,250 --> 00:25:18,020 but on characterX instead of cameraScroll. 489 00:25:18,020 --> 00:25:24,900 So the end result of that is that we have the character here. 490 00:25:24,900 --> 00:25:28,287 And then we can move him left or right, and he go off screen. 491 00:25:28,287 --> 00:25:29,870 Now, there's a couple of things wrong. 492 00:25:29,870 --> 00:25:30,500 What's wrong? 493 00:25:30,500 --> 00:25:33,632 What are some of the things that are wrong with the scene right now? 494 00:25:33,632 --> 00:25:35,450 AUDIENCE: The camera should move with him. 495 00:25:35,450 --> 00:25:36,320 COLTON OGDEN: Camera should move with them. 496 00:25:36,320 --> 00:25:37,700 AUDIENCE: No animation. 497 00:25:37,700 --> 00:25:39,440 COLTON OGDEN: Does not have animation. 498 00:25:39,440 --> 00:25:41,690 Those are probably the two real things that are wrong. 499 00:25:41,690 --> 00:25:45,712 So camera does not track him, which is an important thing. 500 00:25:45,712 --> 00:25:48,670 Obviously, we want to be able to maintain a reference to our character, 501 00:25:48,670 --> 00:25:50,150 unless we're at the left edge of the screen. 502 00:25:50,150 --> 00:25:52,880 If we're at the left edge of the screen, this is actually OK. 503 00:25:52,880 --> 00:25:56,690 And that's part of the distro-- is we clamp the x so 504 00:25:56,690 --> 00:25:59,690 that it doesn't go past the left edge. 505 00:25:59,690 --> 00:26:05,210 But if we're beyond the middle and not to the right edge of the screen, 506 00:26:05,210 --> 00:26:08,910 it should be moving along with him and vice versa. 507 00:26:08,910 --> 00:26:11,030 And then he needs to animate, so his sprite 508 00:26:11,030 --> 00:26:15,050 needs to change every certain number of seconds whether he's moving. 509 00:26:15,050 --> 00:26:17,180 And it has to be only when he's moving, right? 510 00:26:17,180 --> 00:26:20,240 If he's standing still, you can have an idle animation. 511 00:26:20,240 --> 00:26:23,460 Some characters will tap their foot and do stuff like that. 512 00:26:23,460 --> 00:26:26,870 But let's say for the sake of this example we want him just 513 00:26:26,870 --> 00:26:28,790 to stand still when he's idle. 514 00:26:28,790 --> 00:26:31,940 And we want him to have an actual animation when he's moving. 515 00:26:31,940 --> 00:26:34,640 We need to take care of these two pieces-- 516 00:26:34,640 --> 00:26:38,310 three pieces if you count the idle animation part. 517 00:26:38,310 --> 00:26:43,310 So let's go into character2 and take care of the first part, which 518 00:26:43,310 --> 00:26:45,890 is tracking him. 519 00:26:45,890 --> 00:26:47,504 So let me go into character2. 520 00:26:47,504 --> 00:26:49,670 Let's run it first so we can see what it looks like. 521 00:26:49,670 --> 00:26:54,610 522 00:26:54,610 --> 00:26:58,680 So now the camera is basically affixed to the player. 523 00:26:58,680 --> 00:27:01,670 In this example, we don't take care of the left edge issue. 524 00:27:01,670 --> 00:27:03,910 In the distro, that's fixed. 525 00:27:03,910 --> 00:27:09,210 But we have the basic side scrolling mechanic-- take a character, 526 00:27:09,210 --> 00:27:11,610 follow him. 527 00:27:11,610 --> 00:27:13,980 How do you think that we're accomplishing this? 528 00:27:13,980 --> 00:27:17,520 529 00:27:17,520 --> 00:27:18,020 Yes. 530 00:27:18,020 --> 00:27:22,250 AUDIENCE: Translate the drawing against the characterX? 531 00:27:22,250 --> 00:27:24,700 COLTON OGDEN: Yes, exactly. 532 00:27:24,700 --> 00:27:28,190 And it can't be exactly the characterX though, 533 00:27:28,190 --> 00:27:31,760 because if it is, then the character is going to be on the left edge, right? 534 00:27:31,760 --> 00:27:35,290 So we need to offset our x that we translate by. 535 00:27:35,290 --> 00:27:40,820 We need to basically translate by his x minus half the screen space 536 00:27:40,820 --> 00:27:42,590 plus half the character width. 537 00:27:42,590 --> 00:27:45,340 And that will have the effect of translating it but always keeping 538 00:27:45,340 --> 00:27:48,530 that offset half a screen width away from the player, if that makes sense. 539 00:27:48,530 --> 00:27:52,450 And so what we're doing is in character-- this is character2, right-- 540 00:27:52,450 --> 00:27:58,040 character2, we're still doing the same thing we did. 541 00:27:58,040 --> 00:28:01,070 Actually, that's the wrong file. 542 00:28:01,070 --> 00:28:05,510 We are modifying characterX here. 543 00:28:05,510 --> 00:28:07,970 So same thing we did before-- 544 00:28:07,970 --> 00:28:10,920 multiply the move speed by delta time and either add or subtract it 545 00:28:10,920 --> 00:28:12,253 if we're pressing left or right. 546 00:28:12,253 --> 00:28:16,994 But also, here-- reintroducing camera scroll. 547 00:28:16,994 --> 00:28:20,160 And we're setting it to, like I said, characterX minus VIRTUAL_WIDTH divided 548 00:28:20,160 --> 00:28:23,450 by 2, half the screen, and then positive offset 549 00:28:23,450 --> 00:28:27,860 of his width divided by 2 so that he's perfectly right in the center. 550 00:28:27,860 --> 00:28:30,110 Because remember, characters' coordinates 551 00:28:30,110 --> 00:28:34,100 are set by their left, not their center. 552 00:28:34,100 --> 00:28:36,740 And then we just do what we did before. 553 00:28:36,740 --> 00:28:40,010 We translate the scene based on cameraScroll, 554 00:28:40,010 --> 00:28:44,720 and we render him at characterX characterY using math.floor 555 00:28:44,720 --> 00:28:48,430 to prevent him from being at a fractional point in our world space 556 00:28:48,430 --> 00:28:50,690 and then it being blurry and artifacted. 557 00:28:50,690 --> 00:28:55,487 And that's sort of it in terms of how we can get tracking over character. 558 00:28:55,487 --> 00:28:57,320 And if you wanted to track along the y-axis, 559 00:28:57,320 --> 00:28:58,760 you could do the exact same thing. 560 00:28:58,760 --> 00:29:02,690 Maintain a cameraScroll x and a cameraScroll y-- 561 00:29:02,690 --> 00:29:04,940 so keep them separated. 562 00:29:04,940 --> 00:29:09,260 And then you would just translate here. 563 00:29:09,260 --> 00:29:11,750 So we're passing in 0, because we don't want 564 00:29:11,750 --> 00:29:13,430 to track along the y-axis necessarily. 565 00:29:13,430 --> 00:29:18,140 But all you would need to do is pass in your y cameraScroll. 566 00:29:18,140 --> 00:29:20,579 And then you could do it based on characterY 567 00:29:20,579 --> 00:29:22,370 and whether or not they're above the ground 568 00:29:22,370 --> 00:29:25,310 or past a certain point in the sky. 569 00:29:25,310 --> 00:29:29,020 So any questions at all as to how the camera tracking is working here? 570 00:29:29,020 --> 00:29:31,790 571 00:29:31,790 --> 00:29:33,830 All right. 572 00:29:33,830 --> 00:29:38,332 So we took care of one issue, which was the lack of tracking. 573 00:29:38,332 --> 00:29:40,790 But there was one other issue, which was he's not animated. 574 00:29:40,790 --> 00:29:47,330 All he's doing is just moving sort of like M.C. Hammer-- 575 00:29:47,330 --> 00:29:48,451 or is it M.C. Usher? 576 00:29:48,451 --> 00:29:48,950 M.C. Hammer? 577 00:29:48,950 --> 00:29:50,330 I forget. 578 00:29:50,330 --> 00:29:51,140 He's doing that. 579 00:29:51,140 --> 00:29:52,220 He's not doing anything. 580 00:29:52,220 --> 00:29:55,640 We need to actually animate him so that he looks like he has some life to him 581 00:29:55,640 --> 00:29:57,680 and that you can also differentiate importantly 582 00:29:57,680 --> 00:29:59,390 between two separate states. 583 00:29:59,390 --> 00:30:02,630 He can be idle, he's not moving, and he can be moving. 584 00:30:02,630 --> 00:30:05,450 So we should have some sort of visual feedback 585 00:30:05,450 --> 00:30:08,960 as to what's currently going on. 586 00:30:08,960 --> 00:30:14,030 So anybody know how we can go about implementing 587 00:30:14,030 --> 00:30:16,360 an animation for our character? 588 00:30:16,360 --> 00:30:18,703 What are the pieces that we'll need? 589 00:30:18,703 --> 00:30:20,667 AUDIENCE: I guess if he's moving right, then 590 00:30:20,667 --> 00:30:25,570 call a function and a render that looks through some images? 591 00:30:25,570 --> 00:30:26,320 COLTON OGDEN: Yes. 592 00:30:26,320 --> 00:30:29,680 So if he's moving right, then have a function 593 00:30:29,680 --> 00:30:31,750 that sort of loops through some images. 594 00:30:31,750 --> 00:30:34,520 That is effectively what we will be doing. 595 00:30:34,520 --> 00:30:37,680 We have a class called Animation, which I've introduced here. 596 00:30:37,680 --> 00:30:40,750 And all it basically does is keep track of-- you 597 00:30:40,750 --> 00:30:44,140 pass it in a table, which has the frames of the sheet 598 00:30:44,140 --> 00:30:45,760 that you want to animate over. 599 00:30:45,760 --> 00:30:47,160 So we can just pass in-- 600 00:30:47,160 --> 00:30:48,660 let's go ahead and take a look here. 601 00:30:48,660 --> 00:30:50,700 602 00:30:50,700 --> 00:30:53,470 And I referenced the slide earlier, but all of these 603 00:30:53,470 --> 00:30:57,160 are 1, 2, 3, 4, 5, 6, 7, 8, 9, 10-- however many there are, 604 00:30:57,160 --> 00:31:01,296 you just pass into the animation. 605 00:31:01,296 --> 00:31:02,420 Let's say he's on a ladder. 606 00:31:02,420 --> 00:31:05,920 So let's say this is 1, 2, 3, 4, 5, 6, and 7. 607 00:31:05,920 --> 00:31:08,560 You say, the frames are going to be 6 and 7, 608 00:31:08,560 --> 00:31:12,070 so those will just loop left to right, starting back 609 00:31:12,070 --> 00:31:16,240 at the beginning when it's finished And then you give it an interval. 610 00:31:16,240 --> 00:31:19,520 So say I want the animation to happen this fast in terms of seconds, 611 00:31:19,520 --> 00:31:23,000 so I want it that maybe happened every 0.2 seconds. 612 00:31:23,000 --> 00:31:25,934 And so that will have the effect of every 0.2 seconds, 613 00:31:25,934 --> 00:31:27,100 it'll keep track of a timer. 614 00:31:27,100 --> 00:31:30,790 So have we gone over 0.2 seconds? 615 00:31:30,790 --> 00:31:33,700 Start at 0 and then add delta time to it every time. 616 00:31:33,700 --> 00:31:37,750 If we have, increment what our current frame of animation is. 617 00:31:37,750 --> 00:31:42,280 So our current frame is this one, and then 0.2 seconds elapses, 618 00:31:42,280 --> 00:31:43,480 it's going to be this one. 619 00:31:43,480 --> 00:31:47,000 And then 0.2 second elapses, and we need to loop back to the beginning. 620 00:31:47,000 --> 00:31:49,150 So we'll end up using modulus to take care of that 621 00:31:49,150 --> 00:31:51,070 as we can see in the Animation class. 622 00:31:51,070 --> 00:31:54,520 623 00:31:54,520 --> 00:31:55,930 Basically, that's all done here. 624 00:31:55,930 --> 00:32:00,340 So if we have more than one frame of animation, recall it gets a def here. 625 00:32:00,340 --> 00:32:04,112 So we get frames, we get an interval, get a timer that's initialized to 0, 626 00:32:04,112 --> 00:32:05,320 and then get a current frame. 627 00:32:05,320 --> 00:32:09,070 We'll say the current frame is 1. 628 00:32:09,070 --> 00:32:12,322 And then as long as we have more than one frame, 629 00:32:12,322 --> 00:32:14,530 there's no point in looping over or trying to animate 630 00:32:14,530 --> 00:32:16,930 any animation that only has one frame. 631 00:32:16,930 --> 00:32:20,140 And we can, of course, have animations that only have one frame. 632 00:32:20,140 --> 00:32:23,500 Idle is only one frame of animation, as we saw here. 633 00:32:23,500 --> 00:32:24,959 That's only one frame. 634 00:32:24,959 --> 00:32:27,000 We don't need to do any sort of logic to say, oh, 635 00:32:27,000 --> 00:32:29,560 what's the next frame, because there's only one frame. 636 00:32:29,560 --> 00:32:37,330 But if we were to look at character3, we can see two frames there. 637 00:32:37,330 --> 00:32:39,760 And then that's just one, frame he's idle. 638 00:32:39,760 --> 00:32:42,860 And when we move left, he moves in that direction. 639 00:32:42,860 --> 00:32:45,822 Anybody recall how we can get him-- because obviously, 640 00:32:45,822 --> 00:32:47,530 we saw the spreadsheet just a second ago, 641 00:32:47,530 --> 00:32:50,290 and there was only one direction that the sprites were facing-- 642 00:32:50,290 --> 00:32:54,070 how we can get him to look that way, even though there's 643 00:32:54,070 --> 00:32:57,323 no sprites for him to look that way? 644 00:32:57,323 --> 00:32:59,170 AUDIENCE: Flip it on the axis. 645 00:32:59,170 --> 00:33:03,190 COLTON OGDEN: Flip it, so love.graphics.draw. 646 00:33:03,190 --> 00:33:07,900 Recall you can pass in a negative scale factor on whatever axis you want, 647 00:33:07,900 --> 00:33:10,912 and that'll have the result of flipping it along that axis. 648 00:33:10,912 --> 00:33:11,870 That's all we're doing. 649 00:33:11,870 --> 00:33:14,161 So this is the default frame, so we're just drawing it. 650 00:33:14,161 --> 00:33:17,300 And then we have to keep a reference to whatever direction he's facing. 651 00:33:17,300 --> 00:33:19,390 And if his direction is equal to right, we'll 652 00:33:19,390 --> 00:33:23,300 just draw that frame and then loop and process the animation. 653 00:33:23,300 --> 00:33:29,380 If he's facing left, draw it, but also perform a negative 1 transformation 654 00:33:29,380 --> 00:33:30,820 on the x-axis. 655 00:33:30,820 --> 00:33:33,670 And just like that, we have that working. 656 00:33:33,670 --> 00:33:34,600 So all we're doing-- 657 00:33:34,600 --> 00:33:36,070 just keep a timer. 658 00:33:36,070 --> 00:33:41,050 And then when the timer goes over our interval, just increment the frame. 659 00:33:41,050 --> 00:33:44,140 And then use modulus to loop back over it-- 660 00:33:44,140 --> 00:33:45,480 back to starting at 1. 661 00:33:45,480 --> 00:33:48,691 And that's all done here on this line 28. 662 00:33:48,691 --> 00:33:50,440 And so you can look in there a little more 663 00:33:50,440 --> 00:33:53,320 if you want to get a handle on how the math works, 664 00:33:53,320 --> 00:33:58,300 but it is just a simple sequence of iterating over a collection of frames 665 00:33:58,300 --> 00:33:59,640 based on a timer. 666 00:33:59,640 --> 00:34:02,830 And that has the effect-- just like a flip book, as I said earlier-- 667 00:34:02,830 --> 00:34:06,160 of our character having an animation and having some life. 668 00:34:06,160 --> 00:34:10,060 So any questions as to how this animation class works? 669 00:34:10,060 --> 00:34:12,437 AUDIENCE: The render is in the Animate class? 670 00:34:12,437 --> 00:34:13,270 COLTON OGDEN: So no. 671 00:34:13,270 --> 00:34:14,894 The render is not in the animate class. 672 00:34:14,894 --> 00:34:16,780 So the render is-- 673 00:34:16,780 --> 00:34:20,060 I realize I didn't show any actual main here. 674 00:34:20,060 --> 00:34:22,830 We have two animations here, which was just the idle one, 675 00:34:22,830 --> 00:34:24,550 so we're just passing in one frame. 676 00:34:24,550 --> 00:34:26,794 We're going to give it an interval of 1. 677 00:34:26,794 --> 00:34:29,710 It's not going to really matter, but just for the sake of consistency, 678 00:34:29,710 --> 00:34:31,750 we're giving it an interval of 1-- arbitrary. 679 00:34:31,750 --> 00:34:35,090 And maybe we want to change his animation later. 680 00:34:35,090 --> 00:34:39,100 So by having an interval here, we won't forget to add one later. 681 00:34:39,100 --> 00:34:41,409 Moving animation-- recall 10 and 11. 682 00:34:41,409 --> 00:34:43,130 So it's toward the end of the sheet-- 683 00:34:43,130 --> 00:34:46,090 the two walking frames. 684 00:34:46,090 --> 00:34:48,520 Interval here is 0.2 seconds. 685 00:34:48,520 --> 00:34:51,969 We need a current animation to render him, 686 00:34:51,969 --> 00:34:55,909 and then we keep a reference to whatever direction he's looking at. 687 00:34:55,909 --> 00:34:58,570 So if he's looking to the right, we're going 688 00:34:58,570 --> 00:35:02,282 to reference this in the love.graphics.draw at the bottom. 689 00:35:02,282 --> 00:35:04,990 And that's what we're going to use to perform the sprite flipping 690 00:35:04,990 --> 00:35:07,560 along the x-axis. 691 00:35:07,560 --> 00:35:09,500 Maintain a reference to that. 692 00:35:09,500 --> 00:35:14,500 And then down here, the part that we actually reference the animation 693 00:35:14,500 --> 00:35:18,670 is on line 150 if you're looking at character1. 694 00:35:18,670 --> 00:35:20,870 Or is it character2? 695 00:35:20,870 --> 00:35:22,410 Sorry, character3. 696 00:35:22,410 --> 00:35:26,130 If you're looking at line 150 in character3, 697 00:35:26,130 --> 00:35:29,307 we're using currentAnimation:getCurrentFrame(). 698 00:35:29,307 --> 00:35:32,640 So the class will actually just tell you whatever the current frame of animation 699 00:35:32,640 --> 00:35:37,410 is, because it keeps a reference to what frame it is based on the timer 700 00:35:37,410 --> 00:35:39,287 and how much has elapsed. 701 00:35:39,287 --> 00:35:43,760 AUDIENCE: So the class is generating a different frame real time 702 00:35:43,760 --> 00:35:45,019 and plugging it in there. 703 00:35:45,019 --> 00:35:45,810 COLTON OGDEN: Yeah. 704 00:35:45,810 --> 00:35:49,420 It's maintaining a reference to whatever the current frame is, 705 00:35:49,420 --> 00:35:51,990 and it's in the table of frames that it got when you gave it 706 00:35:51,990 --> 00:35:56,000 the definition up at the top here-- 707 00:35:56,000 --> 00:35:59,880 lines 51 to 58 where we create the two animations. 708 00:35:59,880 --> 00:36:03,870 Basically, it maintains a reference to which index in this frame table 709 00:36:03,870 --> 00:36:04,930 we're at. 710 00:36:04,930 --> 00:36:10,180 So if 0.2 seconds has elapsed, we start at 1, and then we go to 2. 711 00:36:10,180 --> 00:36:11,520 And then we'll go back to 1. 712 00:36:11,520 --> 00:36:15,970 And so it'll just basically return frames, index. 713 00:36:15,970 --> 00:36:20,720 And frames index 1 is 10, frames index 2 is 11. 714 00:36:20,720 --> 00:36:25,480 And so the function is getCurrentFrame. 715 00:36:25,480 --> 00:36:29,730 So characterQuads, currentAnimation, getCurrentFrame. 716 00:36:29,730 --> 00:36:37,410 And then here, because we're performing an origin transformation-- 717 00:36:37,410 --> 00:36:43,260 so that's another thing to consider when you're flipping sprites. 718 00:36:43,260 --> 00:36:45,480 When you flip a sprite, it actually flips along 719 00:36:45,480 --> 00:36:47,490 whatever its default origin is. 720 00:36:47,490 --> 00:36:53,130 And the default origin of any sprite is its top left corner here. 721 00:36:53,130 --> 00:36:56,880 So if you flip something along its x-axis, 722 00:36:56,880 --> 00:37:00,630 it'll appear here instead of just flipping in place. 723 00:37:00,630 --> 00:37:03,870 So you actually have to set the origin to its center 724 00:37:03,870 --> 00:37:07,090 when you do any sort of in place flipping of a sprite. 725 00:37:07,090 --> 00:37:10,290 So you'll notice in the code when you're looking 726 00:37:10,290 --> 00:37:16,980 at it that we have plus CHARACTER_WIDTH divided by 2 and plus CHARACTER_HEIGHT 727 00:37:16,980 --> 00:37:21,000 divided by 2 on these two here. 728 00:37:21,000 --> 00:37:24,240 So we shift where it gets drawn, and then we shift its origin 729 00:37:24,240 --> 00:37:26,610 offsets which are here on line 160. 730 00:37:26,610 --> 00:37:28,920 So if you look at love.graphics.draw, you'll 731 00:37:28,920 --> 00:37:31,770 see it has a lot of optional arguments. 732 00:37:31,770 --> 00:37:36,960 And these two at the bottom are the origin offset arguments. 733 00:37:36,960 --> 00:37:39,840 And so these only really come into play when 734 00:37:39,840 --> 00:37:44,970 you do some kind of flipping of a sprite on an axis 735 00:37:44,970 --> 00:37:50,560 and you want graphical consistency not to have it flip one way or the other. 736 00:37:50,560 --> 00:37:53,381 Sometimes that's the effect you're looking for, but in this case, 737 00:37:53,381 --> 00:37:53,880 it's not. 738 00:37:53,880 --> 00:37:56,360 We want him to literally stay in the exact same place. 739 00:37:56,360 --> 00:37:58,334 So to flip a sprite in the exact same place, 740 00:37:58,334 --> 00:38:00,750 you need a set its origin to its center, not its top left. 741 00:38:00,750 --> 00:38:04,090 Does that makes sense? 742 00:38:04,090 --> 00:38:05,130 OK. 743 00:38:05,130 --> 00:38:08,940 And also here, 0 is the rotation here. 744 00:38:08,940 --> 00:38:11,140 So it's sort of required if you're going to add 745 00:38:11,140 --> 00:38:13,870 this many arguments to the function. 746 00:38:13,870 --> 00:38:18,540 But we're testing if direction is equal to left, we want to flip by negative 1 747 00:38:18,540 --> 00:38:22,920 on the x, else just give it 1. 748 00:38:22,920 --> 00:38:26,880 So 1 just means default transformation, so no flipping. 749 00:38:26,880 --> 00:38:30,360 And then we don't flip on the y at all, so that will always be 1. 750 00:38:30,360 --> 00:38:34,950 And so that's in a nutshell how you can get your character to animate and also 751 00:38:34,950 --> 00:38:37,560 stay in place when you animate it. 752 00:38:37,560 --> 00:38:43,810 So any questions as to how animations or the origin offsets or any of that work? 753 00:38:43,810 --> 00:38:47,660 754 00:38:47,660 --> 00:38:48,230 OK. 755 00:38:48,230 --> 00:38:50,500 So we did talk about animations. 756 00:38:50,500 --> 00:38:53,450 The last thing we'll talk about for the character is jumping. 757 00:38:53,450 --> 00:38:58,240 So if you recall from Flappy Bird, how can we get our character to jump? 758 00:38:58,240 --> 00:39:02,300 What are some of the pieces we need? 759 00:39:02,300 --> 00:39:07,050 AUDIENCE: Key press, and then the y goes up. 760 00:39:07,050 --> 00:39:08,490 And then we have to have gravity. 761 00:39:08,490 --> 00:39:09,360 COLTON OGDEN: Yep. 762 00:39:09,360 --> 00:39:14,140 So key press is one thing we need, so check for space 763 00:39:14,140 --> 00:39:16,020 is going to be the default key. 764 00:39:16,020 --> 00:39:19,240 y goes up, and then check for gravity. 765 00:39:19,240 --> 00:39:22,860 So not only do we need y, but we also need delta y. 766 00:39:22,860 --> 00:39:29,340 We need velocity, because gravity is a transformation on velocity, not 767 00:39:29,340 --> 00:39:31,780 strictly on position. 768 00:39:31,780 --> 00:39:39,540 So if we go back to character4, this is sort 769 00:39:39,540 --> 00:39:41,970 of a hackish way of implementing gravity, 770 00:39:41,970 --> 00:39:44,700 because we haven't actually incorporated tile collisions. 771 00:39:44,700 --> 00:39:47,830 And I'll defer most of the implementation for that 772 00:39:47,830 --> 00:39:51,060 as to the distro, and I'll go over with you guys. 773 00:39:51,060 --> 00:39:53,700 But right now, we have the exact same thing we had before, 774 00:39:53,700 --> 00:39:55,460 where we have tile scrolling. 775 00:39:55,460 --> 00:39:58,500 But if I press space bar, I go up, and then he comes down. 776 00:39:58,500 --> 00:40:01,300 And notice that he has an animation as well. 777 00:40:01,300 --> 00:40:02,410 He has a different frame. 778 00:40:02,410 --> 00:40:05,469 So if he's jumping, he's got a little jump frame. 779 00:40:05,469 --> 00:40:07,260 So that means now we have three animations. 780 00:40:07,260 --> 00:40:09,834 We have an idle animation, we have a moving animation, 781 00:40:09,834 --> 00:40:11,250 and then we have a jump animation. 782 00:40:11,250 --> 00:40:14,250 So effectively, we have three states as well-- 783 00:40:14,250 --> 00:40:18,090 idle state, moving state, and jumping state. 784 00:40:18,090 --> 00:40:20,220 Four states, actually. 785 00:40:20,220 --> 00:40:23,490 And also, I noticed a slight bug here where if you're still in the air, 786 00:40:23,490 --> 00:40:25,307 his frame doesn't change. 787 00:40:25,307 --> 00:40:27,390 So it actually probably should stay to that frame, 788 00:40:27,390 --> 00:40:28,830 even if he's standing still. 789 00:40:28,830 --> 00:40:30,700 But I guess it doesn't matter too much. 790 00:40:30,700 --> 00:40:34,050 We also interpret it as a feature. 791 00:40:34,050 --> 00:40:40,520 But he's got a couple of states when he's in the air. 792 00:40:40,520 --> 00:40:42,480 There should be two states here. 793 00:40:42,480 --> 00:40:44,730 One is jumping state, and one is falling state. 794 00:40:44,730 --> 00:40:48,540 And do we know why the two being different is an important thing? 795 00:40:48,540 --> 00:40:52,780 796 00:40:52,780 --> 00:40:56,070 So if we think about Super Mario Bros. 797 00:40:56,070 --> 00:41:00,970 and we think about the differences between jumping and falling, 798 00:41:00,970 --> 00:41:04,260 what are some of the things that change based on whether Mario is jumping 799 00:41:04,260 --> 00:41:07,080 or whether he's falling? 800 00:41:07,080 --> 00:41:10,520 How does he interact differently with the environment, I should say? 801 00:41:10,520 --> 00:41:15,050 802 00:41:15,050 --> 00:41:21,480 So if unfamiliar, Mario-- when he jumps, he can actually hit blocks. 803 00:41:21,480 --> 00:41:23,570 So if he's below a block and he hits a block that 804 00:41:23,570 --> 00:41:27,120 has some sort of behavior in it, it will trigger whatever is in that block, 805 00:41:27,120 --> 00:41:29,960 whether it's a coin or whether it's to destroy the block. 806 00:41:29,960 --> 00:41:34,790 And if he's falling, recall if he lands on top of an enemy like a goomba, 807 00:41:34,790 --> 00:41:36,450 he'll destroy the enemy. 808 00:41:36,450 --> 00:41:39,290 And so we need to distinguish between these two states. 809 00:41:39,290 --> 00:41:43,310 Because when he's jumping, he's not able to-- 810 00:41:43,310 --> 00:41:46,730 when he's actually going up, he can't attack the enemy. 811 00:41:46,730 --> 00:41:49,580 And likewise, when he's falling down, he can't destroy the block. 812 00:41:49,580 --> 00:41:52,550 So even though he's jumping up in the air 813 00:41:52,550 --> 00:41:55,460 and the gravity is applying a transformation 814 00:41:55,460 --> 00:42:00,590 and it all looks like one state, there's actually two important changes 815 00:42:00,590 --> 00:42:02,250 in his state that are relevant. 816 00:42:02,250 --> 00:42:04,740 And that's something that we'll need to pay attention to, 817 00:42:04,740 --> 00:42:05,698 and it's in the distro. 818 00:42:05,698 --> 00:42:09,920 He has a falling state and a jumping state. 819 00:42:09,920 --> 00:42:13,160 Even though they share the same animation, 820 00:42:13,160 --> 00:42:15,690 they have different behavior. 821 00:42:15,690 --> 00:42:24,180 So let's go ahead and look at the character4 distro here. 822 00:42:24,180 --> 00:42:28,130 So what I've done here is I've added a delta y for the character. 823 00:42:28,130 --> 00:42:31,340 So just like in Flappy Bird when we press space 824 00:42:31,340 --> 00:42:34,710 and we made our delta y go up to negative 50-- 825 00:42:34,710 --> 00:42:37,370 so instantly shot up pretty high, because that was getting 826 00:42:37,370 --> 00:42:38,870 applied every frame. 827 00:42:38,870 --> 00:42:41,150 Same thing here. 828 00:42:41,150 --> 00:42:45,560 Once we press space, we're going to change delta y to negative 50 829 00:42:45,560 --> 00:42:53,100 if we go down to right here. 830 00:42:53,100 --> 00:42:56,990 So if the key is equal to space, I have it in the love.keyPressed function. 831 00:42:56,990 --> 00:42:59,810 Since we're doing all this in main.lua just for illustration, 832 00:42:59,810 --> 00:43:02,890 things are a little simple. 833 00:43:02,890 --> 00:43:05,720 If key is equal to space and his delta y is 834 00:43:05,720 --> 00:43:07,670 equal to 0, what would happen if we didn't 835 00:43:07,670 --> 00:43:09,775 check to see if delta y was equal to 0? 836 00:43:09,775 --> 00:43:11,475 AUDIENCE: We double jump in the air. 837 00:43:11,475 --> 00:43:12,225 COLTON OGDEN: Yep. 838 00:43:12,225 --> 00:43:15,470 We'd be able to jump infinitely, so we have to do a check for that. 839 00:43:15,470 --> 00:43:17,330 We set his dy to JUMP_VELOCITY. 840 00:43:17,330 --> 00:43:26,660 JUMP_VELOCITY is a constant up top on line 29, which is negative 200. 841 00:43:26,660 --> 00:43:29,060 And then gravity is equal to 7. 842 00:43:29,060 --> 00:43:34,220 And so what we do is we set it to negative 200-- his delta y-- 843 00:43:34,220 --> 00:43:35,600 as soon as he jumps. 844 00:43:35,600 --> 00:43:40,850 And then every frame down in update, we basically 845 00:43:40,850 --> 00:43:43,580 increment his delta y by gravity. 846 00:43:43,580 --> 00:43:50,634 And then we increment his y by delta y times delta time. 847 00:43:50,634 --> 00:43:52,800 And so it'll have the effect of when he's in the air 848 00:43:52,800 --> 00:43:57,950 and he's got a negative velocity, it'll actually 849 00:43:57,950 --> 00:44:00,530 start becoming positive and positive until it is positive, 850 00:44:00,530 --> 00:44:03,350 and then he falls back to the ground. 851 00:44:03,350 --> 00:44:05,810 And then the hack that I was referring to earlier-- 852 00:44:05,810 --> 00:44:10,490 since we don't have collision detection implemented in this example yet-- 853 00:44:10,490 --> 00:44:13,310 is we're just basically checking to see whether he has 854 00:44:13,310 --> 00:44:17,180 gone below what we set the map's floor. 855 00:44:17,180 --> 00:44:22,280 And if he has, then set his position, first of all, to be above that tile 856 00:44:22,280 --> 00:44:23,780 here on line 133. 857 00:44:23,780 --> 00:44:25,722 And then set his delta y equal to 0. 858 00:44:25,722 --> 00:44:27,680 And that will allow us then to hit space again, 859 00:44:27,680 --> 00:44:29,462 because his delta y will be equal to 0. 860 00:44:29,462 --> 00:44:32,696 AUDIENCE: So I didn't see [INAUDIBLE] on it. 861 00:44:32,696 --> 00:44:34,550 Looks like there's always gravity, then. 862 00:44:34,550 --> 00:44:35,810 COLTON OGDEN: There is always gravity, something 863 00:44:35,810 --> 00:44:37,570 I realized shortly before lecture. 864 00:44:37,570 --> 00:44:44,540 But all you would really have to do is, I think, if character dy-- 865 00:44:44,540 --> 00:44:45,230 Yeah. 866 00:44:45,230 --> 00:44:48,271 You could easily take that out of there-- just an if statement around it. 867 00:44:48,271 --> 00:44:50,272 AUDIENCE: It's just a waste of resources, right? 868 00:44:50,272 --> 00:44:51,104 COLTON OGDEN: It is. 869 00:44:51,104 --> 00:44:53,300 I mean, it's not expensive, because all you're doing 870 00:44:53,300 --> 00:44:57,075 is incrementing a variable by a certain amount. 871 00:44:57,075 --> 00:44:59,700 If anything, if you're introducing an if condition every frame, 872 00:44:59,700 --> 00:45:01,910 which is probably the same if not actually more. 873 00:45:01,910 --> 00:45:06,030 I think a branch is more CPU than just an assignment. 874 00:45:06,030 --> 00:45:09,004 I'm not entirely sure about that. 875 00:45:09,004 --> 00:45:09,940 AUDIENCE: Interesting. 876 00:45:09,940 --> 00:45:10,731 COLTON OGDEN: Yeah. 877 00:45:10,731 --> 00:45:13,860 In this case, it doesn't really have any side effects. 878 00:45:13,860 --> 00:45:16,410 But it's a good thing to notice. 879 00:45:16,410 --> 00:45:20,265 But now notice that we can just sort of walk along the floor here, 880 00:45:20,265 --> 00:45:21,890 because there's no collision detection. 881 00:45:21,890 --> 00:45:27,320 We'll talk about how we implement a collision detection soon. 882 00:45:27,320 --> 00:45:31,430 So one thing that we'll start talking on-- and we'll take a break fairly 883 00:45:31,430 --> 00:45:32,660 soon-- 884 00:45:32,660 --> 00:45:34,250 is procedural level generation. 885 00:45:34,250 --> 00:45:36,350 So I am a big fan of procedural level generation, 886 00:45:36,350 --> 00:45:39,470 and platformer levels are actually fairly easy-- 887 00:45:39,470 --> 00:45:40,790 at least in a simple sense-- 888 00:45:40,790 --> 00:45:43,170 to procedurally generate. 889 00:45:43,170 --> 00:45:47,060 And so like with match three, all we basically 890 00:45:47,060 --> 00:45:48,890 did was just loop through our grid and just 891 00:45:48,890 --> 00:45:52,580 say, oh, get a random color and a random variety. 892 00:45:52,580 --> 00:45:56,306 And then with the assignment, it was a little bit more complicated, 893 00:45:56,306 --> 00:45:59,180 where you actually had to check to see whether you were on level one. 894 00:45:59,180 --> 00:46:01,040 And then if you weren't, then your variety 895 00:46:01,040 --> 00:46:05,422 should be maybe a certain amount depending on how far along you've 896 00:46:05,422 --> 00:46:06,380 progressed in the game. 897 00:46:06,380 --> 00:46:09,710 898 00:46:09,710 --> 00:46:13,970 With a platformer level, we have to think 899 00:46:13,970 --> 00:46:21,800 about how we can take that grid of tile IDs and think about it mathematically. 900 00:46:21,800 --> 00:46:26,870 How can we get the results of a level, but make it different every time-- 901 00:46:26,870 --> 00:46:30,110 introduce some variation, right? 902 00:46:30,110 --> 00:46:33,710 And so the solution that I found that makes the most sense 903 00:46:33,710 --> 00:46:37,500 is going column by column. 904 00:46:37,500 --> 00:46:41,880 So here, we just have a bunch of-- this is just 905 00:46:41,880 --> 00:46:45,680 a very simple perfect screenshot to illustrate a very simple way 906 00:46:45,680 --> 00:46:47,030 of generating the level. 907 00:46:47,030 --> 00:46:53,180 But recall, if we just think about these tiles here-- these empty spaces-- 908 00:46:53,180 --> 00:46:59,720 being a 0 and these being a 1, it's sort of almost like binary in this case. 909 00:46:59,720 --> 00:47:05,630 We could just fill the entire thing with 0 first, just assume empty space. 910 00:47:05,630 --> 00:47:10,040 And then we could just column by column go down and just have a chance 911 00:47:10,040 --> 00:47:10,730 every column. 912 00:47:10,730 --> 00:47:12,740 OK, do I want to generate a ground here? 913 00:47:12,740 --> 00:47:15,830 If I do, start at the ground level and then just 914 00:47:15,830 --> 00:47:18,470 generate earth tiles all the way down. 915 00:47:18,470 --> 00:47:22,850 And then go to the next x position, do the same thing, do the same thing. 916 00:47:22,850 --> 00:47:26,420 And then maybe every column of the world that you're generating, 917 00:47:26,420 --> 00:47:29,540 you also have a chance to generate a pillar like this. 918 00:47:29,540 --> 00:47:35,300 So if generate pillar is true, then I want to spawn-- 919 00:47:35,300 --> 00:47:38,720 instead of starting the ground here, I want to start it here. 920 00:47:38,720 --> 00:47:41,020 And then maybe you have a flag that says, 921 00:47:41,020 --> 00:47:43,140 OK, not only do I want to generate pillars, 922 00:47:43,140 --> 00:47:45,590 I also want to generate chasms-- 923 00:47:45,590 --> 00:47:47,874 just empty space, obstacles for the player. 924 00:47:47,874 --> 00:47:50,290 Because if he falls down-- it goes below the world space-- 925 00:47:50,290 --> 00:47:51,870 it should be game over. 926 00:47:51,870 --> 00:47:55,400 So in that case, you just say if generate chasm-- 927 00:47:55,400 --> 00:47:59,270 make math.random 10 or whatever it is-- 928 00:47:59,270 --> 00:48:01,160 then just go to the next x. 929 00:48:01,160 --> 00:48:02,340 Don't even do anything. 930 00:48:02,340 --> 00:48:04,670 And that will have the result of generating a chasm. 931 00:48:04,670 --> 00:48:07,250 And so little piece by piece-- 932 00:48:07,250 --> 00:48:10,010 doing small things like that has the net effect 933 00:48:10,010 --> 00:48:14,810 of generating a lot of visually interesting, dynamic, 934 00:48:14,810 --> 00:48:15,691 and random levels. 935 00:48:15,691 --> 00:48:16,940 You never know what to expect. 936 00:48:16,940 --> 00:48:19,290 And this is a very basic example. 937 00:48:19,290 --> 00:48:21,780 You could go infinitely far with it. 938 00:48:21,780 --> 00:48:25,520 However many ideas you have in terms of how 939 00:48:25,520 --> 00:48:28,610 to create obstacles and interesting levels and scenery for the player-- 940 00:48:28,610 --> 00:48:30,758 you could absolutely implement that. 941 00:48:30,758 --> 00:48:35,159 AUDIENCE: How do you handle if there's a platform to jump on? 942 00:48:35,159 --> 00:48:37,239 You have to have that consistency. 943 00:48:37,239 --> 00:48:38,030 COLTON OGDEN: Yeah. 944 00:48:38,030 --> 00:48:41,750 So if it's a platform, it depends on how you want to implement platforms. 945 00:48:41,750 --> 00:48:45,090 946 00:48:45,090 --> 00:48:47,870 And actually, I did a seminar on Super Mario Bros., 947 00:48:47,870 --> 00:48:50,840 and we did platforms as tiles. 948 00:48:50,840 --> 00:48:53,980 In this case, we'll have blocks that are actually 949 00:48:53,980 --> 00:48:55,940 what we've denoted as game objects-- which 950 00:48:55,940 --> 00:48:58,010 are a little bit different than tiles. 951 00:48:58,010 --> 00:49:00,950 Because they can have arbitrary sizes, and they don't necessarily 952 00:49:00,950 --> 00:49:02,990 have to be affixed to the world grid. 953 00:49:02,990 --> 00:49:08,960 But if you were to treat a platform that was, let's say, 954 00:49:08,960 --> 00:49:14,630 two tiles wide as tiles, all you would do 955 00:49:14,630 --> 00:49:19,250 is just basically have a flag up here that's like, generate platform 956 00:49:19,250 --> 00:49:20,987 equals true or whatever-- 957 00:49:20,987 --> 00:49:22,570 AUDIENCE: And then turn it off after-- 958 00:49:22,570 --> 00:49:24,170 COLTON OGDEN: Turn it off after however many iterations. 959 00:49:24,170 --> 00:49:25,610 You also need the size of it. 960 00:49:25,610 --> 00:49:29,834 You'll need a flag that's like platform width equals however many, 961 00:49:29,834 --> 00:49:31,250 and so you'll just keep a counter. 962 00:49:31,250 --> 00:49:35,960 It's like current platform tile equals 1, 2, 3. 963 00:49:35,960 --> 00:49:39,260 And if it's equal to width, then you don't generate it any more. 964 00:49:39,260 --> 00:49:43,560 And that has the effect of potentially colliding with pillars 965 00:49:43,560 --> 00:49:44,810 if you don't account for that. 966 00:49:44,810 --> 00:49:49,760 So you can also in your logic say, if I'm generating a platform right now, 967 00:49:49,760 --> 00:49:50,930 don't generate a pillar. 968 00:49:50,930 --> 00:49:52,971 But you could generate a chasm, because the chasm 969 00:49:52,971 --> 00:49:56,330 doesn't interfere with your platform. 970 00:49:56,330 --> 00:50:01,160 971 00:50:01,160 --> 00:50:04,170 If you don't have platforms as tiles-- if they're different objects-- 972 00:50:04,170 --> 00:50:07,370 then you don't have to do it during the actual world generation phase. 973 00:50:07,370 --> 00:50:08,660 You can just test. 974 00:50:08,660 --> 00:50:12,020 You can just create a game object that's a platform. 975 00:50:12,020 --> 00:50:14,417 Depending on how complicated your algorithm is, 976 00:50:14,417 --> 00:50:17,250 maybe make sure that it's not next to a pillar when you generate it. 977 00:50:17,250 --> 00:50:20,900 And you could just do that by getting the tile here and then looking 978 00:50:20,900 --> 00:50:22,640 at the next four tiles-- 979 00:50:22,640 --> 00:50:23,810 something like that. 980 00:50:23,810 --> 00:50:29,690 We don't do platforms in this example, but it's something 981 00:50:29,690 --> 00:50:31,910 that you could pretty easily do with tiles. 982 00:50:31,910 --> 00:50:36,470 And slightly more difficult but also still fairly easy to 983 00:50:36,470 --> 00:50:40,460 do with game objects, which is included in the distro 984 00:50:40,460 --> 00:50:42,680 and which we'll touch on in a little bit. 985 00:50:42,680 --> 00:50:45,802 986 00:50:45,802 --> 00:50:49,450 Let's see, we're at level-- 987 00:50:49,450 --> 00:50:51,950 oh, another couple of things that I wanted to show before we 988 00:50:51,950 --> 00:50:55,730 actually start getting into the code for how to generate levels. 989 00:50:55,730 --> 00:51:00,275 This is the sprite sheet for this whole project, which is a really cool sprite 990 00:51:00,275 --> 00:51:02,460 sheet that I found online. 991 00:51:02,460 --> 00:51:04,944 It's in the spirit of platformers like Mario, 992 00:51:04,944 --> 00:51:07,110 and it's got a nice little mockup here on the right. 993 00:51:07,110 --> 00:51:08,930 So I encourage you to take a look at that 994 00:51:08,930 --> 00:51:12,530 and just maybe get some inspiration and see all the different cool stuff. 995 00:51:12,530 --> 00:51:14,896 Tinker around with it if you want to. 996 00:51:14,896 --> 00:51:17,770 But as you can see here, there's a couple of pretty prominent things. 997 00:51:17,770 --> 00:51:19,580 We have a ton of tiles. 998 00:51:19,580 --> 00:51:20,830 These are all tiles here-- 999 00:51:20,830 --> 00:51:22,720 different tiles and variations. 1000 00:51:22,720 --> 00:51:25,240 And then we have a ton of these toppers here. 1001 00:51:25,240 --> 00:51:32,500 And so what really helps this whole demonstration 1002 00:51:32,500 --> 00:51:35,140 of generating these levels is the fact that we have 1003 00:51:35,140 --> 00:51:37,750 so much visual content to work with. 1004 00:51:37,750 --> 00:51:40,930 And so here, again, are the tiles. 1005 00:51:40,930 --> 00:51:42,130 Here are the toppers. 1006 00:51:42,130 --> 00:51:46,360 And then when you take the two together and then you also 1007 00:51:46,360 --> 00:51:49,840 have these random backgrounds-- 1008 00:51:49,840 --> 00:51:52,300 these are toppers here, the top of the tiles here. 1009 00:51:52,300 --> 00:51:57,340 It's incredibly easy to just have a sheer abundance of visual variety 1010 00:51:57,340 --> 00:52:00,240 and interesting things in your game levels without even-- 1011 00:52:00,240 --> 00:52:02,230 and the algorithms here are very simple. 1012 00:52:02,230 --> 00:52:06,160 All we're doing is just checking to generate pillars and columns. 1013 00:52:06,160 --> 00:52:07,420 I know. 1014 00:52:07,420 --> 00:52:13,060 I thought it was really cool and helps illustrate the importance and power 1015 00:52:13,060 --> 00:52:15,934 of this whole procedural approach to creating the levels for this. 1016 00:52:15,934 --> 00:52:18,850 And there's actually not that many games that take advantage, I think, 1017 00:52:18,850 --> 00:52:21,720 of procedural level generation in the platformer genre. 1018 00:52:21,720 --> 00:52:24,184 Plenty of games like Minecraft and Terraria-- 1019 00:52:24,184 --> 00:52:26,600 Terraria is a great platformer that is an example of that. 1020 00:52:26,600 --> 00:52:29,342 But I don't think I've seen a really good Super Mario Bros. 1021 00:52:29,342 --> 00:52:30,800 game that does something like that. 1022 00:52:30,800 --> 00:52:33,620 1023 00:52:33,620 --> 00:52:34,120 Let's see. 1024 00:52:34,120 --> 00:52:34,580 What time is it? 1025 00:52:34,580 --> 00:52:35,230 6:23. 1026 00:52:35,230 --> 00:52:37,210 Let's take a five minute. 1027 00:52:37,210 --> 00:52:39,100 And then as soon as we get back from that, 1028 00:52:39,100 --> 00:52:43,150 we'll start going into how we actually can implement the procedural level 1029 00:52:43,150 --> 00:52:45,440 generation in more detail. 1030 00:52:45,440 --> 00:52:46,920 All right, welcome back. 1031 00:52:46,920 --> 00:52:47,890 This is lecture four. 1032 00:52:47,890 --> 00:52:49,681 And before we took a break, we were talking 1033 00:52:49,681 --> 00:52:54,040 about procedural level generation in the context of platformer levels. 1034 00:52:54,040 --> 00:53:00,940 So recall, here are just a few examples that I took pretty quickly of my code. 1035 00:53:00,940 --> 00:53:04,450 And you can see they have different backgrounds, different tiles. 1036 00:53:04,450 --> 00:53:07,420 Sometimes we have chasms, sometimes we have pillars. 1037 00:53:07,420 --> 00:53:12,610 We'll be talking about a few ways to do the tile version of that, 1038 00:53:12,610 --> 00:53:14,120 because there's two levels here. 1039 00:53:14,120 --> 00:53:18,620 In the distro, we'll see there are also things like bushes, for example. 1040 00:53:18,620 --> 00:53:21,460 We can see in the top middle there the purple-- 1041 00:53:21,460 --> 00:53:24,280 well, I guess those little purple cacti. 1042 00:53:24,280 --> 00:53:28,960 And the one right below that, there is a pillar with a yellow fern on it. 1043 00:53:28,960 --> 00:53:31,780 Those are separate objects from the tiles-- 1044 00:53:31,780 --> 00:53:32,860 game objects. 1045 00:53:32,860 --> 00:53:38,170 But the actual tiles themselves we'll dig in here a little bit as to how 1046 00:53:38,170 --> 00:53:39,710 to get those generated. 1047 00:53:39,710 --> 00:53:44,615 So the first thing we want to look at, level0, is just some flat levels-- so 1048 00:53:44,615 --> 00:53:46,240 just basically what we've already done. 1049 00:53:46,240 --> 00:53:50,980 So I'm going to go ahead and go into level0. 1050 00:53:50,980 --> 00:53:54,230 1051 00:53:54,230 --> 00:53:59,680 And then if we see here, we have a simple flat level, 1052 00:53:59,680 --> 00:54:01,040 just like we did before. 1053 00:54:01,040 --> 00:54:02,690 Now the tiles are different. 1054 00:54:02,690 --> 00:54:07,360 And if I press R, they're randomly generating every time. 1055 00:54:07,360 --> 00:54:12,782 So you can get a sense of just how visually diverse this generation looks. 1056 00:54:12,782 --> 00:54:14,740 Oh, I think that might have been a bug earlier. 1057 00:54:14,740 --> 00:54:16,090 I'm not sure. 1058 00:54:16,090 --> 00:54:17,440 Haven't seen that yet. 1059 00:54:17,440 --> 00:54:19,480 But we can see here, I'm pressing R. 1060 00:54:19,480 --> 00:54:26,020 All I'm doing is taking the array of tiles that we have, 1061 00:54:26,020 --> 00:54:30,430 and I'm assigning it a tile set and a topper set 1062 00:54:30,430 --> 00:54:35,830 in the case of the scope of this generation. 1063 00:54:35,830 --> 00:54:39,460 So recall that the topper is just the top layer sprite, 1064 00:54:39,460 --> 00:54:46,180 and the tile set is the tiles underneath. 1065 00:54:46,180 --> 00:54:51,100 Anybody want to just suggest how I'm rendering the topper versus the tiles 1066 00:54:51,100 --> 00:54:53,856 and what's going on there? 1067 00:54:53,856 --> 00:54:58,100 AUDIENCE: You're just pulling it from part of the sheet, right? 1068 00:54:58,100 --> 00:54:58,850 COLTON OGDEN: Yes. 1069 00:54:58,850 --> 00:55:01,282 Yeah, in a nutshell, I'm just pulling the toppers 1070 00:55:01,282 --> 00:55:02,740 from a different part of the sheet. 1071 00:55:02,740 --> 00:55:05,510 Any idea how I'm storing information-- what's being stored here 1072 00:55:05,510 --> 00:55:08,195 to get it to render like this? 1073 00:55:08,195 --> 00:55:12,155 AUDIENCE: Maybe you just need to store the position of the topper 1074 00:55:12,155 --> 00:55:15,620 and know that everything else is below that. 1075 00:55:15,620 --> 00:55:16,460 COLTON OGDEN: Yes. 1076 00:55:16,460 --> 00:55:18,410 So you could store the position of the topper 1077 00:55:18,410 --> 00:55:21,200 and know that everything else is below that. 1078 00:55:21,200 --> 00:55:24,360 That would work for a flat level. 1079 00:55:24,360 --> 00:55:27,502 I don't think that would be reliable for a level that has pillars on it, 1080 00:55:27,502 --> 00:55:29,960 because the pillars are a higher elevation than the ground. 1081 00:55:29,960 --> 00:55:32,001 And then there's also chasms and stuff like that. 1082 00:55:32,001 --> 00:55:35,090 1083 00:55:35,090 --> 00:55:37,115 So what's going on here actually is we're 1084 00:55:37,115 --> 00:55:41,850 storing a flag in the tile that says whether or not it has a topper on it. 1085 00:55:41,850 --> 00:55:45,320 And if it has a topper, then we render not only the tile, 1086 00:55:45,320 --> 00:55:49,580 but as soon as we render the tile, we also render the topper. 1087 00:55:49,580 --> 00:55:54,650 And I won't go too deep into the code here. 1088 00:55:54,650 --> 00:55:58,640 But what we're doing to get all these different tile sets and topper sets 1089 00:55:58,640 --> 00:56:04,570 too is we have to take all of these tile sets-- these collections of tiles-- 1090 00:56:04,570 --> 00:56:06,050 and divide them up, right? 1091 00:56:06,050 --> 00:56:09,390 We have to know that if we want to render the entire level in tile 1092 00:56:09,390 --> 00:56:15,450 set one, then we should basically take this into its own sheet-- 1093 00:56:15,450 --> 00:56:19,190 its own table-- this into its own table, this into its own table, going 1094 00:56:19,190 --> 00:56:21,560 left to right actually. 1095 00:56:21,560 --> 00:56:26,370 And we have basically four way nested loop. 1096 00:56:26,370 --> 00:56:31,460 So we go every set on the x by every set on the y. 1097 00:56:31,460 --> 00:56:36,380 And then within each of those, we want to look for every tile along the x 1098 00:56:36,380 --> 00:56:40,070 and every tile along the y therein and split up 1099 00:56:40,070 --> 00:56:45,020 the tile sets so that we can index into the individual quads. 1100 00:56:45,020 --> 00:56:49,430 So in the actual code, I won't go too deeply into it here. 1101 00:56:49,430 --> 00:56:53,330 But I'll show you where it is if you're curious to look into how we do that. 1102 00:56:53,330 --> 00:56:58,790 It's in Mario in Source, util.lua, which is recall where we before 1103 00:56:58,790 --> 00:57:02,930 stored our generateQuads function, which does a simple split on a tile 1104 00:57:02,930 --> 00:57:08,420 sheet along its x and y based on whatever width and height you pass in. 1105 00:57:08,420 --> 00:57:14,210 We have in here also a generateTileSets function, which takes in the quads 1106 00:57:14,210 --> 00:57:16,790 from a generateQuads table. 1107 00:57:16,790 --> 00:57:22,520 So we first generate quads on all of this or all of this. 1108 00:57:22,520 --> 00:57:26,840 So we have every single frame of this divided by 16, which is-- 1109 00:57:26,840 --> 00:57:28,130 I don't know how many that is. 1110 00:57:28,130 --> 00:57:33,990 6 by 5 times 10 by 5, 10 by 4-- 1111 00:57:33,990 --> 00:57:38,510 that many quads, so thousands of quads, I think, if not hundreds. 1112 00:57:38,510 --> 00:57:41,930 This I'm pretty sure is thousands of quads. 1113 00:57:41,930 --> 00:57:45,890 And then we take that and then divide it using 1114 00:57:45,890 --> 00:57:49,790 the number of sets along the x-axis, sets on the y-axis, 1115 00:57:49,790 --> 00:57:53,480 and then the size of each tile set along the x and size along the y. 1116 00:57:53,480 --> 00:57:59,801 We basically divide it using a four way nested loop here. 1117 00:57:59,801 --> 00:58:01,250 We basically just divide it up. 1118 00:58:01,250 --> 00:58:03,980 And then instead of doing a generateQuads 1119 00:58:03,980 --> 00:58:09,950 along the entirety of the picture, we just 1120 00:58:09,950 --> 00:58:12,830 basically do a 2D slice of that quad table we get back 1121 00:58:12,830 --> 00:58:14,930 from the first generateQuads call. 1122 00:58:14,930 --> 00:58:18,229 So I encourage you to look in here and experiment with that. 1123 00:58:18,229 --> 00:58:21,020 You don't need to necessarily know how it works for the assignment. 1124 00:58:21,020 --> 00:58:25,400 But that's how we can basically take a giant sheet like this 1125 00:58:25,400 --> 00:58:28,370 and easily integrate it into our code. 1126 00:58:28,370 --> 00:58:32,090 We can just swap in and out whatever active tile 1127 00:58:32,090 --> 00:58:34,520 sheet we want to work with, assuming that everything 1128 00:58:34,520 --> 00:58:38,510 is cleanly laid out like this, which is on the part of you or your artist. 1129 00:58:38,510 --> 00:58:44,000 You want to make sure that everything is conducive to programmatic organization. 1130 00:58:44,000 --> 00:58:46,830 Had things been scattered around in a very awkward way-- 1131 00:58:46,830 --> 00:58:50,300 maybe things were zig zagged or there were weird spaces or something like 1132 00:58:50,300 --> 00:58:50,900 that-- 1133 00:58:50,900 --> 00:58:53,630 we wouldn't be able to do something as clean as what we did here 1134 00:58:53,630 --> 00:58:57,620 in util.lua with just 63 minus 20 lines of code 1135 00:58:57,620 --> 00:59:01,164 by getting each individual tile set. 1136 00:59:01,164 --> 00:59:03,080 So that's an important consideration if you're 1137 00:59:03,080 --> 00:59:06,260 looking at creating assets for your project 1138 00:59:06,260 --> 00:59:11,601 and you want to do some programmatic hot swapping of your tile sets. 1139 00:59:11,601 --> 00:59:13,100 Let's make sure we're in the right-- 1140 00:59:13,100 --> 00:59:15,980 we're not in the right example here, so we're 1141 00:59:15,980 --> 00:59:23,210 going to go into level0 into main. 1142 00:59:23,210 --> 00:59:27,380 And we have constants now for all of our tile sets and what the height is 1143 00:59:27,380 --> 00:59:28,865 and how many they are wide by tall. 1144 00:59:28,865 --> 00:59:32,330 1145 00:59:32,330 --> 00:59:33,380 We do it here. 1146 00:59:33,380 --> 00:59:36,530 We get our regular quads from our tiles and toppers, 1147 00:59:36,530 --> 00:59:39,730 so these are just literally every single tile within that big tile 1148 00:59:39,730 --> 00:59:40,810 sheet put in one table. 1149 00:59:40,810 --> 00:59:42,560 And then we just divided up into tile sets 1150 00:59:42,560 --> 00:59:45,860 and topper sets here with generateTileSets function. 1151 00:59:45,860 --> 00:59:50,160 And then we get a random tile set and a random topper set here-- 1152 00:59:50,160 --> 00:59:54,260 math.random, number of tile sets, number of topper sets. 1153 00:59:54,260 --> 01:00:00,480 And then at the very bottom also, we have a generateLevel function-- 1154 01:00:00,480 --> 01:00:03,846 223-- which is going to be built upon in the next two examples. 1155 01:00:03,846 --> 01:00:05,720 Level0 is just a flat level, so it's actually 1156 01:00:05,720 --> 01:00:11,960 exactly what we saw before, which was just if y is less than 7, 1157 01:00:11,960 --> 01:00:14,770 ID should be equal to sky or ground. 1158 01:00:14,770 --> 01:00:17,067 And then this part is actually what I was 1159 01:00:17,067 --> 01:00:19,150 alluding to before with the topper, because recall 1160 01:00:19,150 --> 01:00:23,690 we need to store a flag in a tile to render a topper or not. 1161 01:00:23,690 --> 01:00:28,540 And it should be whatever the top tile is in the level on the ground. 1162 01:00:28,540 --> 01:00:31,960 In this very simple flat thing, we can always 1163 01:00:31,960 --> 01:00:34,060 assume it'll be the same y level. 1164 01:00:34,060 --> 01:00:36,580 In this case, if it's equal to 7, then topper 1165 01:00:36,580 --> 01:00:38,500 should be true, otherwise, false. 1166 01:00:38,500 --> 01:00:43,600 So every tile along y 7 is going to have topper equals true. 1167 01:00:43,600 --> 01:00:46,420 And this comes into play up here. 1168 01:00:46,420 --> 01:00:55,090 If we do love.graphics.draw(tilesheet), we have not only just tile.ID as we did 1169 01:00:55,090 --> 01:01:01,150 before, but we have tile sets indexed into tileset now. 1170 01:01:01,150 --> 01:01:03,760 So remember, tileset got a random value between 1 1171 01:01:03,760 --> 01:01:08,930 and however many tile sets we had that we spliced out of our massive tile 1172 01:01:08,930 --> 01:01:09,700 sheet. 1173 01:01:09,700 --> 01:01:14,650 Now, we just index into that, and then we index into tile.ID. 1174 01:01:14,650 --> 01:01:21,070 And tile.ID will then be whatever our ID is but relative to that sheet, not 1175 01:01:21,070 --> 01:01:24,320 the whole entire sprite at once. 1176 01:01:24,320 --> 01:01:25,570 And the same thing for topper. 1177 01:01:25,570 --> 01:01:27,700 We have a topper set, we index into topper 1178 01:01:27,700 --> 01:01:31,510 sets here at the topper set that we got, and then that's 1179 01:01:31,510 --> 01:01:38,045 where we'll have the collection of tiles that form that particular set. 1180 01:01:38,045 --> 01:01:39,670 And so the two are completely separate. 1181 01:01:39,670 --> 01:01:43,150 They can be one random color tile with one random topper, but it's consistent. 1182 01:01:43,150 --> 01:01:43,660 It's global. 1183 01:01:43,660 --> 01:01:47,870 We have one topper set and one tile set that are active at any one time. 1184 01:01:47,870 --> 01:01:50,560 And if we press R, which I did up here, then we 1185 01:01:50,560 --> 01:01:52,972 just reset them to random on line 139 and 140. 1186 01:01:52,972 --> 01:01:55,930 Tile set gets a new random number, topper set gets a new random number. 1187 01:01:55,930 --> 01:01:58,220 It has the effect of-- 1188 01:01:58,220 --> 01:02:01,000 we can just walk around and then generate random sets. 1189 01:02:01,000 --> 01:02:04,090 1190 01:02:04,090 --> 01:02:04,960 So pretty simple. 1191 01:02:04,960 --> 01:02:07,270 And recall again, topper is-- 1192 01:02:07,270 --> 01:02:12,110 because the tile that we're standing on is y seven, topper equals true. 1193 01:02:12,110 --> 01:02:16,624 So in that case, that particular top layer is always going to have a topper. 1194 01:02:16,624 --> 01:02:18,790 And it gives us a nice little bit of visual variety, 1195 01:02:18,790 --> 01:02:22,690 because it actually makes quite a bit of a difference having 1196 01:02:22,690 --> 01:02:23,944 a topper versus no topper. 1197 01:02:23,944 --> 01:02:26,110 And you can also just not have a topper and consider 1198 01:02:26,110 --> 01:02:33,250 that a permutation of the toppers times tiles, like procedural algorithm. 1199 01:02:33,250 --> 01:02:34,150 That's flat levels. 1200 01:02:34,150 --> 01:02:36,740 Does anybody have any questions as to how this works 1201 01:02:36,740 --> 01:02:38,230 or anything that we're doing here? 1202 01:02:38,230 --> 01:02:41,300 1203 01:02:41,300 --> 01:02:42,780 OK. 1204 01:02:42,780 --> 01:02:46,140 So things are a little flat, a little boring. 1205 01:02:46,140 --> 01:02:50,160 The next step will be actually introducing one of the things 1206 01:02:50,160 --> 01:02:55,110 that we can see here in our little collection of sample levels, 1207 01:02:55,110 --> 01:02:58,290 like this pillar right here in the very middle. 1208 01:02:58,290 --> 01:03:02,670 Does anybody recall how we go about spawning a pillar as opposed 1209 01:03:02,670 --> 01:03:06,428 to just flat land? 1210 01:03:06,428 --> 01:03:10,027 AUDIENCE: For that column, just put some more dirt down or more tiles down. 1211 01:03:10,027 --> 01:03:11,430 COLTON OGDEN: Yep. 1212 01:03:11,430 --> 01:03:15,880 So for that column, just put more tiles down instead of just the ground level. 1213 01:03:15,880 --> 01:03:17,530 That's exactly what we're going to do. 1214 01:03:17,530 --> 01:03:21,840 So I'm going to go ahead and open up level1 and main, 1215 01:03:21,840 --> 01:03:25,770 and I'll run the example here as well just so you can see it looks like. 1216 01:03:25,770 --> 01:03:30,100 1217 01:03:30,100 --> 01:03:32,590 So here we have quite a few. 1218 01:03:32,590 --> 01:03:34,340 And notice we haven't implement collision, 1219 01:03:34,340 --> 01:03:35,970 so we're still walking through them. 1220 01:03:35,970 --> 01:03:38,120 But they're just random. 1221 01:03:38,120 --> 01:03:40,530 Their random amount is up to taste, really. 1222 01:03:40,530 --> 01:03:43,920 Right here it's pretty common, so it might be worth 1223 01:03:43,920 --> 01:03:46,500 lowering the amount a little bit. 1224 01:03:46,500 --> 01:03:51,120 If you wanted to, you could also maybe have a flag that says, spawn pillar, 1225 01:03:51,120 --> 01:03:52,800 and maybe you want a pillar width. 1226 01:03:52,800 --> 01:03:56,430 You could have anywhere between one and three tiles. 1227 01:03:56,430 --> 01:04:02,070 And if its width is greater than 1, then just loop over a few times 1228 01:04:02,070 --> 01:04:05,970 and just draw that same height a few times as opposed to just one time, 1229 01:04:05,970 --> 01:04:08,982 and then set the flag back to false. 1230 01:04:08,982 --> 01:04:10,440 A lot of things you can do with it. 1231 01:04:10,440 --> 01:04:12,120 And also, they're a little tall here. 1232 01:04:12,120 --> 01:04:15,214 For the main distro, I ended up making them a little shorter. 1233 01:04:15,214 --> 01:04:17,130 But we'll see how we do this in the code here. 1234 01:04:17,130 --> 01:04:23,800 It's going to mostly be down in our generateLevel function. 1235 01:04:23,800 --> 01:04:26,340 So what we're doing here-- go ahead and hide that-- 1236 01:04:26,340 --> 01:04:28,970 is we have basically this code here-- 1237 01:04:28,970 --> 01:04:31,500 line 227 to 236. 1238 01:04:31,500 --> 01:04:38,820 So all we're doing here is just filling our entire thing with just sky. 1239 01:04:38,820 --> 01:04:41,340 We're just setting the entire thing to empty. 1240 01:04:41,340 --> 01:04:45,520 And now we have a fully populated 2D array. 1241 01:04:45,520 --> 01:04:47,520 All we need to do in order to change a tile-- we 1242 01:04:47,520 --> 01:04:52,530 don't have to worry about insertions or adding too many tiles to our array. 1243 01:04:52,530 --> 01:04:56,670 All we can do now is just directly change whatever tile exists there. 1244 01:04:56,670 --> 01:05:01,380 So all we need to do is starting on line 239, 1245 01:05:01,380 --> 01:05:05,400 we're going to start doing the column by column iteration over our entire level 1246 01:05:05,400 --> 01:05:09,150 and deciding whether we should generate pillars or not. 1247 01:05:09,150 --> 01:05:11,960 And we're always going to generate ground. 1248 01:05:11,960 --> 01:05:14,960 So here's the flag spawnPillar. 1249 01:05:14,960 --> 01:05:18,690 And if it's equal to 1, this is going to basically be assigned to spawnPillar. 1250 01:05:18,690 --> 01:05:20,190 So math.random(5)==1. 1251 01:05:20,190 --> 01:05:24,300 We have a 1 in 5 chance of spawning a pillar. 1252 01:05:24,300 --> 01:05:28,755 If we just want a pillar, then pillar gets equal to 4 from 4 to 6-- 1253 01:05:28,755 --> 01:05:34,470 so y gets 4 to 6 effectively-- 1254 01:05:34,470 --> 01:05:37,800 tiles at pillar x, ID ground. 1255 01:05:37,800 --> 01:05:41,310 And then here's where we set the topper, recall, 1256 01:05:41,310 --> 01:05:47,820 because now pillars can be the top most tile on the surface. 1257 01:05:47,820 --> 01:05:49,320 But they're above the ground level. 1258 01:05:49,320 --> 01:05:53,540 So we just basically say, when we're generating a pillar, 1259 01:05:53,540 --> 01:05:57,300 if pillar is equal to 4-- which is the very first tile that we start at-- 1260 01:05:57,300 --> 01:05:59,190 then set topper equal to true here. 1261 01:05:59,190 --> 01:06:01,140 Otherwise, set it to false. 1262 01:06:01,140 --> 01:06:06,601 So that's how we can get pillars to also have toppers and then in this case, 1263 01:06:06,601 --> 01:06:08,100 we're not generating any chasms yet. 1264 01:06:08,100 --> 01:06:09,183 So all we're going to do-- 1265 01:06:09,183 --> 01:06:11,850 1266 01:06:11,850 --> 01:06:15,030 once we've generated a pillar on that particular column, 1267 01:06:15,030 --> 01:06:18,060 we'll just say ground gets 7 until the map height-- 1268 01:06:18,060 --> 01:06:21,130 so towards the very bottom of the screen. 1269 01:06:21,130 --> 01:06:22,720 And then we'll just set it to ground. 1270 01:06:22,720 --> 01:06:25,890 And then topper-- in this case, we're going to make sure 1271 01:06:25,890 --> 01:06:27,690 that we're not spawning a pillar. 1272 01:06:27,690 --> 01:06:29,850 Because if we don't check this, then it'll 1273 01:06:29,850 --> 01:06:35,310 also spawn a topper where the pillar meets the ground, 1274 01:06:35,310 --> 01:06:36,930 and it'll look a little bit silly. 1275 01:06:36,930 --> 01:06:39,500 And then we also want to check that ground is equal to 7. 1276 01:06:39,500 --> 01:06:44,560 And so all together, that has the effect of this behavior. 1277 01:06:44,560 --> 01:06:47,400 And so if we didn't check for that spawnPillar, 1278 01:06:47,400 --> 01:06:49,560 we'd have a topper right below our feet here too, 1279 01:06:49,560 --> 01:06:52,320 which looks graphically strange. 1280 01:06:52,320 --> 01:06:54,240 And also, you can see-- 1281 01:06:54,240 --> 01:06:59,169 emergently, we're getting double width pillars. 1282 01:06:59,169 --> 01:07:00,960 And that's just kind of a natural byproduct 1283 01:07:00,960 --> 01:07:02,420 of a lot of these randomizations. 1284 01:07:02,420 --> 01:07:05,490 A lot of these procedural algorithms-- 1285 01:07:05,490 --> 01:07:08,040 they'll generate outcomes that you might not necessarily 1286 01:07:08,040 --> 01:07:10,500 have anticipated, which is kind of a cool thing. 1287 01:07:10,500 --> 01:07:15,180 You didn't necessarily program it to have pillars that were two tiles wide, 1288 01:07:15,180 --> 01:07:18,000 but just the nature of randomization-- that's just what you get. 1289 01:07:18,000 --> 01:07:20,791 And that's another exciting thing about procedural level generation 1290 01:07:20,791 --> 01:07:25,260 is that it can surprise even the person that wrote the algorithm. 1291 01:07:25,260 --> 01:07:30,300 It's really cool, and it saves you work having to create levels. 1292 01:07:30,300 --> 01:07:32,070 So that was pillared levels. 1293 01:07:32,070 --> 01:07:37,109 Chasm levels-- who can tell me how we can do chasm levels? 1294 01:07:37,109 --> 01:07:40,350 AUDIENCE: You just skip a column. 1295 01:07:40,350 --> 01:07:42,590 COLTON OGDEN: Yep. 1296 01:07:42,590 --> 01:07:43,550 you skip a column. 1297 01:07:43,550 --> 01:07:45,841 So at the very beginning, all we can just basically say 1298 01:07:45,841 --> 01:07:48,020 is, do I want to generate a chasm here? 1299 01:07:48,020 --> 01:07:50,270 If I do, just skip. 1300 01:07:50,270 --> 01:07:53,360 Go to the next iteration of the loop. 1301 01:07:53,360 --> 01:07:56,060 And so we'll take a look at that. 1302 01:07:56,060 --> 01:08:00,299 As simple as it is, because Lua doesn't have the notion of continue-- 1303 01:08:00,299 --> 01:08:02,090 this will be a refresher, because I believe 1304 01:08:02,090 --> 01:08:04,850 this was in one of the assignments-- 1305 01:08:04,850 --> 01:08:08,310 it has a goto statement. 1306 01:08:08,310 --> 01:08:13,450 So basically, same code as before, starting column by column. 1307 01:08:13,450 --> 01:08:16,279 x equals one until map width. 1308 01:08:16,279 --> 01:08:17,890 We have a 1 in 7 chance-- 1309 01:08:17,890 --> 01:08:20,149 just arbitrary. 1310 01:08:20,149 --> 01:08:22,250 And this should ideally-- 1311 01:08:22,250 --> 01:08:28,529 if you're engineering an entire large game or application, 1312 01:08:28,529 --> 01:08:31,839 this would be called SPAWN_CHASM_CHANCE probably, 1313 01:08:31,839 --> 01:08:33,380 and just set that to seven somewhere. 1314 01:08:33,380 --> 01:08:35,180 But we're just setting it to 7 here-- 1315 01:08:35,180 --> 01:08:39,560 just a static magic number, but magic numbers are generally bad. 1316 01:08:39,560 --> 01:08:46,460 Goto continue-- and so continue is here at the very bottom of the loop 1317 01:08:46,460 --> 01:08:50,536 here, which is this for x = 1, mapWidth. 1318 01:08:50,536 --> 01:08:53,660 So it will have the effect of skipping straight to x equals 2 if this at 1, 1319 01:08:53,660 --> 01:08:55,670 for example. 1320 01:08:55,670 --> 01:08:58,630 A lot of languages just simply have continue. 1321 01:08:58,630 --> 01:09:05,090 Lua does not have continue, so this is a community established tradition 1322 01:09:05,090 --> 01:09:07,729 for implementing continue-like behavior in Lua. 1323 01:09:07,729 --> 01:09:14,000 You create a label via double colon with a name and then a double colon, 1324 01:09:14,000 --> 01:09:16,200 and then you just goto it. 1325 01:09:16,200 --> 01:09:18,859 And so that's as simple as it is for generating chasms. 1326 01:09:18,859 --> 01:09:26,800 And so if we go to level2 and run that, we get chasms. 1327 01:09:26,800 --> 01:09:30,359 And so now we've got a little bit of interesting visual variety. 1328 01:09:30,359 --> 01:09:33,680 It's not spawning a ton of chasms in this example. 1329 01:09:33,680 --> 01:09:35,590 It spawned one so far. 1330 01:09:35,590 --> 01:09:36,470 There's another one. 1331 01:09:36,470 --> 01:09:38,762 And then sometimes just emergently, you can get two. 1332 01:09:38,762 --> 01:09:39,470 See, there we go. 1333 01:09:39,470 --> 01:09:41,540 We get some interesting obstacles as a result. 1334 01:09:41,540 --> 01:09:43,939 It almost looks as if someone intentionally did that-- 1335 01:09:43,939 --> 01:09:45,199 almost. 1336 01:09:45,199 --> 01:09:47,990 I would probably, like I said, shrink the pillar size a little bit. 1337 01:09:47,990 --> 01:09:48,781 It's a little tall. 1338 01:09:48,781 --> 01:09:52,310 1339 01:09:52,310 --> 01:09:52,910 That's that. 1340 01:09:52,910 --> 01:09:56,360 That's basic procedural-- in the context of platformers, 1341 01:09:56,360 --> 01:09:58,970 that's the mental model for how we can start 1342 01:09:58,970 --> 01:10:01,059 thinking about generating obstacles. 1343 01:10:01,059 --> 01:10:03,350 And there's a lot of different directions you could go. 1344 01:10:03,350 --> 01:10:09,320 Let's say maybe you wanted to generate pyramids. 1345 01:10:09,320 --> 01:10:10,910 I mean, it's a common thing in Mario. 1346 01:10:10,910 --> 01:10:14,480 There will be steps, [INAUDIBLE] set for it. 1347 01:10:14,480 --> 01:10:18,457 The same implementation would basically happen here. 1348 01:10:18,457 --> 01:10:20,540 It would be a little bit different, because you're 1349 01:10:20,540 --> 01:10:22,081 doing it on a column by column basis. 1350 01:10:22,081 --> 01:10:24,770 But you'd effectively just maintain a reference 1351 01:10:24,770 --> 01:10:27,620 to something like step height, and then you 1352 01:10:27,620 --> 01:10:30,216 would say generate stairs is true here. 1353 01:10:30,216 --> 01:10:32,090 And then you would just set step height to 1. 1354 01:10:32,090 --> 01:10:34,360 So then you add a tile here. 1355 01:10:34,360 --> 01:10:37,800 You would go from ground level up until step height, generate a tile, 1356 01:10:37,800 --> 01:10:40,520 go the next one, and then increment step height to 2. 1357 01:10:40,520 --> 01:10:44,630 And then do from ground until step height-- 1358 01:10:44,630 --> 01:10:45,620 tiles go up. 1359 01:10:45,620 --> 01:10:51,080 So 1 and then 2 and 3 until you've gone to stairs width, in which case 1360 01:10:51,080 --> 01:10:52,400 you stop generating stairs. 1361 01:10:52,400 --> 01:10:56,955 That's this principle behind how you could 1362 01:10:56,955 --> 01:10:58,580 do something a little more complicated. 1363 01:10:58,580 --> 01:11:00,980 Or pyramids-- same exact thing, pyramid width. 1364 01:11:00,980 --> 01:11:03,890 And then you just go until pyramid width equals-- 1365 01:11:03,890 --> 01:11:07,430 or we're at pyramid width divided by 2, make it go up. 1366 01:11:07,430 --> 01:11:09,540 And if we're higher than that, make it go down. 1367 01:11:09,540 --> 01:11:12,760 And then you have the effect of the pyramid approach. 1368 01:11:12,760 --> 01:11:13,516 Yeah. 1369 01:11:13,516 --> 01:11:15,265 AUDIENCE: Where are you putting the column 1370 01:11:15,265 --> 01:11:18,576 generation if it's a [INAUDIBLE]. 1371 01:11:18,576 --> 01:11:20,480 It's not in the play state. 1372 01:11:20,480 --> 01:11:22,550 COLTON OGDEN: In this case, it's all in main.lua. 1373 01:11:22,550 --> 01:11:25,520 But in the distro, it's going to be in levelmaker.lua. 1374 01:11:25,520 --> 01:11:28,400 So we've broken out all of this functionality 1375 01:11:28,400 --> 01:11:31,730 into just how we did Breakout. 1376 01:11:31,730 --> 01:11:34,100 We had the same sort of thing-- level maker-- 1377 01:11:34,100 --> 01:11:35,620 and it just has levelMaker.generate. 1378 01:11:35,620 --> 01:11:37,370 And then you give it a width and a height, 1379 01:11:37,370 --> 01:11:39,621 and it will generate an entire level for you. 1380 01:11:39,621 --> 01:11:42,507 AUDIENCE: An entire level, but it has to continuously-- 1381 01:11:42,507 --> 01:11:44,912 oh, you generate it all at once? 1382 01:11:44,912 --> 01:11:48,244 It doesn't generate as you walk? 1383 01:11:48,244 --> 01:11:51,410 COLTON OGDEN: The question is, does it generate continuously or all at once? 1384 01:11:51,410 --> 01:11:53,010 It just generates all at once. 1385 01:11:53,010 --> 01:11:56,090 So you could implement a-- 1386 01:11:56,090 --> 01:12:01,820 if you wanted to do an infinite runner, the way you would do that is you 1387 01:12:01,820 --> 01:12:04,450 would break up your level into chunks. 1388 01:12:04,450 --> 01:12:07,860 And with infinite runners, usually you can only move in one direction. 1389 01:12:07,860 --> 01:12:10,850 So as you go right, your levels that you've generated before-- they get 1390 01:12:10,850 --> 01:12:14,960 discarded, so you avoid memory overconsumption. 1391 01:12:14,960 --> 01:12:17,270 What you would do is you would just generate a chunk-- 1392 01:12:17,270 --> 01:12:19,940 maybe a 100 by 20 level. 1393 01:12:19,940 --> 01:12:22,850 And then you would go through that, through that. 1394 01:12:22,850 --> 01:12:28,040 And then when you get to level end minus maybe like five tiles or 10 tiles, 1395 01:12:28,040 --> 01:12:32,840 you would generate another one, append it, put it to the right, 1396 01:12:32,840 --> 01:12:36,740 and then you would just go from the left to the right. 1397 01:12:36,740 --> 01:12:40,960 And you probably would need some sort of semi-fancy code 1398 01:12:40,960 --> 01:12:44,350 to splice them together once you've generated them. 1399 01:12:44,350 --> 01:12:47,627 Alternatively, you could just always pad your-- 1400 01:12:47,627 --> 01:12:49,210 no, you probably wouldn't want to pad. 1401 01:12:49,210 --> 01:12:56,560 I would probably just splice them end to end and then get rid of x equals 1 100 1402 01:12:56,560 --> 01:13:01,810 or however many on the left once it's gone past the left edge of the screen. 1403 01:13:01,810 --> 01:13:05,050 In this case, to summarize, it's all static. 1404 01:13:05,050 --> 01:13:06,780 But you could very easily-- 1405 01:13:06,780 --> 01:13:11,090 not easily, but you could very well make it an infinite runner. 1406 01:13:11,090 --> 01:13:11,590 Yeah. 1407 01:13:11,590 --> 01:13:16,572 AUDIENCE: So we're rendering the entire level, but we just can't see it all? 1408 01:13:16,572 --> 01:13:19,280 COLTON OGDEN: The question is, are we rendering the entire level, 1409 01:13:19,280 --> 01:13:20,488 but we just can't see it all? 1410 01:13:20,488 --> 01:13:21,410 The answer is yes. 1411 01:13:21,410 --> 01:13:24,493 Currently, in this implementation, we're just rendering the entire level-- 1412 01:13:24,493 --> 01:13:26,720 so tile by tile is getting drawn to the screen. 1413 01:13:26,720 --> 01:13:30,410 For small examples like this, it's not a concern. 1414 01:13:30,410 --> 01:13:33,860 But for a large level-- like if we did a Terraria level, for example. 1415 01:13:33,860 --> 01:13:38,480 Terraria's thousands and thousands of tiles wide by probably 1416 01:13:38,480 --> 01:13:40,340 1,000 or more tiles tall-- 1417 01:13:40,340 --> 01:13:44,630 you want to render only a chunk, only what you can visibly see. 1418 01:13:44,630 --> 01:13:49,730 And for that, you could use your camera offset and then just render 1419 01:13:49,730 --> 01:13:53,949 from one tile to the left and above that to one tile below the bottom edge 1420 01:13:53,949 --> 01:13:55,490 of the camera and to the right of it. 1421 01:13:55,490 --> 01:13:57,650 Just render that subset of tiles. 1422 01:13:57,650 --> 01:14:01,250 So you just need a for loop to iterate over a small section. 1423 01:14:01,250 --> 01:14:04,550 AUDIENCE: So you can kind of make an array 1424 01:14:04,550 --> 01:14:07,034 of what the map's going to look like and then 1425 01:14:07,034 --> 01:14:11,184 just render only slices of the array that you can see. 1426 01:14:11,184 --> 01:14:12,180 Is that right? 1427 01:14:12,180 --> 01:14:18,654 If you put a multi-dimensional array and then you just go through it 1428 01:14:18,654 --> 01:14:20,542 and render as you go-- is that the thought? 1429 01:14:20,542 --> 01:14:22,250 COLTON OGDEN: Question was, you just have 1430 01:14:22,250 --> 01:14:24,400 a multi-dimensional array of tiles for your level, 1431 01:14:24,400 --> 01:14:25,983 and then you just render it as you go. 1432 01:14:25,983 --> 01:14:27,050 The answer is yes. 1433 01:14:27,050 --> 01:14:29,600 You would have your overall tiles-- 1434 01:14:29,600 --> 01:14:35,030 your big 2D array of 100 by 20 or however many thousands of tiles. 1435 01:14:35,030 --> 01:14:38,295 And then based on wherever your camera is rendering, 1436 01:14:38,295 --> 01:14:41,150 it's just a for loop within that just of a nested amount. 1437 01:14:41,150 --> 01:14:46,970 So maybe your player is at x 30 plus 6 tiles. 1438 01:14:46,970 --> 01:14:51,350 So you would just render from 30 tiles to maybe 45 tiles 1439 01:14:51,350 --> 01:14:55,040 on x and maybe 10 to 20 on the y-- 1440 01:14:55,040 --> 01:14:56,120 just that chunk. 1441 01:14:56,120 --> 01:14:58,100 And it's just relative to where your camera is. 1442 01:14:58,100 --> 01:15:00,230 You're always rendering just a small little-- 1443 01:15:00,230 --> 01:15:02,630 basically, it is effectively a camera at that point. 1444 01:15:02,630 --> 01:15:05,630 It's rendering a chunk of the tiles to the screen. 1445 01:15:05,630 --> 01:15:07,359 AUDIENCE: But in this code, it's not. 1446 01:15:07,359 --> 01:15:08,650 COLTON OGDEN: In this code, no. 1447 01:15:08,650 --> 01:15:10,300 The levels here are-- 1448 01:15:10,300 --> 01:15:13,494 it's sufficiently complicated to introduce. 1449 01:15:13,494 --> 01:15:15,410 I mean, it's not too complicated to introduce. 1450 01:15:15,410 --> 01:15:16,520 It's pretty easy. 1451 01:15:16,520 --> 01:15:20,540 But the consumption-- the processing here-- 1452 01:15:20,540 --> 01:15:23,030 is very light, because the levels are fairly small. 1453 01:15:23,030 --> 01:15:25,430 And even if we did have really large levels, 1454 01:15:25,430 --> 01:15:29,550 it's sufficiently small to not have to worry about it. 1455 01:15:29,550 --> 01:15:33,770 But if we did get to a point where your levels were 1,000 tiles or more, 1456 01:15:33,770 --> 01:15:35,656 and then maybe those tiles have additional, 1457 01:15:35,656 --> 01:15:38,030 you just want to squeeze all the performance possible out 1458 01:15:38,030 --> 01:15:40,350 of your application. 1459 01:15:40,350 --> 01:15:43,050 You could look into just rendering a subset. 1460 01:15:43,050 --> 01:15:46,340 It's fairly simple to introduce but just not something 1461 01:15:46,340 --> 01:15:50,420 that we included in this assignment. 1462 01:15:50,420 --> 01:15:54,687 Any other questions as to how this sort of thing works? 1463 01:15:54,687 --> 01:15:58,115 1464 01:15:58,115 --> 01:15:58,615 OK. 1465 01:15:58,615 --> 01:16:01,120 1466 01:16:01,120 --> 01:16:04,960 So far, we've talked about procedural level generation. 1467 01:16:04,960 --> 01:16:09,250 We've talked about animation and rendering and all that stuff. 1468 01:16:09,250 --> 01:16:12,970 We haven't really talked about how to do tile collision. 1469 01:16:12,970 --> 01:16:15,550 And we won't go into a terrible amount of detail, 1470 01:16:15,550 --> 01:16:17,170 because the code is a little lengthy. 1471 01:16:17,170 --> 01:16:19,961 It'll be part of your assignment to read over it and understand it, 1472 01:16:19,961 --> 01:16:24,940 but it's in the TileMap class that we have. 1473 01:16:24,940 --> 01:16:29,290 Basically, the whole gist is that because we're 1474 01:16:29,290 --> 01:16:34,990 on a 2D tile array that's fixed, it'll always be at 0, 0, at least 1475 01:16:34,990 --> 01:16:37,580 in the model that we've currently implemented. 1476 01:16:37,580 --> 01:16:42,730 We can just convert coordinates to tiles and then 1477 01:16:42,730 --> 01:16:48,310 just check to see whether or not the tiles at whatever that is 1478 01:16:48,310 --> 01:16:51,220 are solid or not. 1479 01:16:51,220 --> 01:16:57,830 Let's say we wanted to look at the top of our character in this case. 1480 01:16:57,830 --> 01:17:00,970 So if we have our character here. 1481 01:17:00,970 --> 01:17:04,240 For the sake of illustration, I put him between two tiles above him 1482 01:17:04,240 --> 01:17:07,750 just to show why we need to do this the way that we are doing it. 1483 01:17:07,750 --> 01:17:10,600 But you take the point here-- 1484 01:17:10,600 --> 01:17:14,440 his very top left, so player.x and then player.y, 1485 01:17:14,440 --> 01:17:17,080 which is effectively their version of 0, 0. 1486 01:17:17,080 --> 01:17:20,610 And then player.x plus player.width minus-- 1487 01:17:20,610 --> 01:17:27,210 we do a minus one for a lot of collisions 1488 01:17:27,210 --> 01:17:29,740 so that he can walk between blocks and stuff like that. 1489 01:17:29,740 --> 01:17:33,940 Because if you don't basically give him slightly less than the amount-- 1490 01:17:33,940 --> 01:17:37,080 because he's 16 pixels wide, and the tiles are 16 pixels wide-- 1491 01:17:37,080 --> 01:17:39,730 if he's between two blocks and he wanted to fall down, 1492 01:17:39,730 --> 01:17:42,580 he just won't fall down, because it's still detecting a collision. 1493 01:17:42,580 --> 01:17:45,340 1494 01:17:45,340 --> 01:17:47,140 Because if he's on the hole here-- 1495 01:17:47,140 --> 01:17:50,500 let's say this is the hole, and these are the tiles here. 1496 01:17:50,500 --> 01:17:52,230 The x plus the width-- 1497 01:17:52,230 --> 01:17:55,120 it'll trigger a collision on this tile and this tile still. 1498 01:17:55,120 --> 01:17:58,480 So basically, you need to minimize his collision box by one pixel 1499 01:17:58,480 --> 01:18:03,970 to fit through 16 pixel gaps essentially is what it boils down to. 1500 01:18:03,970 --> 01:18:07,315 But the gist behind collision-- 1501 01:18:07,315 --> 01:18:09,190 in this case, this would only apply when he's 1502 01:18:09,190 --> 01:18:11,440 jumping, because this is the only time at which he can really 1503 01:18:11,440 --> 01:18:13,240 collide with tiles that are above him. 1504 01:18:13,240 --> 01:18:17,190 You would test for whatever block falls on this pixel 1505 01:18:17,190 --> 01:18:19,490 and whatever block falls on this pixel. 1506 01:18:19,490 --> 01:18:23,380 And if either of them are solid, you trigger collision. 1507 01:18:23,380 --> 01:18:25,610 And if not, then there's no collision at all. 1508 01:18:25,610 --> 01:18:29,210 So if he's right here, for example-- 1509 01:18:29,210 --> 01:18:31,960 right directly beneath a tile-- it's only going to check one tile. 1510 01:18:31,960 --> 01:18:35,440 This point and this point are both going to fall on this tile. 1511 01:18:35,440 --> 01:18:39,220 But the reason that we want to check for both points here and here 1512 01:18:39,220 --> 01:18:42,850 is in the event that he is beneath two separate tiles, 1513 01:18:42,850 --> 01:18:45,354 because now this point's going to check this tile, 1514 01:18:45,354 --> 01:18:47,020 and this one's going to check this tile. 1515 01:18:47,020 --> 01:18:50,950 We can't just check this tile, because if we only check this tile 1516 01:18:50,950 --> 01:18:53,860 and there was no tile here but there was a tile here, 1517 01:18:53,860 --> 01:18:57,070 him jumping would still not trigger a collision. 1518 01:18:57,070 --> 01:18:59,950 It would think that it was only looking here and not here. 1519 01:18:59,950 --> 01:19:05,200 So for every collision on every side we do of him effectively, 1520 01:19:05,200 --> 01:19:09,520 we need to check both corners of that edge effectively. 1521 01:19:09,520 --> 01:19:14,610 So when he's jumping, we turn this point-- 1522 01:19:14,610 --> 01:19:19,690 this x, y-- into a tile by just dividing it by tile size. 1523 01:19:19,690 --> 01:19:23,890 So we can say, player.x divided by tile size plus one. 1524 01:19:23,890 --> 01:19:27,520 That's going to equal whatever tile this is on the x. 1525 01:19:27,520 --> 01:19:29,200 And then same thing for the y-- 1526 01:19:29,200 --> 01:19:32,410 we just divide the y by tile size, and then we add 1 to it. 1527 01:19:32,410 --> 01:19:34,900 And that will allow us to get the exact tile. 1528 01:19:34,900 --> 01:19:39,340 If we use those x, y that we get from that operation, 1529 01:19:39,340 --> 01:19:44,560 we get the exact tile at that y, x index in our tile's 2D array effectively. 1530 01:19:44,560 --> 01:19:45,820 So we do that for jump. 1531 01:19:45,820 --> 01:19:50,099 We check both corners of the top of his head. 1532 01:19:50,099 --> 01:19:52,390 We do the same thing for the bottom, only at that time, 1533 01:19:52,390 --> 01:19:56,260 we're checking x, and then y plus height, 1534 01:19:56,260 --> 01:19:58,810 and then x plus width, y plus height. 1535 01:19:58,810 --> 01:20:01,862 And then if we're doing the left edge, what are we checking? 1536 01:20:01,862 --> 01:20:05,208 1537 01:20:05,208 --> 01:20:07,125 AUDIENCE: The bottom left and top left? 1538 01:20:07,125 --> 01:20:08,000 COLTON OGDEN: We are. 1539 01:20:08,000 --> 01:20:13,220 So that will be x0, y, and then x0, y plus height. 1540 01:20:13,220 --> 01:20:15,590 And then if it's the right edge, same thing. 1541 01:20:15,590 --> 01:20:23,420 We check x plus width y, and then we check x plus width y plus height. 1542 01:20:23,420 --> 01:20:30,320 And so that's the gist behind collision detection in the distro here. 1543 01:20:30,320 --> 01:20:35,405 And you can see it in Mario if we go to TileMap. 1544 01:20:35,405 --> 01:20:39,590 1545 01:20:39,590 --> 01:20:42,680 Point to tile-- this is effectively where it happens. 1546 01:20:42,680 --> 01:20:49,610 1547 01:20:49,610 --> 01:20:52,390 On line 32, we're basically returning-- 1548 01:20:52,390 --> 01:20:53,660 this bit of code here-- 1549 01:20:53,660 --> 01:20:56,030 28 to 30-- is a check. 1550 01:20:56,030 --> 01:20:59,120 Because we can jump over the map edge, we 1551 01:20:59,120 --> 01:21:02,669 won't be able to check at tile y divided by TILE_SIZE 1552 01:21:02,669 --> 01:21:05,460 plus 1, x divided by TILE_SIZE plus one, because those will be nil. 1553 01:21:05,460 --> 01:21:07,209 Those won't exist, because he'll literally 1554 01:21:07,209 --> 01:21:08,510 be outside the map boundaries. 1555 01:21:08,510 --> 01:21:12,540 Same thing if he goes below it or he goes beyond the left or right edge. 1556 01:21:12,540 --> 01:21:14,030 So that's all this code is here. 1557 01:21:14,030 --> 01:21:17,780 It just makes sure that if we do go beyond the map boundaries, 1558 01:21:17,780 --> 01:21:18,920 we return nil. 1559 01:21:18,920 --> 01:21:23,540 So that way, we can check nil rather than getting a tile index error. 1560 01:21:23,540 --> 01:21:26,370 And then on line 32 is the operation that I just mentioned, 1561 01:21:26,370 --> 01:21:28,550 which was we take the y-- 1562 01:21:28,550 --> 01:21:33,020 so this x and y that we pass in are going to be the player's actual x, y. 1563 01:21:33,020 --> 01:21:38,360 When we pass those in, we're just going to get the tile at self.tiles, 1564 01:21:38,360 --> 01:21:43,130 and then effectively y divided by TILE_SIZE taken down to an integer, 1565 01:21:43,130 --> 01:21:43,890 and then add 1. 1566 01:21:43,890 --> 01:21:49,070 Because recall, tables are 1 indexed, but the coordinates are 0 indexed. 1567 01:21:49,070 --> 01:21:53,210 So this will result in a 0 indexed outcome, so we want to add 1 to it. 1568 01:21:53,210 --> 01:21:57,000 Same thing for here-- math.floor(x) divided by TILE_SIZE plus 1. 1569 01:21:57,000 --> 01:21:59,870 So effectively, points to tiles. 1570 01:21:59,870 --> 01:22:01,660 And then we'll just get a tile from that. 1571 01:22:01,660 --> 01:22:05,130 And the tile-- we can just check, hey, is that tile solid or not? 1572 01:22:05,130 --> 01:22:07,830 If it is, trigger collision. 1573 01:22:07,830 --> 01:22:12,980 So that's the gist behind being able to do it in a platformer 1574 01:22:12,980 --> 01:22:14,459 where everything is fixed. 1575 01:22:14,459 --> 01:22:16,250 That's sort of like a shortcut we can take. 1576 01:22:16,250 --> 01:22:19,730 Because now, what's the nice thing about this? 1577 01:22:19,730 --> 01:22:24,980 What jumps out as being a super nice thing about this algorithm, 1578 01:22:24,980 --> 01:22:35,720 imagining that we have, let's say, 10,000 tiles in our game world. 1579 01:22:35,720 --> 01:22:38,390 So if you look and see, all we're doing is 1580 01:22:38,390 --> 01:22:40,700 we're just doing a simple mathematical operation 1581 01:22:40,700 --> 01:22:44,150 on what his x and y is, right? 1582 01:22:44,150 --> 01:22:46,520 What's the alternative to this? 1583 01:22:46,520 --> 01:22:50,430 If we were doing this via AABB, for example, 1584 01:22:50,430 --> 01:22:54,518 we'd have to iterate over every single tile, right? 1585 01:22:54,518 --> 01:22:56,474 AUDIENCE: Can you summarize? 1586 01:22:56,474 --> 01:23:00,386 To avoid iterating over everything on the screen, 1587 01:23:00,386 --> 01:23:03,820 you just check the column that he's in and the column tile? 1588 01:23:03,820 --> 01:23:04,880 COLTON OGDEN: Yep. 1589 01:23:04,880 --> 01:23:09,720 So the gist is he's got an x and a y. 1590 01:23:09,720 --> 01:23:13,940 The x and the y are going to be in world coordinates, so his x could be 67 1591 01:23:13,940 --> 01:23:16,040 and his y could be 38 or something like that. 1592 01:23:16,040 --> 01:23:17,450 They don't map evenly to tiles. 1593 01:23:17,450 --> 01:23:20,660 But if we divide those by whatever the tile size is in our world-- 1594 01:23:20,660 --> 01:23:25,310 16-- that's going to be the exact tile. 1595 01:23:25,310 --> 01:23:30,500 We also have to add 1 to it, because the tables in lua are 1 indexed. 1596 01:23:30,500 --> 01:23:34,040 But we can index our self.tiles at the x, y 1597 01:23:34,040 --> 01:23:36,945 that we get from that-- the dividing by 16. 1598 01:23:36,945 --> 01:23:39,320 And that will be the exact tile that he's colliding with. 1599 01:23:39,320 --> 01:23:43,340 1600 01:23:43,340 --> 01:23:45,710 We don't have to basically have a collection of tiles 1601 01:23:45,710 --> 01:23:48,710 that we iterate over and check whether they 1602 01:23:48,710 --> 01:23:51,200 collide with the player using AABB collision detection 1603 01:23:51,200 --> 01:23:52,220 like we've done before. 1604 01:23:52,220 --> 01:23:54,620 Because recall, in Breakout, we had the bricks, right? 1605 01:23:54,620 --> 01:23:56,990 They all had their own x, y, but they weren't on a grid. 1606 01:23:56,990 --> 01:23:58,040 They weren't fixed. 1607 01:23:58,040 --> 01:24:01,130 So we had to actually take them and do an AABB. 1608 01:24:01,130 --> 01:24:03,740 We had to iterate over them and perform AABB on them, 1609 01:24:03,740 --> 01:24:08,450 because there's no deterministic way to just index at them really quickly. 1610 01:24:08,450 --> 01:24:11,300 It's the same thing with arrays versus linked lists. 1611 01:24:11,300 --> 01:24:16,610 Because arrays-- you can calculate how far some value is given an index. 1612 01:24:16,610 --> 01:24:18,450 You have instant access to it. 1613 01:24:18,450 --> 01:24:22,070 It's an order of one operation as opposed to a linked list. 1614 01:24:22,070 --> 01:24:24,230 If you want to try and get to a particular value, 1615 01:24:24,230 --> 01:24:26,854 you have to iterate through the entire thing until you find it. 1616 01:24:26,854 --> 01:24:31,790 AUDIENCE: Can you just look for the column that you might be landing on? 1617 01:24:31,790 --> 01:24:36,650 COLTON OGDEN: You're getting the exact tile at whatever your x divided by 16-- 1618 01:24:36,650 --> 01:24:38,150 or whatever your tile size is-- 1619 01:24:38,150 --> 01:24:40,370 and your y divided by 16 is. 1620 01:24:40,370 --> 01:24:43,010 And you're doing it, recall, for two different points 1621 01:24:43,010 --> 01:24:44,777 depending on what you're looking for. 1622 01:24:44,777 --> 01:24:47,360 If you're looking for the tiles that are above your character, 1623 01:24:47,360 --> 01:24:49,280 you're going to be doing it for this point. 1624 01:24:49,280 --> 01:24:53,030 So whatever this value is-- his base x, y-- 1625 01:24:53,030 --> 01:24:57,490 whatever that is divided by 16 and then whatever that is divided by 16. 1626 01:24:57,490 --> 01:25:00,310 And then that'll get you whatever tiles are directly above him. 1627 01:25:00,310 --> 01:25:03,160 It will intersect with whatever tile intersects with this point 1628 01:25:03,160 --> 01:25:05,272 and whatever tile intersects with this point. 1629 01:25:05,272 --> 01:25:07,730 Same thing with here and here if we're looking on the left, 1630 01:25:07,730 --> 01:25:09,080 here, here if we're looking on the bottom, 1631 01:25:09,080 --> 01:25:11,610 and here and here if we're looking on the right side. 1632 01:25:11,610 --> 01:25:13,640 And we check for collision after he's already 1633 01:25:13,640 --> 01:25:18,340 moved so that these points will be intersected with potential blocks. 1634 01:25:18,340 --> 01:25:20,840 And that's how we can check whether it's a collision or not. 1635 01:25:20,840 --> 01:25:26,844 We do this when he moves and is in some sort of movement state. 1636 01:25:26,844 --> 01:25:29,130 AUDIENCE: So you're still doing collision detection 1637 01:25:29,130 --> 01:25:31,430 with his actual coordinates, but you're just 1638 01:25:31,430 --> 01:25:34,620 narrowing what you're character width-- 1639 01:25:34,620 --> 01:25:36,000 COLTON OGDEN: Yep. 1640 01:25:36,000 --> 01:25:39,360 we're turning it from iterating over every single tile 1641 01:25:39,360 --> 01:25:42,540 to an instant operation, because we can just mathematically get 1642 01:25:42,540 --> 01:25:45,210 the exact tiles that he's at without having 1643 01:25:45,210 --> 01:25:47,130 to worry about where he is in the map. 1644 01:25:47,130 --> 01:25:50,450 It's just instant access. 1645 01:25:50,450 --> 01:25:53,820 And this only works because we know the tiles are always 1646 01:25:53,820 --> 01:25:55,290 fixed in the exact same locations. 1647 01:25:55,290 --> 01:25:56,623 They're always starting at 0, 0. 1648 01:25:56,623 --> 01:25:58,262 They're always going to be TILE_SIZE. 1649 01:25:58,262 --> 01:25:59,970 Things get a little more complicated when 1650 01:25:59,970 --> 01:26:04,920 we introduce game objects, which have their own independent x, y. 1651 01:26:04,920 --> 01:26:06,824 And for those, you do have to iterate over. 1652 01:26:06,824 --> 01:26:09,990 You have basically a collection of game objects or a collection of entities. 1653 01:26:09,990 --> 01:26:12,270 Let's say we have snails in the game world. 1654 01:26:12,270 --> 01:26:16,770 The snails aren't going to be at some fixed location every time. 1655 01:26:16,770 --> 01:26:19,170 They can move continuously. 1656 01:26:19,170 --> 01:26:21,960 So for those, we have to actually keep them all in a container 1657 01:26:21,960 --> 01:26:26,940 and then loop through them and say, has my player collided with any of these? 1658 01:26:26,940 --> 01:26:29,520 If he has, then trigger a collision with that snail-- 1659 01:26:29,520 --> 01:26:34,532 kill it or kill the player if he's in a walking state or a jump state. 1660 01:26:34,532 --> 01:26:36,240 And if he's in a falling state, then they 1661 01:26:36,240 --> 01:26:39,480 should die, because he's colliding with them from the top. 1662 01:26:39,480 --> 01:26:43,500 And you narrow down what collision you check for, as you can see at the bottom 1663 01:26:43,500 --> 01:26:44,850 here. 1664 01:26:44,850 --> 01:26:47,267 Tile collision-- when you're looking above your character, 1665 01:26:47,267 --> 01:26:49,683 you're only testing that when you're in the jumping state, 1666 01:26:49,683 --> 01:26:51,390 because it's the only time you need to. 1667 01:26:51,390 --> 01:26:55,380 So that's the only point at which you'll collide with tiles that are above you. 1668 01:26:55,380 --> 01:26:58,980 When you're in the falling state is when you'll check for tiles below you. 1669 01:26:58,980 --> 01:27:01,390 And then you can interact with tiles to your side 1670 01:27:01,390 --> 01:27:04,020 when you're in either the jumping, falling, or moving state, 1671 01:27:04,020 --> 01:27:08,285 so you should check for left and right tiles in all three of those states. 1672 01:27:08,285 --> 01:27:12,835 AUDIENCE: Shouldn't you always test for beneath you in case you get a chasm? 1673 01:27:12,835 --> 01:27:13,960 COLTON OGDEN: In case what? 1674 01:27:13,960 --> 01:27:15,430 AUDIENCE: In case you get a chasm. 1675 01:27:15,430 --> 01:27:17,138 COLTON OGDEN: In case you get chasm, yes. 1676 01:27:17,138 --> 01:27:21,210 1677 01:27:21,210 --> 01:27:21,930 You're correct. 1678 01:27:21,930 --> 01:27:24,660 This should actually be tested only when in the player 1679 01:27:24,660 --> 01:27:26,820 falling state and player walking state, yes. 1680 01:27:26,820 --> 01:27:30,690 So the question was, shouldn't you be testing for tiles beneath you 1681 01:27:30,690 --> 01:27:32,300 when you're walking? 1682 01:27:32,300 --> 01:27:35,274 And yes-- not just falling, but walking as well. 1683 01:27:35,274 --> 01:27:37,440 This one only jumping, this one falling and walking, 1684 01:27:37,440 --> 01:27:39,690 and this one for jumping, falling, and moving. 1685 01:27:39,690 --> 01:27:44,490 1686 01:27:44,490 --> 01:27:45,660 Does that make sense-- 1687 01:27:45,660 --> 01:27:49,890 how we can take the x, y and sort of turn that into a tile 1688 01:27:49,890 --> 01:27:52,410 by just dividing it by 16? 1689 01:27:52,410 --> 01:27:56,970 And do note the plus 1 as well, because our tiles in our self.tiles 1690 01:27:56,970 --> 01:27:58,530 are 1 indexed. 1691 01:27:58,530 --> 01:28:01,440 And so when we divide x, y by tile size, we're 1692 01:28:01,440 --> 01:28:05,160 going to get a 0 indexed coordinate. 1693 01:28:05,160 --> 01:28:10,930 If our x is at 14, we're within the first tile. 1694 01:28:10,930 --> 01:28:13,170 But if we divide that by 16, we're going to get zero. 1695 01:28:13,170 --> 01:28:16,086 So we need to add 1 to that so that we get the first tile in the array 1696 01:28:16,086 --> 01:28:20,280 still, which will be whatever that tile is. 1697 01:28:20,280 --> 01:28:23,100 So that's how the collision works. 1698 01:28:23,100 --> 01:28:27,390 It's all implemented in TileMap here. 1699 01:28:27,390 --> 01:28:36,170 And basically every state that the player is in, which is in StatesEntity, 1700 01:28:36,170 --> 01:28:38,870 and then player falling, idle, jump, and walking-- 1701 01:28:38,870 --> 01:28:43,590 these are all states that perform this check. 1702 01:28:43,590 --> 01:28:45,500 They basically do all the logic that's here 1703 01:28:45,500 --> 01:28:47,720 at the bottom, which is testing in the player 1704 01:28:47,720 --> 01:28:51,620 jumping state, falling state, and moving state for left or right collision. 1705 01:28:51,620 --> 01:28:55,192 And then in the falling state, we check for collision below us. 1706 01:28:55,192 --> 01:28:57,650 And then in jumping state, we check for collision above us. 1707 01:28:57,650 --> 01:29:01,670 That's all done within the states themselves. 1708 01:29:01,670 --> 01:29:05,089 But the actual transformation from pixels to tiles-- 1709 01:29:05,089 --> 01:29:07,130 that's just a function that we call from TileMap. 1710 01:29:07,130 --> 01:29:09,859 It's just a utility function. 1711 01:29:09,859 --> 01:29:11,650 AUDIENCE: What's the function called again? 1712 01:29:11,650 --> 01:29:13,460 COLTON OGDEN: It's called pointToTile. 1713 01:29:13,460 --> 01:29:18,170 So if you're in TileMap on line 27-- 1714 01:29:18,170 --> 01:29:19,430 pointToTile(x, y). 1715 01:29:19,430 --> 01:29:22,430 And the first little bit here is just the bit 1716 01:29:22,430 --> 01:29:25,910 that lets you basically go outside the map bounds 1717 01:29:25,910 --> 01:29:27,960 without getting a tile index error. 1718 01:29:27,960 --> 01:29:33,110 So if it's just outside the tile limits, less than 0, or greater than width, 1719 01:29:33,110 --> 01:29:34,100 just return nil. 1720 01:29:34,100 --> 01:29:35,870 And so you can do a check on nil to check 1721 01:29:35,870 --> 01:29:40,345 to see whether TileMap pointToTile is equal to nil 1722 01:29:40,345 --> 01:29:41,720 or not when you do the collision. 1723 01:29:41,720 --> 01:29:46,570 And if it is, then just don't do anything probably. 1724 01:29:46,570 --> 01:29:51,530 But assuming that you're within the tile boundaries, on line 32 1725 01:29:51,530 --> 01:29:54,691 is where you do that transformation-- the math.floor, recall, 1726 01:29:54,691 --> 01:29:56,690 because we want to get integer values for these. 1727 01:29:56,690 --> 01:29:58,856 We don't want to get fractional numbers, because you 1728 01:29:58,856 --> 01:30:03,220 can't index these tiles as fractional numbers, although I'm not sure. 1729 01:30:03,220 --> 01:30:06,650 I think you might be able to in Lua generally-- index a tile 1730 01:30:06,650 --> 01:30:07,740 by a fractional number. 1731 01:30:07,740 --> 01:30:10,290 But in this case, we just want integers. 1732 01:30:10,290 --> 01:30:14,440 So we call math.floor on y divided by TILE_SIZE plus 1, 1733 01:30:14,440 --> 01:30:16,730 y divided by TILE_SIZE then add 1 to that, 1734 01:30:16,730 --> 01:30:18,484 and then we do the same thing for the x. 1735 01:30:18,484 --> 01:30:20,122 So that's the operation. 1736 01:30:20,122 --> 01:30:23,330 And then wherever we want to check for whatever tiles we want to collide for, 1737 01:30:23,330 --> 01:30:28,070 we just call pointToTile on those x and y coordinates. 1738 01:30:28,070 --> 01:30:33,480 That's the backbone behind all the tile-based collision in the game 1739 01:30:33,480 --> 01:30:34,400 effectively. 1740 01:30:34,400 --> 01:30:37,630 1741 01:30:37,630 --> 01:30:41,490 Any questions as to how this works? 1742 01:30:41,490 --> 01:30:41,990 Yes. 1743 01:30:41,990 --> 01:30:45,742 AUDIENCE: So you're only detecting the collision of corners 1744 01:30:45,742 --> 01:30:47,620 and not the edge itself? 1745 01:30:47,620 --> 01:30:51,230 COLTON OGDEN: Correct, because you don't really need to check for the edge 1746 01:30:51,230 --> 01:30:53,830 if you're taking into consideration the top and bottom corner, 1747 01:30:53,830 --> 01:30:57,610 unless your entity is sufficiently tall that they need 1748 01:30:57,610 --> 01:31:00,340 to check for more than three tiles. 1749 01:31:00,340 --> 01:31:03,730 In this case, our entity is not more than two tiles tall, 1750 01:31:03,730 --> 01:31:08,556 so we only need to check for his top left, bottom left. 1751 01:31:08,556 --> 01:31:10,930 If we're doing a left collision, top right, bottom right. 1752 01:31:10,930 --> 01:31:16,330 If we're doing a right collision, his top left, top right for top and bottom 1753 01:31:16,330 --> 01:31:17,710 left, bottom right for bottom. 1754 01:31:17,710 --> 01:31:20,980 If you had an entity that was eight tiles tall, 1755 01:31:20,980 --> 01:31:26,330 you need to check every single tile along his right side, which 1756 01:31:26,330 --> 01:31:30,550 just means you need to iterate over his entire height divided by tile size. 1757 01:31:30,550 --> 01:31:34,450 And then just offset the y that you're checking for each of those tiles. 1758 01:31:34,450 --> 01:31:36,090 Does that makes sense? 1759 01:31:36,090 --> 01:31:38,860 OK, cool. 1760 01:31:38,860 --> 01:31:39,490 All right. 1761 01:31:39,490 --> 01:31:41,890 I alluded to this briefly by mentioning state. 1762 01:31:41,890 --> 01:31:45,340 1763 01:31:45,340 --> 01:31:48,340 I don't know if I alluded so much to the fact that we're using entities. 1764 01:31:48,340 --> 01:31:53,260 But in this distro, we're introduced to the concept of entities. 1765 01:31:53,260 --> 01:31:57,960 An entity can be almost anything you want it to be. 1766 01:31:57,960 --> 01:31:59,980 In this distro, we're considering entities 1767 01:31:59,980 --> 01:32:03,250 to basically be anything that's living or sentient moving around-- 1768 01:32:03,250 --> 01:32:04,900 in this case, the player or snails. 1769 01:32:04,900 --> 01:32:10,132 Those are entities, and then they just are subsets of entity. 1770 01:32:10,132 --> 01:32:11,590 An entity is a very abstract thing. 1771 01:32:11,590 --> 01:32:15,850 You'll see it in a lot of game engines and a lot of discussions 1772 01:32:15,850 --> 01:32:19,300 about how to organize your game and how to engineer it. 1773 01:32:19,300 --> 01:32:24,460 Unity is probably the most prominent adopter 1774 01:32:24,460 --> 01:32:27,460 of what's called the entity component system, whereby 1775 01:32:27,460 --> 01:32:29,350 you have everything in your game. 1776 01:32:29,350 --> 01:32:31,510 Every single thing in your game is an entity, 1777 01:32:31,510 --> 01:32:34,480 and then every entity is comprised of components. 1778 01:32:34,480 --> 01:32:37,960 And these components ultimately drive your behavior. 1779 01:32:37,960 --> 01:32:41,290 It's sort of like if you're familiar with composition over inheritance. 1780 01:32:41,290 --> 01:32:43,900 If you've heard of this as a software engineering thing, 1781 01:32:43,900 --> 01:32:45,460 that's effectively the same paradigm. 1782 01:32:45,460 --> 01:32:50,290 Rather than inherit a bunch of different things to be your-- 1783 01:32:50,290 --> 01:32:52,620 let's say you have a base monster class. 1784 01:32:52,620 --> 01:32:55,240 And then you have a goblin that's a subset of monster, 1785 01:32:55,240 --> 01:32:56,710 so it inherits from monster. 1786 01:32:56,710 --> 01:32:59,500 And then you have a goblin warlord who inherits from goblin, 1787 01:32:59,500 --> 01:33:02,950 and then you have an ancient goblin warlord that inherits from that. 1788 01:33:02,950 --> 01:33:06,270 Rather than have this nested tree of inheritance, 1789 01:33:06,270 --> 01:33:11,380 you adopt composition, which means you take a base container, 1790 01:33:11,380 --> 01:33:14,110 and then you fill it with different components that represent 1791 01:33:14,110 --> 01:33:16,510 what the behavior of your object is. 1792 01:33:16,510 --> 01:33:19,810 So if you have an entity-- 1793 01:33:19,810 --> 01:33:21,820 let's say you give it a monster component. 1794 01:33:21,820 --> 01:33:25,256 And then maybe you also give it an ancient component, 1795 01:33:25,256 --> 01:33:26,380 so it's an ancient monster. 1796 01:33:26,380 --> 01:33:28,210 And maybe you give it a goblin component, 1797 01:33:28,210 --> 01:33:32,022 so then it's an ancient monster goblin. 1798 01:33:32,022 --> 01:33:33,730 And then you give it a warlord component, 1799 01:33:33,730 --> 01:33:35,150 so it's an ancient goblin monster warlord. 1800 01:33:35,150 --> 01:33:36,940 So it has all the pieces that make it what 1801 01:33:36,940 --> 01:33:40,930 it is without you having to create this crazy chain of inheritance. 1802 01:33:40,930 --> 01:33:44,920 That's effectively what the model of an entity component system 1803 01:33:44,920 --> 01:33:48,820 is versus standard inheritance-- using that to drive 1804 01:33:48,820 --> 01:33:51,050 the model of your problem. 1805 01:33:51,050 --> 01:33:53,890 In this case, we're not going into crazy entity components. 1806 01:33:53,890 --> 01:33:56,020 But I wanted to bring it up, because Unity, 1807 01:33:56,020 --> 01:34:01,000 which we'll be covering in a few weeks, is entirely component-based. 1808 01:34:01,000 --> 01:34:03,550 Everything you write in Unity is a component. 1809 01:34:03,550 --> 01:34:08,140 And entities, whether they're in an entity component system or not, 1810 01:34:08,140 --> 01:34:10,570 form the backbone of most large games. 1811 01:34:10,570 --> 01:34:15,440 Most games that have some complexity to them 1812 01:34:15,440 --> 01:34:19,750 model most of the pieces within them as entities that have behaviors 1813 01:34:19,750 --> 01:34:21,260 and do things. 1814 01:34:21,260 --> 01:34:28,360 And so in this case, entities are snails and our player. 1815 01:34:28,360 --> 01:34:31,900 And then separate from the tiles-- 1816 01:34:31,900 --> 01:34:33,220 when we do collision for that-- 1817 01:34:33,220 --> 01:34:36,130 we want to also check collision on every entity with the player. 1818 01:34:36,130 --> 01:34:39,577 So we make sure that the player's collided with the snail in this case, 1819 01:34:39,577 --> 01:34:41,910 because that's the only other entities that they can be. 1820 01:34:41,910 --> 01:34:47,100 But you can have an arbitrary number of enemies if you want to. 1821 01:34:47,100 --> 01:34:49,970 If you collide with an entity-- so just a for loop. 1822 01:34:49,970 --> 01:34:54,940 So for entity in pairs of entities, check collision. 1823 01:34:54,940 --> 01:34:58,720 If you're in the jump state, then die. 1824 01:34:58,720 --> 01:35:02,410 If you're in the fall state, kill it, et cetera. 1825 01:35:02,410 --> 01:35:06,609 When you're doing most of your entity to entity interaction stuff, 1826 01:35:06,609 --> 01:35:08,150 that's generally how you'll model it. 1827 01:35:08,150 --> 01:35:11,497 You'll just iterate over everything and then just collide everything. 1828 01:35:11,497 --> 01:35:13,330 Depending on what collides with what, you'll 1829 01:35:13,330 --> 01:35:16,970 just collide everything with everything else and process interactions that way. 1830 01:35:16,970 --> 01:35:18,400 That's effectively how we do it. 1831 01:35:18,400 --> 01:35:20,496 We have in the-- 1832 01:35:20,496 --> 01:35:22,930 I believe it's in GameLevel. 1833 01:35:22,930 --> 01:35:28,350 This maintains a reference to a table full of entities, 1834 01:35:28,350 --> 01:35:29,830 a table full of objects. 1835 01:35:29,830 --> 01:35:32,770 Objects can be-- we'll talk about that in a second-- 1836 01:35:32,770 --> 01:35:35,800 gems, and blocks, and bushes, and stuff like that, and then a tile map. 1837 01:35:35,800 --> 01:35:40,820 1838 01:35:40,820 --> 01:35:43,400 For every entity, we just update it. 1839 01:35:43,400 --> 01:35:45,580 And then for every object, we update it. 1840 01:35:45,580 --> 01:35:49,020 And then for every object in objects, we render it as well. 1841 01:35:49,020 --> 01:35:50,660 And then we render every entity. 1842 01:35:50,660 --> 01:35:55,100 This is just sort of basic how you would take a game world, 1843 01:35:55,100 --> 01:35:57,320 populate it, and then process and update it. 1844 01:35:57,320 --> 01:36:02,270 Just containers, tables that maintain a bunch of references to everything, 1845 01:36:02,270 --> 01:36:03,830 and then just update them. 1846 01:36:03,830 --> 01:36:06,050 The actual interaction takes place in the-- 1847 01:36:06,050 --> 01:36:09,590 because they're dependent on what state we're in. 1848 01:36:09,590 --> 01:36:16,610 If you look at all the different states for the player in the states slash 1849 01:36:16,610 --> 01:36:23,040 entity folder, you'll see, for example, on line 62 of the player falling state, 1850 01:36:23,040 --> 01:36:28,390 we're iterating over every object in the level.objects. 1851 01:36:28,390 --> 01:36:31,400 And notice the player has a reference to its level 1852 01:36:31,400 --> 01:36:33,780 so that it can access everything within it. 1853 01:36:33,780 --> 01:36:36,170 And then within that level, all the objects are stored. 1854 01:36:36,170 --> 01:36:38,820 So all it needs to do is just say, if the object collides 1855 01:36:38,820 --> 01:36:43,400 the player and the object is solid, then set our dy to zero, 1856 01:36:43,400 --> 01:36:44,822 et cetera, et cetera. 1857 01:36:44,822 --> 01:36:47,030 All this code's actually pretty easy to read through, 1858 01:36:47,030 --> 01:36:49,010 so I would encourage you to take a look at it 1859 01:36:49,010 --> 01:36:51,800 and just understand how all the collision and stuff is 1860 01:36:51,800 --> 01:36:57,740 working between the player, the objects, the blocks, and things like that-- 1861 01:36:57,740 --> 01:37:01,900 things like blocks are solid, things like bushes are not solid. 1862 01:37:01,900 --> 01:37:05,210 But that's the gist. 1863 01:37:05,210 --> 01:37:07,670 Have a collection of objects or entities. 1864 01:37:07,670 --> 01:37:10,940 And then depending on what state you're in, collide with some. 1865 01:37:10,940 --> 01:37:14,540 And then depending on the state, maybe that kills you, 1866 01:37:14,540 --> 01:37:17,130 maybe that kills the enemy, maybe nothing happens, 1867 01:37:17,130 --> 01:37:18,350 maybe you become invincible. 1868 01:37:18,350 --> 01:37:21,200 Maybe you collide with a power-up game object, 1869 01:37:21,200 --> 01:37:26,570 and that power-up triggers your self.player.invincible is true. 1870 01:37:26,570 --> 01:37:29,430 And then if self.player.invincible is true, 1871 01:37:29,430 --> 01:37:32,060 then maybe you render him with a rainbow animation. 1872 01:37:32,060 --> 01:37:35,960 And then in any of the functions where he would collide and die with an enemy, 1873 01:37:35,960 --> 01:37:38,160 he no longer dies, he just kills them. 1874 01:37:38,160 --> 01:37:44,690 So that's sort of the gist behind how you would interact with objects 1875 01:37:44,690 --> 01:37:46,520 and how to process it. 1876 01:37:46,520 --> 01:37:50,160 Game objects are different. 1877 01:37:50,160 --> 01:37:54,470 Like I said earlier, these are examples of some of the objects here we can see. 1878 01:37:54,470 --> 01:37:58,100 The gems on the bottom left there are all in the distro. 1879 01:37:58,100 --> 01:38:00,740 If you hit a block-- and if we have a few minutes, 1880 01:38:00,740 --> 01:38:02,690 I'll show you really quickly how that works-- 1881 01:38:02,690 --> 01:38:06,590 if you hit a block, you'll have a chance to spawn a gem. 1882 01:38:06,590 --> 01:38:11,000 If you collect the gen-- which means if you collide with that game object-- 1883 01:38:11,000 --> 01:38:13,406 increment your score 100. 1884 01:38:13,406 --> 01:38:16,280 These are all other objects that I didn't have any time to implement. 1885 01:38:16,280 --> 01:38:21,366 But just off the gate, just as a mental exercise, 1886 01:38:21,366 --> 01:38:23,240 how do you think we could implement a ladder? 1887 01:38:23,240 --> 01:38:26,570 1888 01:38:26,570 --> 01:38:28,760 Yeah. 1889 01:38:28,760 --> 01:38:30,700 AUDIENCE: You would just have a climb state. 1890 01:38:30,700 --> 01:38:34,136 And if the player is touching a ladder and presses a certain key, 1891 01:38:34,136 --> 01:38:37,010 they would enter the climb state, and that would cause them to go up. 1892 01:38:37,010 --> 01:38:37,926 COLTON OGDEN: Correct. 1893 01:38:37,926 --> 01:38:41,990 So what Tony said was if they go onto a ladder, 1894 01:38:41,990 --> 01:38:43,820 they should go into a climb state. 1895 01:38:43,820 --> 01:38:45,861 And depending on whether they're in a climb state 1896 01:38:45,861 --> 01:38:49,540 or not, if they press a button, they should go up or down. 1897 01:38:49,540 --> 01:38:50,870 And then you would check. 1898 01:38:50,870 --> 01:38:54,140 If they're at the top of the ladder, get off the climb state, 1899 01:38:54,140 --> 01:38:56,060 go into a walk state. 1900 01:38:56,060 --> 01:38:59,375 Or if they're at the bottom of the ladder, go into a walk state. 1901 01:38:59,375 --> 01:39:02,000 And that's just another game object that you just collide with, 1902 01:39:02,000 --> 01:39:03,841 and then it's a new state for you. 1903 01:39:03,841 --> 01:39:04,340 Yes. 1904 01:39:04,340 --> 01:39:06,288 AUDIENCE: You may actually want it in a fall state, 1905 01:39:06,288 --> 01:39:08,885 because that way you could have a ladder that doesn't actually go anywhere, 1906 01:39:08,885 --> 01:39:09,926 it just gives you height. 1907 01:39:09,926 --> 01:39:13,481 But you could use that to jump over wide gaps. 1908 01:39:13,481 --> 01:39:15,230 COLTON OGDEN: What Tony said was you could 1909 01:39:15,230 --> 01:39:17,980 have the ability to jump off a ladder. 1910 01:39:17,980 --> 01:39:19,540 Is that what you Said Yeah. 1911 01:39:19,540 --> 01:39:21,920 The ability to jump off a ladder so that you can then use it as an obstacle. 1912 01:39:21,920 --> 01:39:22,878 That's absolutely true. 1913 01:39:22,878 --> 01:39:26,950 Actually, in the mock up that we saw up here, it's super hard to see. 1914 01:39:26,950 --> 01:39:28,925 I'll see if I can maybe zoom in on it here. 1915 01:39:28,925 --> 01:39:35,920 1916 01:39:35,920 --> 01:39:41,740 Mario, Mario, graphics, and then it's called full sheet. 1917 01:39:41,740 --> 01:39:46,470 The whole entire sheet that I used for this lecture is called fullsheet.png. 1918 01:39:46,470 --> 01:39:49,560 I don't know what that is. 1919 01:39:49,560 --> 01:39:56,886 So if you zoom in really high here, we can see effectively 1920 01:39:56,886 --> 01:39:58,010 what you were alluding to-- 1921 01:39:58,010 --> 01:40:01,181 right here, this little rope thing. 1922 01:40:01,181 --> 01:40:03,180 I'm guessing for the sake of this mock up that's 1923 01:40:03,180 --> 01:40:04,680 what they were trying to illustrate. 1924 01:40:04,680 --> 01:40:07,970 But you have a game object that lets you go into a climb state. 1925 01:40:07,970 --> 01:40:11,090 Whether it's a ladder or whether it's a rope, 1926 01:40:11,090 --> 01:40:15,636 just add a new state for the player. 1927 01:40:15,636 --> 01:40:18,260 If they're in that climb state, then we have this new animation 1928 01:40:18,260 --> 01:40:21,830 which we saw in the sheet earlier, which was their back or their front. 1929 01:40:21,830 --> 01:40:24,830 And then they just climb up it and just update it 1930 01:40:24,830 --> 01:40:27,881 if they're moving up or down the ladder. 1931 01:40:27,881 --> 01:40:29,880 And then just give them the ability to jump off. 1932 01:40:29,880 --> 01:40:32,255 And then when you get to the top or bottom, just get off. 1933 01:40:32,255 --> 01:40:33,950 1934 01:40:33,950 --> 01:40:37,580 And you could think a lot of the same thing with a lot of these obstacles, 1935 01:40:37,580 --> 01:40:39,410 like the spikes here. 1936 01:40:39,410 --> 01:40:43,410 If you're jumping and you hit it, you should probably die. 1937 01:40:43,410 --> 01:40:50,070 And so you would check for if the object.ID maybe is equal to spikes 1938 01:40:50,070 --> 01:40:55,230 or whether object.lethal equals true. 1939 01:40:55,230 --> 01:40:56,850 Same thing with this one. 1940 01:40:56,850 --> 01:41:01,410 And then some obstacles are completely cosmetic, like this mushroom here. 1941 01:41:01,410 --> 01:41:04,770 In the case of the distro, bushes and mushrooms and cacti 1942 01:41:04,770 --> 01:41:07,230 and all those sorts of things are just completely cosmetic, 1943 01:41:07,230 --> 01:41:08,310 so you can walk through them. 1944 01:41:08,310 --> 01:41:11,130 They don't trigger collision, but they're rendered as game objects. 1945 01:41:11,130 --> 01:41:12,546 They're not part of the tile grid. 1946 01:41:12,546 --> 01:41:17,180 1947 01:41:17,180 --> 01:41:19,560 They don't get processed in the same way as tiles. 1948 01:41:19,560 --> 01:41:20,910 They're not stored in the y, x. 1949 01:41:20,910 --> 01:41:24,930 1950 01:41:24,930 --> 01:41:27,760 So that's effectively how we can start thinking about objects 1951 01:41:27,760 --> 01:41:29,010 and how to give them behavior. 1952 01:41:29,010 --> 01:41:32,400 Part of the assignment is going to be adding a flag. 1953 01:41:32,400 --> 01:41:34,300 So this flag is in the sprite sheet. 1954 01:41:34,300 --> 01:41:36,150 So what you'll do-- 1955 01:41:36,150 --> 01:41:38,760 and I'll touch on this at the end of the lecture here. 1956 01:41:38,760 --> 01:41:41,910 We're getting close to it. 1957 01:41:41,910 --> 01:41:44,490 These keys here actually at the bottom right-- 1958 01:41:44,490 --> 01:41:47,150 so part of the assignment will be to-- 1959 01:41:47,150 --> 01:41:48,430 it's actually right here. 1960 01:41:48,430 --> 01:41:50,040 So I'll go over it really quickly. 1961 01:41:50,040 --> 01:41:52,320 Ensure the player always starts above solid land. 1962 01:41:52,320 --> 01:41:56,830 So in this case, when James came up here and ran, you ran the first example. 1963 01:41:56,830 --> 01:41:58,770 The very first time that we spawned the game, 1964 01:41:58,770 --> 01:42:06,180 it generated a chasm right where the player spawned at x1. 1965 01:42:06,180 --> 01:42:08,480 And so he just falls to his death if that happens. 1966 01:42:08,480 --> 01:42:11,332 1967 01:42:11,332 --> 01:42:13,290 Just right off the gate, anybody have any ideas 1968 01:42:13,290 --> 01:42:16,920 as to what we could do to check to see if we're at solid land, 1969 01:42:16,920 --> 01:42:21,321 assuming that the player's default start is at x1? 1970 01:42:21,321 --> 01:42:24,243 AUDIENCE: At that tile, check if it's solid. 1971 01:42:24,243 --> 01:42:27,824 If not, then just move it there over x until it's true. 1972 01:42:27,824 --> 01:42:28,615 COLTON OGDEN: Yeah. 1973 01:42:28,615 --> 01:42:31,590 What we probably want to do is look all the way down the column, 1974 01:42:31,590 --> 01:42:36,400 because we start towards the top. 1975 01:42:36,400 --> 01:42:39,610 If we find that there's no tiles down there-- it's just pure chasm-- 1976 01:42:39,610 --> 01:42:42,660 we probably want to shift the player. 1977 01:42:42,660 --> 01:42:45,720 And then random keys and locks-- 1978 01:42:45,720 --> 01:42:50,760 let me open up LevelMaker so we can see what you'll be interacting with, 1979 01:42:50,760 --> 01:42:53,940 because most of what you'll be doing actually is in LevelMaker. 1980 01:42:53,940 --> 01:42:58,080 1981 01:42:58,080 --> 01:43:01,830 It does a lot of what we did before with just math.random, 1982 01:43:01,830 --> 01:43:05,670 and then it will insert into objects. 1983 01:43:05,670 --> 01:43:10,290 So objects is a table here. 1984 01:43:10,290 --> 01:43:14,410 It will insert a game object depending on some logic. 1985 01:43:14,410 --> 01:43:17,627 So in this case, if we're generating a pillar, 1986 01:43:17,627 --> 01:43:19,710 we have a chance to generate a bush on the pillar. 1987 01:43:19,710 --> 01:43:23,940 So if math.random(8) is 1, in this case, we're already generating a pillar. 1988 01:43:23,940 --> 01:43:26,935 So we have an additional chance that's on top of the chance 1989 01:43:26,935 --> 01:43:27,960 to generated a pillar-- 1990 01:43:27,960 --> 01:43:33,180 so basically, I think it's a 1 in 64 chance on that particular iteration 1991 01:43:33,180 --> 01:43:36,170 to generate a pillar with a bush. 1992 01:43:36,170 --> 01:43:39,010 1993 01:43:39,010 --> 01:43:42,330 You just add a new game object to objects. 1994 01:43:42,330 --> 01:43:44,710 In this case, this is the constructor for a game object. 1995 01:43:44,710 --> 01:43:48,420 You give it an x, y, width, height, and then a frame. 1996 01:43:48,420 --> 01:43:52,230 And then the frame is relative to whatever quad table matches the texture 1997 01:43:52,230 --> 01:43:52,770 string here. 1998 01:43:52,770 --> 01:43:56,185 So bushes is the texture, and so whatever quad in bushes 1999 01:43:56,185 --> 01:43:57,060 you want to give it-- 2000 01:43:57,060 --> 01:44:02,340 in this case, we just gave it a random frame from that. 2001 01:44:02,340 --> 01:44:05,600 And then a lot of the same logic applies to other parts. 2002 01:44:05,600 --> 01:44:08,550 This is another part where we generate bushes just on flat land. 2003 01:44:08,550 --> 01:44:10,240 We have a chance to generate a block-- 2004 01:44:10,240 --> 01:44:13,330 1 in 10 chance this is a jump block. 2005 01:44:13,330 --> 01:44:16,350 So here we have texture, x, y, width, height, frame. 2006 01:44:16,350 --> 01:44:18,570 Notice that we have collidable is true, and this 2007 01:44:18,570 --> 01:44:22,980 is how we can test to see whether a tile is collidable or not. 2008 01:44:22,980 --> 01:44:26,140 Hit is false, meaning that we haven't hit it yet. 2009 01:44:26,140 --> 01:44:33,870 And if we have hit it, then we do this code basically-- onCollide gets called. 2010 01:44:33,870 --> 01:44:37,170 2011 01:44:37,170 --> 01:44:40,865 You can see where this gets called in the collision code for-- 2012 01:44:40,865 --> 01:44:43,800 2013 01:44:43,800 --> 01:44:45,165 if we look in Player. 2014 01:44:45,165 --> 01:44:52,800 2015 01:44:52,800 --> 01:44:55,200 Player has check left collisions, check right collisions, 2016 01:44:55,200 --> 01:44:59,280 and check object collisions. 2017 01:44:59,280 --> 01:45:02,145 It doesn't have check up and down collisions. 2018 01:45:02,145 --> 01:45:04,020 There is a corner case for both of those such 2019 01:45:04,020 --> 01:45:05,519 that the logic had to be duplicated. 2020 01:45:05,519 --> 01:45:06,960 I forget exactly why. 2021 01:45:06,960 --> 01:45:10,550 But you basically get a list of objects that you check for. 2022 01:45:10,550 --> 01:45:15,840 2023 01:45:15,840 --> 01:45:17,856 Oh, the reason why is because when you get 2024 01:45:17,856 --> 01:45:19,980 the collided objects when you're in the jump state, 2025 01:45:19,980 --> 01:45:22,840 you trigger the onCollide function. 2026 01:45:22,840 --> 01:45:26,560 So let's go to PlayerJumpState. 2027 01:45:26,560 --> 01:45:34,434 If we're in the jump state, this is where we would basically 2028 01:45:34,434 --> 01:45:37,350 check to see if we've gotten any objects that collide with the player. 2029 01:45:37,350 --> 01:45:41,690 If it's solid, call its onCollide function, object.onCollide, and then 2030 01:45:41,690 --> 01:45:44,720 we just pass in the object itself, basically. 2031 01:45:44,720 --> 01:45:48,100 And so if you go back to LevelMaker, that's 2032 01:45:48,100 --> 01:45:50,290 where we write the onCollide function. 2033 01:45:50,290 --> 01:45:55,000 We write the onCollide function within the game object here. 2034 01:45:55,000 --> 01:45:56,750 So we just give it an onCollide, remember, 2035 01:45:56,750 --> 01:45:58,940 because functions are first class citizens. 2036 01:45:58,940 --> 01:46:03,000 We can just say onCollide gets function obj, 2037 01:46:03,000 --> 01:46:06,440 where obj is going to be this object. 2038 01:46:06,440 --> 01:46:12,130 If it wasn't hit already, one in five chance to spawn a gem-- 2039 01:46:12,130 --> 01:46:14,260 so going to create a gem. 2040 01:46:14,260 --> 01:46:16,320 It's got all the same stuff in it. 2041 01:46:16,320 --> 01:46:20,710 In this case, it has its own function called onConsume. 2042 01:46:20,710 --> 01:46:24,289 onConsume takes a player and an object. 2043 01:46:24,289 --> 01:46:26,080 And then this is all arbitrary, by the way. 2044 01:46:26,080 --> 01:46:27,980 You can create whatever functions you want. 2045 01:46:27,980 --> 01:46:31,570 These are callback functions, effectively. 2046 01:46:31,570 --> 01:46:35,050 We're just going to play the pickup sound and then add 100 to our score. 2047 01:46:35,050 --> 01:46:38,830 And then here, in the event that we did get a gem, 2048 01:46:38,830 --> 01:46:41,920 we tween it over the course of 0.1 seconds. 2049 01:46:41,920 --> 01:46:46,040 We tween its y to be from below the block to up above, 2050 01:46:46,040 --> 01:46:49,070 so it has an upwards animation, effectively. 2051 01:46:49,070 --> 01:46:50,820 And then we have another sound that plays. 2052 01:46:50,820 --> 01:46:53,111 But that's effectively how we're spawning game objects. 2053 01:46:53,111 --> 01:46:56,787 Game objects have textures, x, y, width, height, and then 2054 01:46:56,787 --> 01:46:58,870 you can give them callback functions that you then 2055 01:46:58,870 --> 01:47:01,360 execute wherever it's relevant to you. 2056 01:47:01,360 --> 01:47:05,650 In this case, you'll only really need to worry about onCollide, 2057 01:47:05,650 --> 01:47:12,310 because the assignment is create random keys and locks. 2058 01:47:12,310 --> 01:47:15,380 They have to be the same color, but you can choose them at random. 2059 01:47:15,380 --> 01:47:18,190 If the player collides with the key, then he 2060 01:47:18,190 --> 01:47:21,820 should probably get some flag that's like key obtained is true 2061 01:47:21,820 --> 01:47:23,140 or something like that. 2062 01:47:23,140 --> 01:47:27,900 And then you go to the block that spawns in the level, 2063 01:47:27,900 --> 01:47:31,150 so you should spawn a block with that same color. 2064 01:47:31,150 --> 01:47:37,300 And then on collide, you should unlock it, so get rid of the block 2065 01:47:37,300 --> 01:47:40,900 and then spawn a new game object-- 2066 01:47:40,900 --> 01:47:42,250 the flag. 2067 01:47:42,250 --> 01:47:44,770 And then that flag will have its own on collide. 2068 01:47:44,770 --> 01:47:47,830 And when you collide with the flag, restart the level. 2069 01:47:47,830 --> 01:47:51,040 And that's effectively the gist behind the problem set. 2070 01:47:51,040 --> 01:47:52,720 So it probably shouldn't take-- 2071 01:47:52,720 --> 01:47:57,570 I would say probably maybe 40 or 50 lines of code probably should do it. 2072 01:47:57,570 --> 01:47:59,530 AUDIENCE: That game object-- 2073 01:47:59,530 --> 01:48:01,762 was that a class? 2074 01:48:01,762 --> 01:48:02,470 It's not a table. 2075 01:48:02,470 --> 01:48:03,460 What is that? 2076 01:48:03,460 --> 01:48:05,200 COLTON OGDEN: It is a class. 2077 01:48:05,200 --> 01:48:06,460 There's a GameObject class. 2078 01:48:06,460 --> 01:48:11,290 A game object is basically-- and I realize I didn't touch on it too much. 2079 01:48:11,290 --> 01:48:18,760 In the context of this distro, you could almost think of it as an entity. 2080 01:48:18,760 --> 01:48:21,640 In this case, what I've done is I've differentiated 2081 01:48:21,640 --> 01:48:24,040 between living things and non-living things 2082 01:48:24,040 --> 01:48:28,960 as being entities versus game objects, which is a semi-arbitrary distinction. 2083 01:48:28,960 --> 01:48:31,870 But for a small project like this, it makes sense. 2084 01:48:31,870 --> 01:48:37,390 For a large project, I would probably create everything as an entity 2085 01:48:37,390 --> 01:48:39,820 and then give different kinds of entities 2086 01:48:39,820 --> 01:48:41,654 their own behavior and their own components, 2087 01:48:41,654 --> 01:48:43,986 sort of like how you do with an entity component system. 2088 01:48:43,986 --> 01:48:46,305 AUDIENCE: Are there two ways to create a class in Lua, 2089 01:48:46,305 --> 01:48:50,530 one with the curly brackets and one with regular parentheses? 2090 01:48:50,530 --> 01:48:53,510 COLTON OGDEN: There is, actually. 2091 01:48:53,510 --> 01:48:56,650 So the question was, is there more than one way to create a class in Lua 2092 01:48:56,650 --> 01:48:59,690 whether it's parentheses or curly brackets? 2093 01:48:59,690 --> 01:49:00,190 Yes. 2094 01:49:00,190 --> 01:49:02,315 I don't think I've ever actually talked about this. 2095 01:49:02,315 --> 01:49:05,430 2096 01:49:05,430 --> 01:49:08,290 Let's go back to LevelMaker. 2097 01:49:08,290 --> 01:49:12,550 If you instantiate a class and that class 2098 01:49:12,550 --> 01:49:22,930 takes in just a table as its only argument, you can just pass in this. 2099 01:49:22,930 --> 01:49:25,790 This effectively is that argument table. 2100 01:49:25,790 --> 01:49:27,400 You don't need parentheses. 2101 01:49:27,400 --> 01:49:30,141 It's effectively doing this-- 2102 01:49:30,141 --> 01:49:32,140 same thing, only you don't need the parentheses. 2103 01:49:32,140 --> 01:49:35,220 AUDIENCE: And then that's a table that's being passed in? 2104 01:49:35,220 --> 01:49:37,820 COLTON OGDEN: Correct. 2105 01:49:37,820 --> 01:49:40,840 It's just an alternative form of instantiation 2106 01:49:40,840 --> 01:49:44,394 on things that only take a table as their argument for when 2107 01:49:44,394 --> 01:49:45,310 they get instantiated. 2108 01:49:45,310 --> 01:49:47,680 AUDIENCE: And you can only have one table in that case? 2109 01:49:47,680 --> 01:49:49,935 COLTON OGDEN: Correct, yes. 2110 01:49:49,935 --> 01:49:53,572 AUDIENCE: Wouldn't it be easier to create a new class which 2111 01:49:53,572 --> 01:49:59,392 would have its own set of game objects so you 2112 01:49:59,392 --> 01:50:04,260 would create a gem which would be helper dot gem, which 2113 01:50:04,260 --> 01:50:06,754 would in turn create a game object? 2114 01:50:06,754 --> 01:50:08,628 COLTON OGDEN: Can you say that one more time? 2115 01:50:08,628 --> 01:50:10,500 AUDIENCE: It's kind of hard to explain. 2116 01:50:10,500 --> 01:50:15,420 Wouldn't it be easier to create another class which 2117 01:50:15,420 --> 01:50:19,028 would have your gem and everything, and your gem in that class 2118 01:50:19,028 --> 01:50:20,504 would be a game object? 2119 01:50:20,504 --> 01:50:22,472 But in this class, when you wanted a gem, 2120 01:50:22,472 --> 01:50:27,400 you would say local gem equals helper class dot gem. 2121 01:50:27,400 --> 01:50:30,750 COLTON OGDEN: The question was, wouldn't it 2122 01:50:30,750 --> 01:50:37,120 be easier to create a helper class that would allow you to instantiate gems? 2123 01:50:37,120 --> 01:50:37,620 Possibly. 2124 01:50:37,620 --> 01:50:40,260 2125 01:50:40,260 --> 01:50:45,690 I think if you were going to design this a little bit more robustly, 2126 01:50:45,690 --> 01:50:47,490 and if this were going to be a larger game, 2127 01:50:47,490 --> 01:50:51,539 then you would just create a subclass for blocks, gems, et cetera. 2128 01:50:51,539 --> 01:50:54,330 To shrink the number of files that we had in the distro and to sort 2129 01:50:54,330 --> 01:50:59,040 of consolidate everything together and put all of the level code together 2130 01:50:59,040 --> 01:51:04,440 in one spot, I decided to just create GameObject as an abstract class that 2131 01:51:04,440 --> 01:51:07,950 you could then just create your own behavior for within the actual 2132 01:51:07,950 --> 01:51:11,340 constructor-- which is this bit here, which is just the table-- 2133 01:51:11,340 --> 01:51:14,742 and then allow you to override the onCollide and onConsume functions. 2134 01:51:14,742 --> 01:51:16,950 You can actually give it whatever functions you want. 2135 01:51:16,950 --> 01:51:22,540 You could give this some arbitrary named function and then test for it later. 2136 01:51:22,540 --> 01:51:26,490 This is almost like an obscure way of inheritance. 2137 01:51:26,490 --> 01:51:31,980 But I think if I were to engineer this with the goal of making it a really 2138 01:51:31,980 --> 01:51:34,260 large game, I would just subclass. 2139 01:51:34,260 --> 01:51:38,850 I would just create a class for gem, a class for block, a class for bush, 2140 01:51:38,850 --> 01:51:40,830 et cetera, et cetera. 2141 01:51:40,830 --> 01:51:43,170 It wasn't strictly necessary for this example, 2142 01:51:43,170 --> 01:51:45,090 so we ended up keeping everything a little bit 2143 01:51:45,090 --> 01:51:52,470 more abstract in a sense-- a little bit more general purpose. 2144 01:51:52,470 --> 01:51:57,790 But yeah, you could definitely create classes for those. 2145 01:51:57,790 --> 01:52:00,060 And if you were in an entity component system, 2146 01:52:00,060 --> 01:52:03,405 you could have a consumable component. 2147 01:52:03,405 --> 01:52:05,280 And then that consumable component would then 2148 01:52:05,280 --> 01:52:09,510 allow you to give it some sort of behavior that affects the player when 2149 01:52:09,510 --> 01:52:11,221 the player consumes that object. 2150 01:52:11,221 --> 01:52:13,470 In this case, a gem is a consumable, so you would just 2151 01:52:13,470 --> 01:52:16,290 give it a consumable component with a texture of the gem 2152 01:52:16,290 --> 01:52:18,510 and then give it a callback function that 2153 01:52:18,510 --> 01:52:20,025 just increments the player's score. 2154 01:52:20,025 --> 01:52:24,060 You could probably put that in 10 or 15 lines of code. 2155 01:52:24,060 --> 01:52:25,330 It would be pretty easy. 2156 01:52:25,330 --> 01:52:29,680 And then blocks would be a spawner component, 2157 01:52:29,680 --> 01:52:31,170 so they have a chance to spawn. 2158 01:52:31,170 --> 01:52:36,560 And then you would pass in that spawner component a gem maybe, 2159 01:52:36,560 --> 01:52:40,560 so it would have a chance to spawn the gem that you passed into the spawner 2160 01:52:40,560 --> 01:52:46,030 component-- and then also a solid component to say, oh, this is solid. 2161 01:52:46,030 --> 01:52:48,102 So if I hit it, I should trigger a collision 2162 01:52:48,102 --> 01:52:49,560 and not be able to walk through it. 2163 01:52:49,560 --> 01:52:53,760 So you just layer on these components. 2164 01:52:53,760 --> 01:52:58,650 I would encourage you to think about this way of composing 2165 01:52:58,650 --> 01:53:02,340 your objects a little bit, particularly as we get towards Unity, which 2166 01:53:02,340 --> 01:53:07,560 makes a lot of use of this concept. 2167 01:53:07,560 --> 01:53:10,290 In short, yes. 2168 01:53:10,290 --> 01:53:12,326 I think that's pretty much everything. 2169 01:53:12,326 --> 01:53:13,200 Let me just go ahead. 2170 01:53:13,200 --> 01:53:18,090 We're running out of time here, but like I said, one more time-- 2171 01:53:18,090 --> 01:53:21,504 make sure the player starts above solid land, random color key and lock, 2172 01:53:21,504 --> 01:53:24,420 and then make sure that when you get the key, you can unlock the lock, 2173 01:53:24,420 --> 01:53:25,920 and that spawns the goal. 2174 01:53:25,920 --> 01:53:30,540 So this is all something you can just add to the LevelMaker class, 2175 01:53:30,540 --> 01:53:35,620 and it will all work with your game level that way. 2176 01:53:35,620 --> 01:53:39,210 And then you touch the goal flag, then respawn the level. 2177 01:53:39,210 --> 01:53:41,520 So today, we talked about Super Mario Bros. 2178 01:53:41,520 --> 01:53:44,850 The other big Nintendo game of that era-- 2179 01:53:44,850 --> 01:53:47,700 arguably one of the greatest of all time-- is Zelda. 2180 01:53:47,700 --> 01:53:51,840 So we'll be talking about a very simple Legend of Zelda game, 2181 01:53:51,840 --> 01:53:54,810 where we just have a random dungeon that we can go through, 2182 01:53:54,810 --> 01:53:57,480 a top down perspective, fight simple monsters, 2183 01:53:57,480 --> 01:54:00,780 open chests, blow up walls-- that sort of thing. 2184 01:54:00,780 --> 01:54:02,880 We'll talk about triggers and events. 2185 01:54:02,880 --> 01:54:06,530 And then we'll talk about hurt boxes, inventory, 2186 01:54:06,530 --> 01:54:09,950 a very simple GUI for opening up a menu, and then world states 2187 01:54:09,950 --> 01:54:13,710 so that we can see which doors have been blasted open so that they 2188 01:54:13,710 --> 01:54:15,840 render appropriately and whatnot. 2189 01:54:15,840 --> 01:54:16,970 And that's it for Mario. 2190 01:54:16,970 --> 01:54:17,970 Thanks a lot for coming. 2191 01:54:17,970 --> 01:54:20,120 I'll see you guys next time. 2192 01:54:20,120 --> 01:54:21,008