1 00:00:00,000 --> 00:00:16,660 2 00:00:16,660 --> 00:00:18,000 COLTON OGDEN: Hi, everybody. 3 00:00:18,000 --> 00:00:21,740 My name is Colton Ogden, and this is GD50 lecture one, 4 00:00:21,740 --> 00:00:24,480 and today we'll be covering Flappy Bird. 5 00:00:24,480 --> 00:00:27,810 So last year or last week, sorry, we covered Pong, 6 00:00:27,810 --> 00:00:30,730 which was just basic shapes and colors. 7 00:00:30,730 --> 00:00:32,640 Today we'll actually be diving into sprites. 8 00:00:32,640 --> 00:00:35,010 As we can see here, we've got some pipes, and a bird, 9 00:00:35,010 --> 00:00:39,540 and we're covering a few other concepts such as gravity, and more. 10 00:00:39,540 --> 00:00:43,140 Today, the topics that we'll be covering are in a nutshell, images and sprites, 11 00:00:43,140 --> 00:00:43,860 as I just said. 12 00:00:43,860 --> 00:00:46,530 So loading images from memory from our hard drive, 13 00:00:46,530 --> 00:00:50,040 and actually drawing them to the screen instead of just rectangles, 14 00:00:50,040 --> 00:00:50,825 and whatnot. 15 00:00:50,825 --> 00:00:52,560 We'll be covering infinite scrolling. 16 00:00:52,560 --> 00:00:55,170 So seeing things like-- and if you've played the game, 17 00:00:55,170 --> 00:00:57,690 pipes are infinitely going from right to left. 18 00:00:57,690 --> 00:01:00,240 How to actually get that going infinitely so that we're not 19 00:01:00,240 --> 00:01:02,910 using up also infinite memory. 20 00:01:02,910 --> 00:01:05,489 We'll be discussing how games, and in the similar vein, 21 00:01:05,489 --> 00:01:10,710 are illusions in the sense that a lot of the perceived vastness and perceived 22 00:01:10,710 --> 00:01:14,520 complexity of games is often just due to camera trickery, 23 00:01:14,520 --> 00:01:17,650 and more because of limited hardware. 24 00:01:17,650 --> 00:01:19,860 We'll be covering procedural generation, which 25 00:01:19,860 --> 00:01:21,854 ties also into infinite scrolling. 26 00:01:21,854 --> 00:01:24,770 Procedure generation is a topic that I am actually very interested in, 27 00:01:24,770 --> 00:01:27,720 and will be touching on it throughout the course in several locations. 28 00:01:27,720 --> 00:01:32,190 But in the context of today's lecture, we'll be using it for the pipes 29 00:01:32,190 --> 00:01:34,966 because the pipes, they spawn from right to left in Flappy Bird 30 00:01:34,966 --> 00:01:36,840 as you're infinitely going through the level, 31 00:01:36,840 --> 00:01:39,900 but they can spawn at various heights, and the gaps 32 00:01:39,900 --> 00:01:44,640 are shifting as a result of that, therefore creating this infinite level. 33 00:01:44,640 --> 00:01:46,744 We'll be talking more in detail on state machines. 34 00:01:46,744 --> 00:01:49,410 So last week we covered state machines in a very abstract sense. 35 00:01:49,410 --> 00:01:53,040 We used just basically a string as a variable, and then used if conditions. 36 00:01:53,040 --> 00:01:55,170 Today we'll be actually using a state machine 37 00:01:55,170 --> 00:01:57,870 class replete with various methods that allow 38 00:01:57,870 --> 00:02:00,210 us to transition in and out of these states very 39 00:02:00,210 --> 00:02:02,700 cleanly, and allow us to break out all of this logic 40 00:02:02,700 --> 00:02:05,700 that we previously had in our update and render functions, 41 00:02:05,700 --> 00:02:09,600 and then put them separately into their own state classes. 42 00:02:09,600 --> 00:02:12,060 And then lastly, we'll also be touching on mouse input. 43 00:02:12,060 --> 00:02:15,010 And a point that I forgot to mention here, 44 00:02:15,010 --> 00:02:20,070 whoops, is also we'll be talking about music, which is just basically 45 00:02:20,070 --> 00:02:21,960 sound, which we did last week. 46 00:02:21,960 --> 00:02:24,369 But we'll add that as a polishing touch. 47 00:02:24,369 --> 00:02:26,160 If you guys want to download the demo code. 48 00:02:26,160 --> 00:02:31,410 We have a repo up right now on GitHub/games50/fiftybirds. 49 00:02:31,410 --> 00:02:34,440 It's our take on Flappy Bird. 50 00:02:34,440 --> 00:02:36,700 A couple of things, I've been asked a couple of times 51 00:02:36,700 --> 00:02:39,170 whether we have reading materials for the course. 52 00:02:39,170 --> 00:02:40,920 And there are no formal reading materials, 53 00:02:40,920 --> 00:02:42,961 but there are a couple of resources that I really 54 00:02:42,961 --> 00:02:46,890 enjoyed reading, especially as I was getting more into Lua and Love2D. 55 00:02:46,890 --> 00:02:48,720 They are two books. 56 00:02:48,720 --> 00:02:49,800 One is an online book. 57 00:02:49,800 --> 00:02:52,470 Actually, they're both online books, but the latter of which 58 00:02:52,470 --> 00:02:54,180 has a physical form as well. 59 00:02:54,180 --> 00:02:57,360 The first of these is How to Make an RPG by Dan Schoeller, which 60 00:02:57,360 --> 00:02:59,820 is actually completely written in Lua. 61 00:02:59,820 --> 00:03:03,330 He uses a custom game engine very similar to Love2D, 62 00:03:03,330 --> 00:03:04,910 but it's handwritten by him. 63 00:03:04,910 --> 00:03:07,629 But a lot of the same ideas apply, and it's a great opportunity. 64 00:03:07,629 --> 00:03:09,420 It's how I cut my teeth on Lua, and I would 65 00:03:09,420 --> 00:03:12,669 encourage you to take a look at that if that's something you're interested in, 66 00:03:12,669 --> 00:03:14,310 or if you like RPGs. 67 00:03:14,310 --> 00:03:17,070 And then also Game Programming Patterns by Robert Nystrom 68 00:03:17,070 --> 00:03:20,220 is a very great general purpose game development 69 00:03:20,220 --> 00:03:23,820 book that talks about a lot of the sort of more abstract high level concepts 70 00:03:23,820 --> 00:03:25,440 with large scale game development. 71 00:03:25,440 --> 00:03:27,006 But beyond that, no formal reading. 72 00:03:27,006 --> 00:03:28,380 Those aren't from reading either. 73 00:03:28,380 --> 00:03:29,730 Those are just if you're curious, and you 74 00:03:29,730 --> 00:03:32,146 want to read some resources that I found very interesting. 75 00:03:32,146 --> 00:03:33,300 Feel free to do so. 76 00:03:33,300 --> 00:03:36,660 Today's goal is to implement what looks like this. 77 00:03:36,660 --> 00:03:39,150 This is our version of Flappy Bird. 78 00:03:39,150 --> 00:03:41,790 We didn't use the same exact sprites for copyright purposes, 79 00:03:41,790 --> 00:03:45,640 but we note that we have a bird in the middle of the screen. 80 00:03:45,640 --> 00:03:49,410 This bird, on click, or on spacebar, will jump up and down, 81 00:03:49,410 --> 00:03:51,660 and your goal is to prevent the bird from touching 82 00:03:51,660 --> 00:03:55,380 either the pipes or the ground itself. 83 00:03:55,380 --> 00:03:58,920 Every time you make it past a pair of pipes, you will score a point. 84 00:03:58,920 --> 00:04:01,590 As soon as you touch a pipe or hit the ground, the game is over, 85 00:04:01,590 --> 00:04:03,700 and that's that. 86 00:04:03,700 --> 00:04:05,907 So today we'll be covering-- 87 00:04:05,907 --> 00:04:07,740 I'll be doing a little bit more live coding. 88 00:04:07,740 --> 00:04:09,750 So the very first example that I want to cover 89 00:04:09,750 --> 00:04:12,660 is the day zero update for Flappy Bird. 90 00:04:12,660 --> 00:04:15,540 And a important function that is going to be probably 91 00:04:15,540 --> 00:04:18,149 the most noticeable, the most visibly obvious function we'll 92 00:04:18,149 --> 00:04:23,010 be using throughout this lecture, is love.graphics.newImage, 93 00:04:23,010 --> 00:04:24,360 which takes a path. 94 00:04:24,360 --> 00:04:28,320 This function, all it does is load a image file from your disk. 95 00:04:28,320 --> 00:04:32,640 You specify it as a string, and you can then use it as an object, 96 00:04:32,640 --> 00:04:35,250 and draw anywhere you want at an xy coordinate, 97 00:04:35,250 --> 00:04:37,210 and we'll see this in practice here. 98 00:04:37,210 --> 00:04:38,550 So I'm going to go ahead. 99 00:04:38,550 --> 00:04:42,280 If you're looking in the repo, all of these examples are covered-- 100 00:04:42,280 --> 00:04:44,640 0 through 12. 101 00:04:44,640 --> 00:04:48,210 I'm going to start from scratch in a new folder that I've created. 102 00:04:48,210 --> 00:04:53,250 I'm going to create a brand new main.lua, completely fresh. 103 00:04:53,250 --> 00:04:57,000 And the first thing I want to do is because we 104 00:04:57,000 --> 00:04:59,970 are going to use a virtual resolution just like we did last week, 105 00:04:59,970 --> 00:05:01,860 so that we have a more rhetoric, I'm going 106 00:05:01,860 --> 00:05:03,568 to go ahead and require the push library. 107 00:05:03,568 --> 00:05:05,570 So push equals require. 108 00:05:05,570 --> 00:05:06,720 Push just like that. 109 00:05:06,720 --> 00:05:09,780 I've pre put push.lua into this directory. 110 00:05:09,780 --> 00:05:12,240 It'll just load by default in the same directory-- 111 00:05:12,240 --> 00:05:15,724 the current working directory of your script when you run Love. 112 00:05:15,724 --> 00:05:18,515 The next thing I'm going to do, I'm going to define some constants. 113 00:05:18,515 --> 00:05:23,910 So window with should be 1280, and then window height is going to be 720. 114 00:05:23,910 --> 00:05:26,100 Those are our physical window dimensions, 115 00:05:26,100 --> 00:05:30,960 but then we also need a virtual width, and we're going to use 512 by 288. 116 00:05:30,960 --> 00:05:35,040 This is a resolution that I found worked pretty well for the assets we'll 117 00:05:35,040 --> 00:05:38,970 be using today, but you can make this most anything you want to as long 118 00:05:38,970 --> 00:05:40,570 as it's somewhere in that range. 119 00:05:40,570 --> 00:05:43,260 It is a 16 by 9 resolution as well, so that it fits comfortably 120 00:05:43,260 --> 00:05:48,362 on modern wide screen 16 by 9 monitors. 121 00:05:48,362 --> 00:05:51,390 What we're going to do is the first goal that we have today 122 00:05:51,390 --> 00:05:55,530 is to draw two images to the screen. 123 00:05:55,530 --> 00:05:58,020 We want a foreground and a background because notice, 124 00:05:58,020 --> 00:06:01,540 if we go back to the slides, we can see in the very background 125 00:06:01,540 --> 00:06:08,700 we have a hill landscape, and then on the bottom we have a ground. 126 00:06:08,700 --> 00:06:12,150 The two of those are going to eventually scroll at different rates. 127 00:06:12,150 --> 00:06:14,396 It's going to be called parallax scrolling, but just 128 00:06:14,396 --> 00:06:16,770 for our very first example, we want something very basic. 129 00:06:16,770 --> 00:06:18,760 I just want to draw two images to the screen. 130 00:06:18,760 --> 00:06:22,500 So we're going to go ahead and do that here by setting a local variable. 131 00:06:22,500 --> 00:06:25,560 Remember, local means that it's just defined to the scope that 132 00:06:25,560 --> 00:06:29,310 it's in, rather than being global, which means we cannot access this variable 133 00:06:29,310 --> 00:06:31,590 outside of this file. 134 00:06:31,590 --> 00:06:34,895 Local background gets love.graphics.newImage, 135 00:06:34,895 --> 00:06:36,520 the function that we just talked about. 136 00:06:36,520 --> 00:06:40,821 Let me go ahead and hide this inspector here so we can have more room code. 137 00:06:40,821 --> 00:06:42,570 And then it's just going to take a string. 138 00:06:42,570 --> 00:06:44,536 So background.png. 139 00:06:44,536 --> 00:06:47,410 And I realize I actually didn't include those files in the directory. 140 00:06:47,410 --> 00:06:50,080 So I'm going to need to do that as well. 141 00:06:50,080 --> 00:06:52,225 Same thing for the ground, exact same function, 142 00:06:52,225 --> 00:06:55,590 love.graphics.newImage except ground.png. 143 00:06:55,590 --> 00:06:59,280 And before I forget, let's go ahead, and do that right now. 144 00:06:59,280 --> 00:07:02,055 I have the files here. 145 00:07:02,055 --> 00:07:03,660 So ground and background. 146 00:07:03,660 --> 00:07:09,150 We're going to copy those from the distro repo into my bird0 directory 147 00:07:09,150 --> 00:07:11,589 that I'm currently developing in right now. 148 00:07:11,589 --> 00:07:14,130 And as soon as we're done with that, we're going to go ahead, 149 00:07:14,130 --> 00:07:16,230 and we're going to define love.load, which 150 00:07:16,230 --> 00:07:20,500 is the function Love2D calls at the beginning of your program execution. 151 00:07:20,500 --> 00:07:25,560 In there, because we don't want these images to look blurry 152 00:07:25,560 --> 00:07:27,510 when they get loaded and upscaled, we want 153 00:07:27,510 --> 00:07:31,030 to go ahead and set our default filter to nearest on min and mag, 154 00:07:31,030 --> 00:07:34,950 which means on upscale and downscale, apply nearest neighbor filtering, which 155 00:07:34,950 --> 00:07:41,100 means no blurriness, no interpolation of the pixels. 156 00:07:41,100 --> 00:07:47,120 And then one thing that is just a small little touch, 157 00:07:47,120 --> 00:07:51,390 love.window.setTitle fifty bird because it's GD50, 158 00:07:51,390 --> 00:07:54,960 and then we're going to go ahead and set up our screen here 159 00:07:54,960 --> 00:08:01,670 with our virtual width, virtual height, window width, window height. 160 00:08:01,670 --> 00:08:05,730 It's getting a bit long, and then it takes in the table. 161 00:08:05,730 --> 00:08:09,180 Recall tables, just take in keys like so. 162 00:08:09,180 --> 00:08:14,750 Unlike in Python where you might use a colon, we use an equal sign in Love, 163 00:08:14,750 --> 00:08:16,820 or in Lua, I should say. 164 00:08:16,820 --> 00:08:23,740 Resizable to true, and that is the end of our load function. 165 00:08:23,740 --> 00:08:27,600 Now, does anybody recall how if we want to resize-- 166 00:08:27,600 --> 00:08:29,120 so notice I set resizable to true. 167 00:08:29,120 --> 00:08:34,169 Do we know how we can send a message to push to resize our screen for us? 168 00:08:34,169 --> 00:08:37,240 169 00:08:37,240 --> 00:08:41,110 So Love2D to defines a function called love.resize, 170 00:08:41,110 --> 00:08:43,510 which takes in a width and a height. 171 00:08:43,510 --> 00:08:48,005 And in there, all we're going to do is defer that call to push. 172 00:08:48,005 --> 00:08:49,630 Recall the exact same function on push. 173 00:08:49,630 --> 00:08:51,463 It takes a width and a height, and that will 174 00:08:51,463 --> 00:08:55,390 take care of dynamically rescaling the canvas it uses internally. 175 00:08:55,390 --> 00:08:58,157 It renders to a texture, and it's going to render to the texture 176 00:08:58,157 --> 00:09:00,490 that we set as the virtual width and the virtual height, 177 00:09:00,490 --> 00:09:02,890 and it's going to scale it to fit our screen. 178 00:09:02,890 --> 00:09:05,320 And it needs to know our physical screen dimensions 179 00:09:05,320 --> 00:09:11,007 so that it can actually properly scale that internal canvas appropriately. 180 00:09:11,007 --> 00:09:14,090 Does anybody remember the function that we use to get input from the user? 181 00:09:14,090 --> 00:09:17,380 182 00:09:17,380 --> 00:09:23,370 So function.love.keyPressed, recall, takes in a key. 183 00:09:23,370 --> 00:09:26,310 Love is going to call this automatically every time we press a key, 184 00:09:26,310 --> 00:09:27,555 and that's going to be-- 185 00:09:27,555 --> 00:09:31,170 and we're going to have access to that key, and we can do any sort of logic 186 00:09:31,170 --> 00:09:36,180 that we want on that key, using that key, 187 00:09:36,180 --> 00:09:38,550 and we're just going to call love.event.quit 188 00:09:38,550 --> 00:09:41,100 because I don't like to press Command Q or click the red x. 189 00:09:41,100 --> 00:09:44,380 I just want to hit escape, be done with it. 190 00:09:44,380 --> 00:09:47,130 And then what's our render-- what's Love's render function called? 191 00:09:47,130 --> 00:09:49,810 192 00:09:49,810 --> 00:09:52,630 it's called love.draw. 193 00:09:52,630 --> 00:09:55,300 So call love.draw. 194 00:09:55,300 --> 00:09:58,210 And then because we're using push, does anybody 195 00:09:58,210 --> 00:10:01,180 remember what we need to actually do to get push to render 196 00:10:01,180 --> 00:10:03,321 our screen to a virtual resolution? 197 00:10:03,321 --> 00:10:06,030 198 00:10:06,030 --> 00:10:11,200 So recall that there's actually two ways we can do it. 199 00:10:11,200 --> 00:10:16,950 We can call push start and push finish, which we didn't cover last week. 200 00:10:16,950 --> 00:10:22,150 Or we can call-- and that's actually the new de facto way to do it. 201 00:10:22,150 --> 00:10:27,660 Or we can do push apply start, which is the deprecated way to do it. 202 00:10:27,660 --> 00:10:31,790 But starting from here on out, we're going to call push start 203 00:10:31,790 --> 00:10:34,740 and push finish. 204 00:10:34,740 --> 00:10:37,110 And then last, we have our images. 205 00:10:37,110 --> 00:10:40,680 We've allocated them as objects up here. 206 00:10:40,680 --> 00:10:44,190 We have a background and a ground. 207 00:10:44,190 --> 00:10:47,230 All we need to do now is just draw them to the screen. 208 00:10:47,230 --> 00:10:49,470 So this is a new function. 209 00:10:49,470 --> 00:10:52,650 Or it's actually not a new function. 210 00:10:52,650 --> 00:10:55,080 It is a new function, actually. 211 00:10:55,080 --> 00:10:59,320 Love.graphics.rectangle is what we used last week for all of the draw calls. 212 00:10:59,320 --> 00:11:02,640 In this case, we want to draw an image object, 213 00:11:02,640 --> 00:11:04,660 a texture object that we have in memory. 214 00:11:04,660 --> 00:11:06,810 So we're going to call love.graphics.draw, 215 00:11:06,810 --> 00:11:09,240 and it takes a drawable, which means anything 216 00:11:09,240 --> 00:11:11,760 that Love has defined as something that can be drawn. 217 00:11:11,760 --> 00:11:13,860 In this case, images are drawables. 218 00:11:13,860 --> 00:11:18,396 They can be drawn, and they can be drawn at any given position that you specify. 219 00:11:18,396 --> 00:11:20,520 So if you wanted to draw it at the top left corner, 220 00:11:20,520 --> 00:11:24,400 we would just say love.graphics.drawBackground at 0,0, 221 00:11:24,400 --> 00:11:25,670 and it has that effect. 222 00:11:25,670 --> 00:11:30,437 And we're going to the exact same thing with our ground. 223 00:11:30,437 --> 00:11:32,520 The only difference being that obviously, we don't 224 00:11:32,520 --> 00:11:34,100 want to draw at the top of corner. 225 00:11:34,100 --> 00:11:35,980 We want to draw at the bottom of the screen. 226 00:11:35,980 --> 00:11:41,447 So we just call virtual height minus 16, which 227 00:11:41,447 --> 00:11:43,030 happens to be the height of our image. 228 00:11:43,030 --> 00:11:43,990 So if you run this-- 229 00:11:43,990 --> 00:11:48,690 I'm going to go ahead, and make sure I'm in the right directory. 230 00:11:48,690 --> 00:11:52,500 I'm not in the right directory, so I'm going to go into a directory I wrote, 231 00:11:52,500 --> 00:11:55,020 fifty bird scratch. 232 00:11:55,020 --> 00:11:56,830 Go into bird0. 233 00:11:56,830 --> 00:12:00,630 And if I run this, I should theoretically have just two images 234 00:12:00,630 --> 00:12:03,150 layered on top of each other, which I do not. 235 00:12:03,150 --> 00:12:06,570 236 00:12:06,570 --> 00:12:11,070 So we just make sure that it gets saved. 237 00:12:11,070 --> 00:12:15,150 Remember to always save your work, and there you go. 238 00:12:15,150 --> 00:12:18,900 So all we're doing now, it looks infinitely better than last week 239 00:12:18,900 --> 00:12:21,242 already, but it's very simple, very few lines of code. 240 00:12:21,242 --> 00:12:22,950 All the effort that we've put into it has 241 00:12:22,950 --> 00:12:25,470 been in our sprite editor of choice, and you 242 00:12:25,470 --> 00:12:28,140 can use most any application you want to do this sort of stuff. 243 00:12:28,140 --> 00:12:30,480 I use a program called Aseprite, I like a lot, 244 00:12:30,480 --> 00:12:33,090 but you could do this in Gimp, which is free, you could do it in Photoshop, 245 00:12:33,090 --> 00:12:35,256 you could do it in Microsoft Paint if you wanted to. 246 00:12:35,256 --> 00:12:37,660 247 00:12:37,660 --> 00:12:40,790 Godspeed if you do. 248 00:12:40,790 --> 00:12:43,900 But yeah, so that's as simple as it is just to draw images to the screen. 249 00:12:43,900 --> 00:12:48,000 So we've already made quite a lot of progress in a very short period of time 250 00:12:48,000 --> 00:12:50,280 in terms of the visual aspect of our game. 251 00:12:50,280 --> 00:12:53,889 But it's not interesting to look at beyond the initial sort of honeymoon 252 00:12:53,889 --> 00:12:55,680 period of now we have colors on the screen. 253 00:12:55,680 --> 00:13:01,320 We want to actually get scrolling because the game, recall, is 254 00:13:01,320 --> 00:13:02,130 a scrolling game. 255 00:13:02,130 --> 00:13:05,490 And actually, would anybody be willing to volunteer 256 00:13:05,490 --> 00:13:09,360 to come up, and play Flappy Bird just so we can see it live on the stage? 257 00:13:09,360 --> 00:13:13,830 258 00:13:13,830 --> 00:13:15,390 David, you want to come up and play? 259 00:13:15,390 --> 00:13:19,110 Does someone volunteer? 260 00:13:19,110 --> 00:13:20,880 Stephen, you want to come up and play? 261 00:13:20,880 --> 00:13:23,360 STEPHEN: Sure. 262 00:13:23,360 --> 00:13:25,068 COLTON OGDEN: Thank you for volunteering. 263 00:13:25,068 --> 00:13:30,690 264 00:13:30,690 --> 00:13:34,410 I guarantee you're better at this game than I am. 265 00:13:34,410 --> 00:13:41,670 I'm going to go ahead and cd into bird12 in the directory, which is 266 00:13:41,670 --> 00:13:43,735 the final version of the game complete. 267 00:13:43,735 --> 00:13:45,360 So I'm going to go ahead and hit Enter. 268 00:13:45,360 --> 00:13:47,220 [VIDEO GAME MUSIC PLAYING] 269 00:13:47,220 --> 00:13:52,200 So already, we can see the parallax scrolling 270 00:13:52,200 --> 00:13:54,504 that I referred to before, which is the floor 271 00:13:54,504 --> 00:13:56,670 and the background are scrolling at different rates, 272 00:13:56,670 --> 00:13:59,412 and we'll see this very shortly in the next example. 273 00:13:59,412 --> 00:14:00,120 We have a prompt. 274 00:14:00,120 --> 00:14:00,661 We have text. 275 00:14:00,661 --> 00:14:02,660 We've already used this before with the font. 276 00:14:02,660 --> 00:14:05,740 So go ahead, if you press Enter, you're going to get a count down. 277 00:14:05,740 --> 00:14:06,720 So space is to jump. 278 00:14:06,720 --> 00:14:08,507 [BEEPING] 279 00:14:08,507 --> 00:14:10,840 So we have our bird jumping in the middle of the screen. 280 00:14:10,840 --> 00:14:12,255 We have a score at the top. 281 00:14:12,255 --> 00:14:15,101 The goal is to avoid hitting the pipes. 282 00:14:15,101 --> 00:14:16,412 [CRASH] 283 00:14:16,412 --> 00:14:17,470 OK, a score of one. 284 00:14:17,470 --> 00:14:18,386 You want to try again? 285 00:14:18,386 --> 00:14:20,012 Go ahead. 286 00:14:20,012 --> 00:14:21,988 [BEEPING] 287 00:14:21,988 --> 00:14:23,470 288 00:14:23,470 --> 00:14:25,990 So it keeps track of his position, and every time 289 00:14:25,990 --> 00:14:29,380 he gets past the right edge of a pair of pipes, as you can see, 290 00:14:29,380 --> 00:14:32,710 that's when he gets a point. 291 00:14:32,710 --> 00:14:37,149 So if you recall from last week, what do we think is-- 292 00:14:37,149 --> 00:14:38,440 what's detecting the collision? 293 00:14:38,440 --> 00:14:40,430 If we remember last week, what's the term? 294 00:14:40,430 --> 00:14:42,580 Anybody remember? 295 00:14:42,580 --> 00:14:46,654 Axis aligned bound aabb collision detection, axis-aligned bounding box. 296 00:14:46,654 --> 00:14:49,570 It's the same thing that we did with Pong, except now we're doing it-- 297 00:14:49,570 --> 00:14:52,240 we have graphics, but it's the same exact concept. 298 00:14:52,240 --> 00:14:54,000 We're just using rectangles. 299 00:14:54,000 --> 00:14:58,690 And when one rectangle overlaps with another rectangle, we trigger death. 300 00:14:58,690 --> 00:15:02,155 301 00:15:02,155 --> 00:15:04,585 So one last iteration I think, and then we'll. 302 00:15:04,585 --> 00:15:06,590 [CRASH] 303 00:15:06,590 --> 00:15:09,110 We'll let you try one more time. 304 00:15:09,110 --> 00:15:09,980 Go ahead. 305 00:15:09,980 --> 00:15:11,410 I'll give it a shot. 306 00:15:11,410 --> 00:15:12,720 I'm going to lose on purpose. 307 00:15:12,720 --> 00:15:15,230 OK, here we go. 308 00:15:15,230 --> 00:15:18,440 To be unfair, I got plenty of practice when I was developing this, 309 00:15:18,440 --> 00:15:20,830 but we'll see if that actually holds true here. 310 00:15:20,830 --> 00:15:23,455 311 00:15:23,455 --> 00:15:25,730 So notice also, the pipes-- 312 00:15:25,730 --> 00:15:27,870 the procedure generation that I-- oh, I lost. 313 00:15:27,870 --> 00:15:29,050 Three points. 314 00:15:29,050 --> 00:15:31,820 Let me explain a little bit more, while I do one more iteration. 315 00:15:31,820 --> 00:15:34,362 But the pipes themselves, every time we start, 316 00:15:34,362 --> 00:15:36,070 they're spawning at a different location. 317 00:15:36,070 --> 00:15:39,700 This is proceeded generation in pretty much the most simplest 318 00:15:39,700 --> 00:15:45,160 way possible, and notice that the pipes are shifting gradually. 319 00:15:45,160 --> 00:15:48,370 So this is sort of the make up of our level, 320 00:15:48,370 --> 00:15:51,910 and it's just generating bit by bit do to some simple algorithm 321 00:15:51,910 --> 00:15:54,670 that we have that just says hey, spawn another pipe here, shift it 322 00:15:54,670 --> 00:15:55,840 by some amount. 323 00:15:55,840 --> 00:16:00,340 And this very simple approach allows us to have an infinite level over and over 324 00:16:00,340 --> 00:16:01,760 again, and it's very efficient. 325 00:16:01,760 --> 00:16:05,665 We only ever have as many pipes on the screen, and as we'll see soon-- 326 00:16:05,665 --> 00:16:09,362 we'll only have as many pipes in memory as we can see on the screen 327 00:16:09,362 --> 00:16:12,070 at one time, despite the fact that this level could theoretically 328 00:16:12,070 --> 00:16:16,460 go on infinitely, and so it's very cost efficient. 329 00:16:16,460 --> 00:16:18,250 So bird1 is the example. 330 00:16:18,250 --> 00:16:19,420 It's the parallax update. 331 00:16:19,420 --> 00:16:23,410 So parallax scrolling is an important concept in 2D 332 00:16:23,410 --> 00:16:26,160 and also 3D, but 2D game development. 333 00:16:26,160 --> 00:16:31,750 It refers to the illusion of movement given two frames of reference 334 00:16:31,750 --> 00:16:33,680 that are moving at different rates. 335 00:16:33,680 --> 00:16:37,120 So if you're driving on the highway, and you see a fence next to you, 336 00:16:37,120 --> 00:16:39,160 and you see mountains in the distance, you're 337 00:16:39,160 --> 00:16:41,857 observing parallax scroll by seeing how fast the fence moves 338 00:16:41,857 --> 00:16:42,940 relative to the mountains. 339 00:16:42,940 --> 00:16:46,330 The mountains are going to move a lot more slowly than the fences right 340 00:16:46,330 --> 00:16:47,170 next to you. 341 00:16:47,170 --> 00:16:50,140 And we accomplish the same exact illusion in our game 342 00:16:50,140 --> 00:16:54,310 by using this sort of graphical illusion. 343 00:16:54,310 --> 00:16:56,860 And so I'm going to go ahead in my directory 344 00:16:56,860 --> 00:17:02,380 here, in bird1, which is an unpopulated-- 345 00:17:02,380 --> 00:17:07,329 it's populated with the contents of bird0, the complete contents of bird0. 346 00:17:07,329 --> 00:17:10,490 The version that you'll see will have all of the code, 347 00:17:10,490 --> 00:17:16,700 but I'm going to go ahead, and if we run bird0 in that directory. 348 00:17:16,700 --> 00:17:19,900 So I think right now I'm still in the full distro. 349 00:17:19,900 --> 00:17:23,355 So let me go ahead, go into fifty bird scratch again. 350 00:17:23,355 --> 00:17:26,609 351 00:17:26,609 --> 00:17:27,400 Whoops, where am I? 352 00:17:27,400 --> 00:17:31,970 353 00:17:31,970 --> 00:17:34,562 And then I'm going to go into bird1, and run it, 354 00:17:34,562 --> 00:17:36,770 and I get the exact same image that we had last time. 355 00:17:36,770 --> 00:17:40,030 So everything is there from before, just two images, nothing moving, 356 00:17:40,030 --> 00:17:41,980 no parallax that we can observe. 357 00:17:41,980 --> 00:17:44,140 I'm going to go ahead, and start implementing 358 00:17:44,140 --> 00:17:47,540 the basics of this parallax. 359 00:17:47,540 --> 00:17:51,040 So if I go ahead in my main. 360 00:17:51,040 --> 00:17:55,810 So I'm going to go down here to where we have our background. 361 00:17:55,810 --> 00:17:58,580 So we need a couple of new things. 362 00:17:58,580 --> 00:18:00,400 So along with our background image, we need 363 00:18:00,400 --> 00:18:02,890 to keep track of how much it's scrolled because we're 364 00:18:02,890 --> 00:18:04,780 going to need to start drawing this image to the screen, 365 00:18:04,780 --> 00:18:06,580 but if we're going to scroll it, that means 366 00:18:06,580 --> 00:18:08,891 that we need to shift its x offset. 367 00:18:08,891 --> 00:18:11,140 Instead of drawing it at 0,0, if we want it to scroll, 368 00:18:11,140 --> 00:18:15,190 we have to draw it at some negative value instead. 369 00:18:15,190 --> 00:18:18,680 Over time, this will have the effect of it moving right to left. 370 00:18:18,680 --> 00:18:20,680 So I'm going to go ahead, and keep track-- 371 00:18:20,680 --> 00:18:22,570 I'm going to use a variable to keep track of the scroll 372 00:18:22,570 --> 00:18:24,550 now, for both of these images, and we're just 373 00:18:24,550 --> 00:18:27,549 going to call them backgroundScroll and groundScroll, and set them to 0. 374 00:18:27,549 --> 00:18:30,370 So this is going to have the effect of no x offset. 375 00:18:30,370 --> 00:18:34,780 So I could use this variable right now in this draw call down here, 376 00:18:34,780 --> 00:18:36,110 which I'm actually going to do. 377 00:18:36,110 --> 00:18:38,180 I'm going to go ahead, and go to-- 378 00:18:38,180 --> 00:18:40,460 I'm just going to find out if that is correct. 379 00:18:40,460 --> 00:18:46,681 I'm going to go ahead and set that to negative backgroundScroll. 380 00:18:46,681 --> 00:18:49,330 381 00:18:49,330 --> 00:18:53,170 And here, I'm going to set this to negative groundScroll. 382 00:18:53,170 --> 00:18:55,160 So this is not going to change anything yet. 383 00:18:55,160 --> 00:18:57,701 It's going to be the exact same thing because they're both 0. 384 00:18:57,701 --> 00:19:02,870 They were 0 before, but we're going to change them over time. 385 00:19:02,870 --> 00:19:14,110 And in order to do this, I'm going to go ahead, and go into up here. 386 00:19:14,110 --> 00:19:17,600 One thing before we do that actually, we need to set a speed for this. 387 00:19:17,600 --> 00:19:19,660 This is going to happen over time, but since they 388 00:19:19,660 --> 00:19:21,580 need to occur at different rates, the background 389 00:19:21,580 --> 00:19:23,580 needs to go at a slower rate than the foreground 390 00:19:23,580 --> 00:19:28,300 so that we do get this parallax effect, we need to separate speed variables. 391 00:19:28,300 --> 00:19:32,500 Generally, the norm for something that is not going to change 392 00:19:32,500 --> 00:19:34,690 is to write it in caps with underscores. 393 00:19:34,690 --> 00:19:35,900 This is constant notation. 394 00:19:35,900 --> 00:19:38,350 This is frequently seen in most programming languages. 395 00:19:38,350 --> 00:19:39,102 We'll use it here. 396 00:19:39,102 --> 00:19:41,560 I'm going to set a variable called BACKGROUND_SCROLL_SPEED, 397 00:19:41,560 --> 00:19:44,130 and I'm just going to set that to 30. 398 00:19:44,130 --> 00:19:48,580 I'm going to do the same thing, GROUND_SCROLL_SPEED. 399 00:19:48,580 --> 00:19:54,520 Does this need to be higher or lower than the BACKGROUND_SCROLL_SPEED? 400 00:19:54,520 --> 00:19:56,280 The ground is going to move-- 401 00:19:56,280 --> 00:19:59,341 so the background needs to move slower than the ground does. 402 00:19:59,341 --> 00:20:00,590 So this is going to be higher. 403 00:20:00,590 --> 00:20:01,930 So we're just going to set it to 60. 404 00:20:01,930 --> 00:20:03,850 You can set it to whatever you want to get the effect that you want, 405 00:20:03,850 --> 00:20:05,680 but this will already be quite noticeable. 406 00:20:05,680 --> 00:20:09,670 The ground is going to move twice as fast as the background. 407 00:20:09,670 --> 00:20:14,590 And so what we're going to do also is if we 408 00:20:14,590 --> 00:20:19,180 just-- so what's going to happen if we just let our image scroll infinitely? 409 00:20:19,180 --> 00:20:21,147 What's going to happen at a certain point? 410 00:20:21,147 --> 00:20:22,460 AUDIENCE: Run out of image? 411 00:20:22,460 --> 00:20:24,168 COLTON OGDEN: It's going run at an image. 412 00:20:24,168 --> 00:20:26,621 So how do we fix this problem? 413 00:20:26,621 --> 00:20:27,370 AUDIENCE: Loop it. 414 00:20:27,370 --> 00:20:28,894 COLTON OGDEN: Loop it, exactly. 415 00:20:28,894 --> 00:20:31,060 So we're going to go ahead, and set a looping point. 416 00:20:31,060 --> 00:20:35,230 So another constant background looping point, 417 00:20:35,230 --> 00:20:39,340 and we're going to set this to 413, which you kind of have 418 00:20:39,340 --> 00:20:43,330 to look at your image, and determine-- you sort of have to set your images up, 419 00:20:43,330 --> 00:20:47,050 if you want to achieve this effect, by having them be a looping image. 420 00:20:47,050 --> 00:20:51,040 So have either two copies of the exact same thing that's your screen width, 421 00:20:51,040 --> 00:20:53,226 or just copy the same chunk over, and over again. 422 00:20:53,226 --> 00:20:54,350 There's many ways to do it. 423 00:20:54,350 --> 00:20:57,520 In this case, the looping point of the image of our background 424 00:20:57,520 --> 00:20:59,100 is 413 on the x-axis. 425 00:20:59,100 --> 00:21:02,307 So we're going to set that to 413. 426 00:21:02,307 --> 00:21:05,140 And then we're going to go ahead-- the next step is we actually have 427 00:21:05,140 --> 00:21:07,130 to start changing the value values. 428 00:21:07,130 --> 00:21:09,820 So in our update function, which is where 429 00:21:09,820 --> 00:21:12,820 this is going to happen, I'm going to go ahead, 430 00:21:12,820 --> 00:21:16,270 and define love.update, which recall, Love2D will call for you, 431 00:21:16,270 --> 00:21:19,240 but you must define it yourself. 432 00:21:19,240 --> 00:21:22,325 I'm going to go ahead, and set backgroundScroll too. 433 00:21:22,325 --> 00:21:32,366 434 00:21:32,366 --> 00:21:34,240 So what this is going to do, backgroundScroll 435 00:21:34,240 --> 00:21:40,140 gets backgroundScroll to itself plus the speed we set before times delta time. 436 00:21:40,140 --> 00:21:42,035 So it stays frame rate independent. 437 00:21:42,035 --> 00:21:45,020 438 00:21:45,020 --> 00:21:48,400 That will have the effect of adding the speed to our image, 439 00:21:48,400 --> 00:21:49,420 but we need to reset it. 440 00:21:49,420 --> 00:21:51,070 We need to actually perform the reset. 441 00:21:51,070 --> 00:21:54,730 And to do that, we'll just be using Modulus, which recall 442 00:21:54,730 --> 00:21:59,860 from languages like C, simply divides-- 443 00:21:59,860 --> 00:22:04,700 basically, sets that value to the remainder of that division. 444 00:22:04,700 --> 00:22:10,990 So in this case-- so 10 modulo 5 would be 0, 445 00:22:10,990 --> 00:22:18,040 but 10 modulo 9 would be 1, effectively, because we have 0 446 00:22:18,040 --> 00:22:20,050 left over once we divide 10 by 5. 447 00:22:20,050 --> 00:22:22,690 We have 1 left over once we divide 10 by 9. 448 00:22:22,690 --> 00:22:26,240 449 00:22:26,240 --> 00:22:30,950 So I apologize if that concept is not new. 450 00:22:30,950 --> 00:22:40,930 But we're going to do the same exact thing for our ground, 451 00:22:40,930 --> 00:22:47,500 only we're going to modulo by our virtual width in this case. 452 00:22:47,500 --> 00:22:48,880 I did not set a looping point. 453 00:22:48,880 --> 00:22:52,250 I do in later examples, but our ground image is very-- 454 00:22:52,250 --> 00:22:54,280 it's consistent enough such that you don't even 455 00:22:54,280 --> 00:22:57,984 notice it when it loops without just using the virtual with. 456 00:22:57,984 --> 00:23:00,400 So we're just going to use the virtual width in that case. 457 00:23:00,400 --> 00:23:05,260 It's very patterned, and very small. 458 00:23:05,260 --> 00:23:09,550 And aside from that, we already have the background scrolls here 459 00:23:09,550 --> 00:23:11,840 in our draw functions. 460 00:23:11,840 --> 00:23:13,990 So when we run this code, we should theoretically 461 00:23:13,990 --> 00:23:17,010 have scrolling background. 462 00:23:17,010 --> 00:23:20,440 AUDIENCE: So does it even just have to be twice the width 463 00:23:20,440 --> 00:23:21,910 or something so they don't run out? 464 00:23:21,910 --> 00:23:24,118 COLTON OGDEN: They do, at least twice the width, yes. 465 00:23:24,118 --> 00:23:27,280 There's ways you could effectively tile your image, 466 00:23:27,280 --> 00:23:30,180 and do it that way to save memory on texture size. 467 00:23:30,180 --> 00:23:32,942 If you have maybe something that's a quarter of the screen size 468 00:23:32,942 --> 00:23:34,900 that you want to loop over, and over again, you 469 00:23:34,900 --> 00:23:36,775 don't want to have that as one big image, 470 00:23:36,775 --> 00:23:39,400 you'll just draw four copies of that image to fill your screen, 471 00:23:39,400 --> 00:23:40,694 and then to shift all of them. 472 00:23:40,694 --> 00:23:42,610 Maybe five, actually, so you have a little bit 473 00:23:42,610 --> 00:23:48,272 beyond the edge of the screen, and then just put all of them back to 0. 474 00:23:48,272 --> 00:23:51,218 AUDIENCE: So the bottom one, the ground is-- 475 00:23:51,218 --> 00:23:56,610 you wouldn't know if you just restarted showing the image with the larger 476 00:23:56,610 --> 00:23:57,110 background. 477 00:23:57,110 --> 00:24:00,547 You wouldn't have to worry about the mountain getting cut in half 478 00:24:00,547 --> 00:24:02,040 when you replaced the right-- 479 00:24:02,040 --> 00:24:03,370 COLTON OGDEN: Exactly. 480 00:24:03,370 --> 00:24:05,144 So we could actually-- 481 00:24:05,144 --> 00:24:07,310 I could show you right now what that will look like. 482 00:24:07,310 --> 00:24:10,330 So if we just take out the looping point here, 483 00:24:10,330 --> 00:24:15,390 or we set it to some value that's completely inaccurate like 270, 484 00:24:15,390 --> 00:24:24,670 and then we run it, after a while it should just cut. 485 00:24:24,670 --> 00:24:26,480 Yeah, right there. 486 00:24:26,480 --> 00:24:28,686 AUDIENCE: So are you drawing it twice, really? 487 00:24:28,686 --> 00:24:31,060 Like one after another one when it runs out or something? 488 00:24:31,060 --> 00:24:34,420 COLTON OGDEN: No, the image is so wide that it always will fill the screen, 489 00:24:34,420 --> 00:24:38,940 even after it's been set back to-- even after it's gone past the looping point. 490 00:24:38,940 --> 00:24:41,080 I forget how large the texture is. 491 00:24:41,080 --> 00:24:44,102 It is 1157 pixels wide. 492 00:24:44,102 --> 00:24:45,810 So it's more than twice the screen width. 493 00:24:45,810 --> 00:24:48,257 Actually, I think it is exactly twice the screen width. 494 00:24:48,257 --> 00:24:50,090 No, it's not exactly twice the screen width, 495 00:24:50,090 --> 00:24:51,820 but it's more than twice the screen width 496 00:24:51,820 --> 00:24:54,820 so that when the amount-- the 413 pixels has elapsed, 497 00:24:54,820 --> 00:24:59,410 it's still plenty past the right edge of the screen, and the looping part, 498 00:24:59,410 --> 00:25:02,420 it'll be the exact same appearance on the texture, 499 00:25:02,420 --> 00:25:06,190 but it's completely been shifted back to the right. 500 00:25:06,190 --> 00:25:10,150 The 0,0 of our image is now at 0,0 in our screen space. 501 00:25:10,150 --> 00:25:13,690 AUDIENCE: So the looping is just reloading [INAUDIBLE]?? 502 00:25:13,690 --> 00:25:16,660 COLTON OGDEN: Your image is here, moving, and then just instantly 503 00:25:16,660 --> 00:25:20,340 back to the beginning, and then moving back to-- 504 00:25:20,340 --> 00:25:23,470 the setting it back to 0 or technically, how many 505 00:25:23,470 --> 00:25:28,190 pixels it's gone past the edge of the screen because using modulo. 506 00:25:28,190 --> 00:25:29,606 AUDIENCE: So it's just one image. 507 00:25:29,606 --> 00:25:32,147 It's like you just instantaneously flip it at the right time. 508 00:25:32,147 --> 00:25:33,130 COLTON OGDEN: Yeah. 509 00:25:33,130 --> 00:25:33,940 It's a translation. 510 00:25:33,940 --> 00:25:34,900 It's an instant translation. 511 00:25:34,900 --> 00:25:36,010 It takes place over one frame. 512 00:25:36,010 --> 00:25:36,968 So you don't notice it. 513 00:25:36,968 --> 00:25:40,370 Your human eye can't see it because it literally happens in one frame. 514 00:25:40,370 --> 00:25:43,780 The image data is the exact same at those two points 515 00:25:43,780 --> 00:25:45,010 because we have a texture. 516 00:25:45,010 --> 00:25:48,122 We've pre-created a texture that has the exact same data 517 00:25:48,122 --> 00:25:49,330 so that you have that effect. 518 00:25:49,330 --> 00:25:53,560 You have to have a texture that allows you to do this, or smartly 519 00:25:53,560 --> 00:25:55,390 draw four of the same images. 520 00:25:55,390 --> 00:25:57,100 Keep track of all four of them-- 521 00:25:57,100 --> 00:26:00,130 or actually, eight of them-- so you can move them to the left, 522 00:26:00,130 --> 00:26:03,020 and then shift them all back to the right. 523 00:26:03,020 --> 00:26:05,445 AUDIENCE: I assume when we do Super Mario Bros., 524 00:26:05,445 --> 00:26:11,032 we're going to have multiple images that get stacked one after another. 525 00:26:11,032 --> 00:26:12,990 COLTON OGDEN: When we get to Super Mario Bros., 526 00:26:12,990 --> 00:26:15,750 we'll be talking about a concept called tile mapping, which 527 00:26:15,750 --> 00:26:19,830 is where we take a sprite sheet, and then you basically chop it up 528 00:26:19,830 --> 00:26:25,740 into pieces, have a map that is basically numerical so that a brick is 529 00:26:25,740 --> 00:26:28,950 value one, and then you look through this giant two dimensional array 530 00:26:28,950 --> 00:26:31,740 that you have, and then go over it, iterate over it, 531 00:26:31,740 --> 00:26:35,945 and then draw a tile at an offset based on your index into that map. 532 00:26:35,945 --> 00:26:38,820 So it's a little bit more complicated, and actually a lot more memory 533 00:26:38,820 --> 00:26:43,316 efficient, but slightly different implementation. 534 00:26:43,316 --> 00:26:48,740 535 00:26:48,740 --> 00:26:53,740 OK, so we have parallax scrolling now. 536 00:26:53,740 --> 00:26:55,420 I want to take a moment to-- 537 00:26:55,420 --> 00:26:58,510 because we've touched on-- 538 00:26:58,510 --> 00:27:01,930 this is a very introductory way of demonstrating that games are illusions, 539 00:27:01,930 --> 00:27:03,160 by using parallax scrolling. 540 00:27:03,160 --> 00:27:07,084 All we've done, really, is just set two things to scroll at different rates, 541 00:27:07,084 --> 00:27:09,500 and this has made us feel like we have depth in our scene, 542 00:27:09,500 --> 00:27:11,416 but all we're doing, we have two images, we're 543 00:27:11,416 --> 00:27:13,780 scrolling them at different rates. 544 00:27:13,780 --> 00:27:15,790 But this is a common theme in game development, 545 00:27:15,790 --> 00:27:20,530 is trying to devise a scene that maybe is very elaborate, 546 00:27:20,530 --> 00:27:24,790 but doing it on very resource intensive devices like your iPhone, 547 00:27:24,790 --> 00:27:27,520 or like an old console like the Nintendo 64. 548 00:27:27,520 --> 00:27:34,672 These illusions are all over the place, and a YouTube channel 549 00:27:34,672 --> 00:27:37,830 that I recently found that I really like is 550 00:27:37,830 --> 00:27:41,320 it's called-- the name of the channel is She Says, but the actual show that they 551 00:27:41,320 --> 00:27:42,910 have is called Boundary Break. 552 00:27:42,910 --> 00:27:46,670 And what they do is they take a camera that 553 00:27:46,670 --> 00:27:49,060 goes beyond what the game developers allowed it to do, 554 00:27:49,060 --> 00:27:52,729 which they basically hack the game camera so you can see in places where 555 00:27:52,729 --> 00:27:54,520 you weren't supposed to see before, and you 556 00:27:54,520 --> 00:27:57,314 can see a lot of really cool trickery. 557 00:27:57,314 --> 00:27:59,230 I'm about to show you a couple of video clips, 558 00:27:59,230 --> 00:28:02,480 but here's the YouTube URL if you're curious to see the exact video. 559 00:28:02,480 --> 00:28:04,940 It's about a 33 minute video. 560 00:28:04,940 --> 00:28:08,440 It's on Zelda, Ocarina of Time for the N64. 561 00:28:08,440 --> 00:28:12,040 And I extracted a couple of particularly noteworthy clips 562 00:28:12,040 --> 00:28:15,127 that I thought were kind of interesting, and also humorous. 563 00:28:15,127 --> 00:28:16,960 I'm going to go ahead and show the clip now. 564 00:28:16,960 --> 00:28:21,160 So if we could dim the lights, I'll go ahead and start. 565 00:28:21,160 --> 00:28:23,802 This is the first example. 566 00:28:23,802 --> 00:28:26,260 SPEAKER 1: OK, so there's a lot to talk about with the shop 567 00:28:26,260 --> 00:28:27,627 owners in the Ocarina of Time. 568 00:28:27,627 --> 00:28:30,210 So I'm going to just condense it down to the most interesting, 569 00:28:30,210 --> 00:28:32,001 and the first one we're going to talk about 570 00:28:32,001 --> 00:28:34,090 is the bizarre shop owner in Hyrule. 571 00:28:34,090 --> 00:28:38,980 Now, in Majora's Mask, the very same character is actually shown with legs, 572 00:28:38,980 --> 00:28:42,430 but in Ocarina of Time, he did not have those. 573 00:28:42,430 --> 00:28:46,950 In fact, he looks extremely hilarious without his legs. 574 00:28:46,950 --> 00:28:50,530 575 00:28:50,530 --> 00:28:52,445 COLTON OGDEN: So this is a-- 576 00:28:52,445 --> 00:28:55,570 does anybody have an instinct as to why they might have done this this way? 577 00:28:55,570 --> 00:28:57,670 AUDIENCE: You're not going to see it anyway. 578 00:28:57,670 --> 00:29:01,685 COLTON OGDEN: Exactly, and beyond that, also just saving on memory. 579 00:29:01,685 --> 00:29:04,810 Not having to load a character model-- the vertices and textures associated 580 00:29:04,810 --> 00:29:05,680 with it-- 581 00:29:05,680 --> 00:29:08,830 on such a memory constrained device like the N64. 582 00:29:08,830 --> 00:29:10,510 I forget how much memory it had. 583 00:29:10,510 --> 00:29:13,790 Like four megabytes of memory, I think less than that. 584 00:29:13,790 --> 00:29:16,540 And so they are obviously cutting however many corners they could. 585 00:29:16,540 --> 00:29:19,690 In this case, by literally using the illusion 586 00:29:19,690 --> 00:29:23,470 of looking-- not the illusion, but just the fact that you only could see over 587 00:29:23,470 --> 00:29:26,890 the counter, and giving you the illusion that there's a fully living, 588 00:29:26,890 --> 00:29:30,000 talking shop keeper there, but it's just half a model. 589 00:29:30,000 --> 00:29:35,260 And other example here is more to show how Ocarina of Time 590 00:29:35,260 --> 00:29:39,550 used the N64's limited memory to give you 591 00:29:39,550 --> 00:29:43,250 the sense of being in a very large level when you might not actually have been. 592 00:29:43,250 --> 00:29:46,400 So if we could dim the lights one time, we'll go ahead and show this. 593 00:29:46,400 --> 00:29:48,850 SPEAKER 1: So this one was apparently a hot suggestion, 594 00:29:48,850 --> 00:29:50,570 [CHUCKLES] 595 00:29:50,570 --> 00:29:51,860 596 00:29:51,860 --> 00:29:56,340 which is free camera on Death Mountain, including our friend, Big Goran. 597 00:29:56,340 --> 00:29:59,920 The smoke halo looks sort of weird against the black sky, 598 00:29:59,920 --> 00:30:02,440 and here you can see Nintendo fooled us. 599 00:30:02,440 --> 00:30:05,430 It's not full mountain, only the cliff face is actually rendered, 600 00:30:05,430 --> 00:30:09,000 and that's the path leading towards the Fire Temple. 601 00:30:09,000 --> 00:30:13,060 And if we zoom out, we can see the scale of the whole mop. 602 00:30:13,060 --> 00:30:15,240 Bigger than I thought it would be actually. 603 00:30:15,240 --> 00:30:19,990 The battle music's not quite fitting for an epic panning shot, though. 604 00:30:19,990 --> 00:30:23,090 COLTON OGDEN: Same idea here, really, just limited memory space. 605 00:30:23,090 --> 00:30:25,840 So let's load you know as much as we could possibly ever 606 00:30:25,840 --> 00:30:29,550 see from the perspective of the camera of Link, 607 00:30:29,550 --> 00:30:32,560 and it's actually very similar to how, I guess, people 608 00:30:32,560 --> 00:30:36,230 create stages in real life to make you feel as if you're in a-- 609 00:30:36,230 --> 00:30:38,920 when you go to a play, feel like you're actually in a scene. 610 00:30:38,920 --> 00:30:43,090 But they've clearly cut as many corners as possible, but it works. 611 00:30:43,090 --> 00:30:47,520 In the game, you can't tell, and that's very common in game development. 612 00:30:47,520 --> 00:30:50,260 If you're trying to achieve a particularly grand effect, 613 00:30:50,260 --> 00:30:52,090 it's something to think about is how can I 614 00:30:52,090 --> 00:30:55,300 make it seem like I'm doing something, but I'm actually not. 615 00:30:55,300 --> 00:30:58,540 How can I make it seem like I'm a bird flying through an infinite series 616 00:30:58,540 --> 00:31:01,540 of levels, but I'm actually not. 617 00:31:01,540 --> 00:31:07,020 We have a lot of more of that to show coming up soon. 618 00:31:07,020 --> 00:31:10,750 So far we have our background, but we don't 619 00:31:10,750 --> 00:31:16,870 have the title character of our game, and in this case, fifty bird. 620 00:31:16,870 --> 00:31:20,980 So I'm going to go ahead, and illustrate how we can get a bird actually 621 00:31:20,980 --> 00:31:23,390 rendering on the screen. 622 00:31:23,390 --> 00:31:27,800 So I'm going to go ahead into my bird2 directory here, that I've created. 623 00:31:27,800 --> 00:31:31,000 And note again, bird2 in your directory if you've loaded the code, 624 00:31:31,000 --> 00:31:34,210 is going to have the complete implementation. 625 00:31:34,210 --> 00:31:38,096 But in main, I'm going to do a couple of things. 626 00:31:38,096 --> 00:31:39,970 So actually, the first thing I'm going to do, 627 00:31:39,970 --> 00:31:42,310 we're going to-- notice that I've included-- 628 00:31:42,310 --> 00:31:48,010 actually, I haven't included the class file. 629 00:31:48,010 --> 00:31:49,580 So I'm going to do that right now. 630 00:31:49,580 --> 00:31:53,770 So in bird1-- sorry, I'm going to take from bird3, the class.lua. 631 00:31:53,770 --> 00:31:56,590 I'm going to go ahead, and put it into bird2 because we're 632 00:31:56,590 --> 00:31:57,746 going to make a bird class. 633 00:31:57,746 --> 00:31:59,620 Recall, from last week, a class is just a way 634 00:31:59,620 --> 00:32:03,010 of taking several variables that we might once have had disparate from one 635 00:32:03,010 --> 00:32:04,989 another, putting them together in a package, 636 00:32:04,989 --> 00:32:07,030 putting functions associated with those variables 637 00:32:07,030 --> 00:32:09,160 together so that we can call-- 638 00:32:09,160 --> 00:32:13,660 we can sort of think of our game world more abstractly, and more 639 00:32:13,660 --> 00:32:16,060 compartmentalized, and cleaner. 640 00:32:16,060 --> 00:32:19,000 So I'm going to go ahead-- and now I have in bird2, the class.lua. 641 00:32:19,000 --> 00:32:23,394 That's just the library we're using to get classes in Love2D in Lua. 642 00:32:23,394 --> 00:32:25,810 I'm going to go ahead, and I'm going to create a new file. 643 00:32:25,810 --> 00:32:27,490 This one's called bird.lua. 644 00:32:27,490 --> 00:32:31,660 So remember, the trend is for classes, capitalize them to differentiate them 645 00:32:31,660 --> 00:32:35,920 from functions and variables. 646 00:32:35,920 --> 00:32:41,020 This one I'm going to go ahead, and just go ahead and use my cheat sheet here. 647 00:32:41,020 --> 00:32:51,636 648 00:32:51,636 --> 00:32:53,940 My sheets are sticking together. 649 00:32:53,940 --> 00:32:58,232 OK, so this bird class is actually fairly simple. 650 00:32:58,232 --> 00:33:00,190 Recall that all we have to do to create a class 651 00:33:00,190 --> 00:33:03,220 is just use the class library, the capital C with the brackets 652 00:33:03,220 --> 00:33:05,332 there to initialize it. 653 00:33:05,332 --> 00:33:07,540 We're going to go ahead and define our init function. 654 00:33:07,540 --> 00:33:13,120 So every class has an init function, which initializes the object 655 00:33:13,120 --> 00:33:16,420 that it's going to refer to later. 656 00:33:16,420 --> 00:33:18,800 In this case, we're going to need a few things. 657 00:33:18,800 --> 00:33:20,932 So we're going to need an image for our bird 658 00:33:20,932 --> 00:33:22,640 because we want it to draw to the screen, 659 00:33:22,640 --> 00:33:25,056 and so what we need to do, same thing that we did before-- 660 00:33:25,056 --> 00:33:26,140 love.graphics.newImage. 661 00:33:26,140 --> 00:33:28,480 I'm going to go ahead, and hide this really fast. 662 00:33:28,480 --> 00:33:31,270 And then bird.png. 663 00:33:31,270 --> 00:33:33,280 Simple, easy. 664 00:33:33,280 --> 00:33:37,600 We want the width in the height of our bird. 665 00:33:37,600 --> 00:33:40,640 So I'm going to go ahead, and set that too. 666 00:33:40,640 --> 00:33:45,130 So every image has a set of functions associated 667 00:33:45,130 --> 00:33:46,930 with it that Love implements for us. 668 00:33:46,930 --> 00:33:49,665 The image that we get back from love.graphics.newImage 669 00:33:49,665 --> 00:33:54,400 is itself, sort of a class, which has a function called getwidth. 670 00:33:54,400 --> 00:33:58,600 So this will allow us to achieve the with, dynamically, of whatever 671 00:33:58,600 --> 00:34:00,250 class we-- 672 00:34:00,250 --> 00:34:04,917 whatever image file we happen to allocate, and create an object from. 673 00:34:04,917 --> 00:34:07,750 And then we're going to go ahead and set our x and y because recall, 674 00:34:07,750 --> 00:34:09,310 we have to draw it somewhere. 675 00:34:09,310 --> 00:34:11,690 We want to draw our bird in the middle of the screen. 676 00:34:11,690 --> 00:34:14,126 So we're going to go ahead, and just calculate this 677 00:34:14,126 --> 00:34:15,250 based on our virtual width. 678 00:34:15,250 --> 00:34:17,250 So we're going to do VIRTUAL_WIDTH divided by 2. 679 00:34:17,250 --> 00:34:19,083 So it's halfway in the middle of the screen, 680 00:34:19,083 --> 00:34:21,007 but since it draws from the top left corner, 681 00:34:21,007 --> 00:34:22,340 we want to shift it to the left. 682 00:34:22,340 --> 00:34:25,060 So we're going to use our width that we just-- 683 00:34:25,060 --> 00:34:27,719 684 00:34:27,719 --> 00:34:30,730 instant error-- we just initialized from the image data, 685 00:34:30,730 --> 00:34:34,060 and then we're going to do a self.width divided by 2. 686 00:34:34,060 --> 00:34:38,469 So we're going to divide the width by 2, shift that to the left on our x-axis. 687 00:34:38,469 --> 00:34:41,650 That's going to put us in the middle, horizontally. 688 00:34:41,650 --> 00:34:46,210 Vertically, it's the exact same thing, except reusing height instead of width. 689 00:34:46,210 --> 00:34:51,670 And that's pretty much it, except for one the last bit here. 690 00:34:51,670 --> 00:34:54,050 We want to be able to render our bird, pretty important. 691 00:34:54,050 --> 00:35:03,580 So we're going to do love.graphics.draw our image, and then at self.x, self.y. 692 00:35:03,580 --> 00:35:05,920 And so this is all we really need just to get 693 00:35:05,920 --> 00:35:08,140 a very simple sprite onto the screen. 694 00:35:08,140 --> 00:35:10,930 Now, it's not going to do anything because this sort of lives 695 00:35:10,930 --> 00:35:12,580 in a vacuum at the moment. 696 00:35:12,580 --> 00:35:15,640 What we need to do is in our main file, we're 697 00:35:15,640 --> 00:35:19,150 going to require bird, which is going to actually put it into our-- 698 00:35:19,150 --> 00:35:20,560 allow us to use it in our code. 699 00:35:20,560 --> 00:35:23,180 700 00:35:23,180 --> 00:35:32,790 We're going to create a local bird variable. 701 00:35:32,790 --> 00:35:35,180 We're just going to call it bird. 702 00:35:35,180 --> 00:35:47,270 We're going to, after that, simply render to the screen like that, 703 00:35:47,270 --> 00:35:51,560 and if all is done and well, and if I'm in the right directory-- 704 00:35:51,560 --> 00:35:55,210 705 00:35:55,210 --> 00:35:56,270 it did not work. 706 00:35:56,270 --> 00:35:59,810 707 00:35:59,810 --> 00:36:02,950 Make sure you save your work, again. 708 00:36:02,950 --> 00:36:07,291 Oh, I did not require class. 709 00:36:07,291 --> 00:36:07,790 My bad. 710 00:36:07,790 --> 00:36:15,280 So also, we need to do this since we added that to our directory. 711 00:36:15,280 --> 00:36:17,762 And I did not include the bird.png as well. 712 00:36:17,762 --> 00:36:19,345 So I'm going to go ahead, and do that. 713 00:36:19,345 --> 00:36:22,100 I'm going to borrow that from the next directory. 714 00:36:22,100 --> 00:36:27,740 That should be all we need to do, and attempt to call method. 715 00:36:27,740 --> 00:36:29,990 Render a null value. 716 00:36:29,990 --> 00:36:32,720 Interesting. 717 00:36:32,720 --> 00:36:33,710 Did I not save bird? 718 00:36:33,710 --> 00:36:36,350 I did not save bird. 719 00:36:36,350 --> 00:36:37,180 There we go. 720 00:36:37,180 --> 00:36:38,240 We did it. 721 00:36:38,240 --> 00:36:42,440 So not particularly interesting, but we're making steps. 722 00:36:42,440 --> 00:36:44,300 Remember to save your work. 723 00:36:44,300 --> 00:36:47,960 As we can see, I do not. 724 00:36:47,960 --> 00:36:49,400 But we're making progress. 725 00:36:49,400 --> 00:36:51,500 We have our entity that we will control. 726 00:36:51,500 --> 00:36:54,110 727 00:36:54,110 --> 00:36:59,540 Visually, we're getting very close, but a lot of important details are missing. 728 00:36:59,540 --> 00:37:03,170 What should be the next step, do we think? 729 00:37:03,170 --> 00:37:08,127 730 00:37:08,127 --> 00:37:09,960 AUDIENCE: Let's get him jumping and falling. 731 00:37:09,960 --> 00:37:12,210 COLTON OGDEN: Exactly, and we'll do that with the help 732 00:37:12,210 --> 00:37:14,970 of a notion that's common in platformers, and a lot of games, 733 00:37:14,970 --> 00:37:16,680 actually, but gravity. 734 00:37:16,680 --> 00:37:21,858 How do we think we can simulate gravity in the context of 2D game development? 735 00:37:21,858 --> 00:37:25,200 AUDIENCE: Just by default, falling at a constant rate. 736 00:37:25,200 --> 00:37:27,180 COLTON OGDEN: We could do that, certainly, 737 00:37:27,180 --> 00:37:29,220 and that's effectively what we will be doing. 738 00:37:29,220 --> 00:37:32,820 We'll be using something that we used last week, which 739 00:37:32,820 --> 00:37:42,150 was velocity, delta y, and applying that velocity to our birds y, 740 00:37:42,150 --> 00:37:46,740 frame by frame, and that will give it the illusion of falling. 741 00:37:46,740 --> 00:37:51,120 Now, falling at a constant rate isn't accurate to what gravity actually does. 742 00:37:51,120 --> 00:37:55,420 What we want to do, probably, is some gravity over and over again. 743 00:37:55,420 --> 00:37:58,330 Increment our gravity by some sort of constant value 744 00:37:58,330 --> 00:38:02,320 so that just like in real life, things fall faster, and faster, 745 00:38:02,320 --> 00:38:05,470 and then we want to add that to our y value. 746 00:38:05,470 --> 00:38:08,630 So I'm going to go ahead, and start implementing that now in bird3. 747 00:38:08,630 --> 00:38:09,610 Wrong repo. 748 00:38:09,610 --> 00:38:16,670 So bird3, we have everything that we had from before, 749 00:38:16,670 --> 00:38:33,721 except now, I'm going to go ahead, and in main.lua, in our update function, 750 00:38:33,721 --> 00:38:35,470 this is where we're actually going to want 751 00:38:35,470 --> 00:38:44,230 to perform the update logic for making the velocity apply to the bird. 752 00:38:44,230 --> 00:38:46,120 We're going to defer that to the bird class. 753 00:38:46,120 --> 00:38:49,286 We're going to assume that we have a method called update in our bird class, 754 00:38:49,286 --> 00:38:54,430 which we're going to implement shortly, and that's actually 755 00:38:54,430 --> 00:38:57,210 all we need to do in our main class. 756 00:38:57,210 --> 00:39:01,150 And the beauty of having classes that you can delegate all this work 757 00:39:01,150 --> 00:39:06,300 to, your main file, though it's still getting quite large-- it's 108 lines-- 758 00:39:06,300 --> 00:39:11,470 it's not 200 , 300, 400, thousands of lines of code because we're able 759 00:39:11,470 --> 00:39:16,660 to break out this code, and encapsulate it elsewhere. 760 00:39:16,660 --> 00:39:18,580 So I'm going to remember to save it this time, 761 00:39:18,580 --> 00:39:23,500 and then I'm going to go into the bird.lua file 762 00:39:23,500 --> 00:39:26,890 in that directory, which is the same with comments 763 00:39:26,890 --> 00:39:31,390 because I loaded it from the official repo, the same bird code 764 00:39:31,390 --> 00:39:33,310 that we wrote before. 765 00:39:33,310 --> 00:39:35,390 I'm going to go ahead, and do a couple of things. 766 00:39:35,390 --> 00:39:38,440 So the first thing that I'm going to do is define a constant. 767 00:39:38,440 --> 00:39:42,760 So I mentioned gravity before. 768 00:39:42,760 --> 00:39:47,800 Gravity is going to be a constant value just like it is in real life. 769 00:39:47,800 --> 00:39:49,040 I'm going to define it to 20. 770 00:39:49,040 --> 00:39:50,331 It's just some arbitrary value. 771 00:39:50,331 --> 00:39:52,656 This is a value that I decided felt right, 772 00:39:52,656 --> 00:39:54,280 but you can tune this however you want. 773 00:39:54,280 --> 00:39:56,060 There's no right or wrong way to do it. 774 00:39:56,060 --> 00:39:58,794 The less the gravity is, the slower it'll fall, 775 00:39:58,794 --> 00:40:01,960 and the more you'll feel like you're sort of in outer space, or on the moon, 776 00:40:01,960 --> 00:40:02,460 or whatnot. 777 00:40:02,460 --> 00:40:05,230 778 00:40:05,230 --> 00:40:10,030 We're going to also go ahead, and define-- 779 00:40:10,030 --> 00:40:11,950 recall that we need some way to keep track 780 00:40:11,950 --> 00:40:16,010 of how our how our bird is falling. 781 00:40:16,010 --> 00:40:17,800 We want a velocity, a y velocity. 782 00:40:17,800 --> 00:40:19,870 This is going to update our position each frame, 783 00:40:19,870 --> 00:40:22,000 and it's going to make it feel like we're falling. 784 00:40:22,000 --> 00:40:24,000 So we're going to set our initial velocity to 0. 785 00:40:24,000 --> 00:40:25,749 The bird's just going to be in the middle. 786 00:40:25,749 --> 00:40:27,130 It's not going be falling yet. 787 00:40:27,130 --> 00:40:31,300 What we don't want to do is apply this velocity. 788 00:40:31,300 --> 00:40:35,650 So remember, in our main file we assumed that we had an update function, 789 00:40:35,650 --> 00:40:37,480 but we haven't actually implemented it yet. 790 00:40:37,480 --> 00:40:38,980 So we're going to do that right now. 791 00:40:38,980 --> 00:40:42,490 We're going to say birdupdate dt. 792 00:40:42,490 --> 00:40:47,530 We're going to pass it in the same dt that we use in our main file, 793 00:40:47,530 --> 00:40:51,370 and we're going to go ahead, and just say our velocity 794 00:40:51,370 --> 00:40:59,630 is equal to our current velocity plus gravity times delta time. 795 00:40:59,630 --> 00:41:01,720 We're just going to scale gravity by delta time. 796 00:41:01,720 --> 00:41:04,210 So it will move the same amount no matter 797 00:41:04,210 --> 00:41:07,960 whether we're running at 10 frames per second or 60 frames per second. 798 00:41:07,960 --> 00:41:09,716 And then we're going to go ahead-- 799 00:41:09,716 --> 00:41:12,340 we have a velocity, but it's not actually changing our y value. 800 00:41:12,340 --> 00:41:14,590 The y value is what ultimately moves us on the screen. 801 00:41:14,590 --> 00:41:18,130 So we need to apply our new delta y to our y. 802 00:41:18,130 --> 00:41:21,190 So we're going to go ahead, and just do that. 803 00:41:21,190 --> 00:41:26,310 Self.y gets self.y way plus self dot delta y, dy. 804 00:41:26,310 --> 00:41:32,540 And so if I go back into bird3, assuming I saved everything, 805 00:41:32,540 --> 00:41:34,900 we should just fall straight to the screen, which we do. 806 00:41:34,900 --> 00:41:40,840 Not terribly useful, but notice it's slightly hard to tell, maybe, 807 00:41:40,840 --> 00:41:44,470 but it does move faster, and faster, frame by frame because that delta 808 00:41:44,470 --> 00:41:48,190 y is increasing as well as our y, and that delta y is getting 809 00:41:48,190 --> 00:41:49,939 applied to our y, frame by frame. 810 00:41:49,939 --> 00:41:50,980 I'll do it one more time. 811 00:41:50,980 --> 00:41:52,150 It's just funny to look at. 812 00:41:52,150 --> 00:41:55,210 All right, so we have basic gravity. 813 00:41:55,210 --> 00:41:56,620 Super basic computation. 814 00:41:56,620 --> 00:42:01,120 Just keep track of some gravity constant, delta y, 815 00:42:01,120 --> 00:42:05,740 increase that, and apply that to your y, and that gives you gravity. 816 00:42:05,740 --> 00:42:07,670 But Flappy Bird can jump. 817 00:42:07,670 --> 00:42:09,970 So we need to find a way to defy gravity. 818 00:42:09,970 --> 00:42:12,280 So we're going to do the-- in bird4, we're 819 00:42:12,280 --> 00:42:14,440 going to call this the anti-gravity update, 820 00:42:14,440 --> 00:42:19,690 and we're going to talk about how we can actually get that going. 821 00:42:19,690 --> 00:42:22,540 So I found this diagram, which I felt was pretty apt, 822 00:42:22,540 --> 00:42:25,581 and it also covers a few of the other concepts we're talking about today. 823 00:42:25,581 --> 00:42:29,290 But see here, this gravity, that's the constant we had just defined before, 824 00:42:29,290 --> 00:42:31,780 the 20 or whatever, and this gets applied 825 00:42:31,780 --> 00:42:33,340 at whatever value you want it to be. 826 00:42:33,340 --> 00:42:36,670 This gets applied frame by frame to your y. 827 00:42:36,670 --> 00:42:37,810 What we want is this. 828 00:42:37,810 --> 00:42:40,360 This vector here is jump velocity. 829 00:42:40,360 --> 00:42:46,420 We want some value to counteract this gravity that we've been accumulating. 830 00:42:46,420 --> 00:42:48,400 So how do we think we can go about doing this? 831 00:42:48,400 --> 00:42:51,640 832 00:42:51,640 --> 00:42:57,070 We can set gravity to some, perhaps, negative value, a high value. 833 00:42:57,070 --> 00:42:59,740 And that will have the effect of frame by frame, 834 00:42:59,740 --> 00:43:04,270 if we go from some positive value, which is taking us down on the y-axis, 835 00:43:04,270 --> 00:43:07,330 and we go to a negative value, frame by frame, 836 00:43:07,330 --> 00:43:09,770 it's going to say-- let's say that we start at negative 5. 837 00:43:09,770 --> 00:43:12,970 We set it's velocity to negative 5 it's going 838 00:43:12,970 --> 00:43:17,590 to set y to negative-- it's going to set it to plus negative 5 pixels 839 00:43:17,590 --> 00:43:20,350 plus negative 4.9 pixels, 4.8 pixels. 840 00:43:20,350 --> 00:43:22,930 It's going to shoot us up pretty fast in a series of pixels, 841 00:43:22,930 --> 00:43:26,700 but since we're applying gravity frame by frame, this value 842 00:43:26,700 --> 00:43:30,210 that we set before, 20, it's going to have the effect-- 843 00:43:30,210 --> 00:43:31,160 20 times delta time. 844 00:43:31,160 --> 00:43:33,570 So it gets, effectively, divided by 60. 845 00:43:33,570 --> 00:43:36,060 It's going to counteract this again. 846 00:43:36,060 --> 00:43:39,371 So we're going to shoot up pretty fast, but gravity 847 00:43:39,371 --> 00:43:41,370 is going to start taking hold immediately after, 848 00:43:41,370 --> 00:43:45,570 and we're going to start getting the effect of our bird jumping, 849 00:43:45,570 --> 00:43:47,429 and then falling down to the ground. 850 00:43:47,429 --> 00:43:49,470 A couple of other things that this diagram shows, 851 00:43:49,470 --> 00:43:54,032 which I thought were pretty cool, this pipe gap 852 00:43:54,032 --> 00:43:56,490 distance here, something that we'll be talking about pretty 853 00:43:56,490 --> 00:43:59,972 shortly because this needs to be defined so that we can offset our pipes. 854 00:43:59,972 --> 00:44:02,430 Pipe separation, it's another thing we'll be talking about. 855 00:44:02,430 --> 00:44:05,100 And also pipe width, which is just an intrinsic value 856 00:44:05,100 --> 00:44:07,400 characteristic of the pipe sprite we'll be using, 857 00:44:07,400 --> 00:44:09,290 but I thought it was very apt. 858 00:44:09,290 --> 00:44:11,970 NYU did a nice article, if you want to look 859 00:44:11,970 --> 00:44:13,470 at this, about exploring game space. 860 00:44:13,470 --> 00:44:17,190 They computationally determined what would make a Flappy Bird 861 00:44:17,190 --> 00:44:20,340 level difficult or not, and rated Flappy Bird levels that were dynamically 862 00:44:20,340 --> 00:44:22,562 generated based on some sort of scale. 863 00:44:22,562 --> 00:44:24,270 So if you're curious, it's in the slides, 864 00:44:24,270 --> 00:44:28,510 but I thought it was a cool find as I was putting together this lecture. 865 00:44:28,510 --> 00:44:34,200 So what we need to do is then, simply, add some negative value to gravity. 866 00:44:34,200 --> 00:44:35,922 Negative sort of anti-gravity. 867 00:44:35,922 --> 00:44:37,380 So we're going to go ahead do that. 868 00:44:37,380 --> 00:44:46,590 So in bird4 of the little mini repo that I have here, 869 00:44:46,590 --> 00:44:48,715 we're going to go ahead in main, first. 870 00:44:48,715 --> 00:44:55,540 871 00:44:55,540 --> 00:45:01,120 One thing that we want to do is because another part of this 872 00:45:01,120 --> 00:45:04,390 is taking input from the user, being able to jump, 873 00:45:04,390 --> 00:45:08,050 we want to be able to detect whether they've pressed space. 874 00:45:08,050 --> 00:45:15,260 But if we want to detect input for every single entity that we ever-- 875 00:45:15,260 --> 00:45:19,070 in an instance like this, it's not terribly important, 876 00:45:19,070 --> 00:45:22,840 but let's say we have 20 or 30 different kinds of entities, 877 00:45:22,840 --> 00:45:25,180 and they all have their own input handling, 878 00:45:25,180 --> 00:45:27,670 we don't want to clog our main with that, necessarily. 879 00:45:27,670 --> 00:45:30,760 So we can dedicate that-- 880 00:45:30,760 --> 00:45:34,700 delegate that, I should say, to another section of the code. 881 00:45:34,700 --> 00:45:37,120 In this case, we can sort of put our birds input 882 00:45:37,120 --> 00:45:40,080 handling together with our bird class, right, 883 00:45:40,080 --> 00:45:45,940 and expound upon the model of the class, taking control of the code and data 884 00:45:45,940 --> 00:45:49,850 for that particular object in our scene. 885 00:45:49,850 --> 00:46:01,420 So what we're going to do is in our love.load, 886 00:46:01,420 --> 00:46:05,190 I'm going to go ahead, and do something here. 887 00:46:05,190 --> 00:46:09,130 I'm going to go ahead and set love.keyboard.keysPressed 888 00:46:09,130 --> 00:46:10,130 equals a table. 889 00:46:10,130 --> 00:46:15,385 And what I'm doing is just adding onto a table that Love defined. 890 00:46:15,385 --> 00:46:16,810 It's called love.keyboard. 891 00:46:16,810 --> 00:46:20,890 I'm adding my own value into it called keysPressed, 892 00:46:20,890 --> 00:46:23,060 and I'm assigning it to an empty table. 893 00:46:23,060 --> 00:46:24,520 So what we're going to do-- 894 00:46:24,520 --> 00:46:29,230 this is now part of what Love gives us as part of its SDK, 895 00:46:29,230 --> 00:46:32,050 but it's something that we've created ourselves, 896 00:46:32,050 --> 00:46:36,190 and you can do this because in Lua, basically everything 897 00:46:36,190 --> 00:46:39,440 beyond basic variables are just tables, and you can manipulate tables 898 00:46:39,440 --> 00:46:40,360 however you want. 899 00:46:40,360 --> 00:46:42,150 In this case, love.keyboard is a table. 900 00:46:42,150 --> 00:46:43,720 I'm just adding a new key called keysPressed, 901 00:46:43,720 --> 00:46:45,835 and I'm assigning it to an empty table of my own. 902 00:46:45,835 --> 00:46:50,120 And we're going to see how this is actually used in just a moment. 903 00:46:50,120 --> 00:46:55,060 So I'm going to go ahead in our keyPressed function here-- 904 00:46:55,060 --> 00:46:58,540 905 00:46:58,540 --> 00:47:03,190 this function is called every time a user presses a key in the game, 906 00:47:03,190 --> 00:47:06,850 but I'm going to use it. 907 00:47:06,850 --> 00:47:11,320 Because it does that, I can go ahead, and just do something like this. 908 00:47:11,320 --> 00:47:19,540 Love.keyboard.keysPressed key gets true, and what that means is in this table 909 00:47:19,540 --> 00:47:23,500 that we've just defined, we've created ourselves, anytime the user 910 00:47:23,500 --> 00:47:27,220 presses any key, because love.keyPressed gets called for you, 911 00:47:27,220 --> 00:47:30,220 we can safely rest assured that this is going to get populated no matter 912 00:47:30,220 --> 00:47:32,290 what key they press because it's just something 913 00:47:32,290 --> 00:47:33,850 that Love2D takes care for you. 914 00:47:33,850 --> 00:47:35,810 But it's not getting stored until now. 915 00:47:35,810 --> 00:47:38,440 Now we're actually going to keep track of it 916 00:47:38,440 --> 00:47:44,180 in our own table for reasons that will become apparent very shortly. 917 00:47:44,180 --> 00:47:49,900 The next part of this code is defining a custom function. 918 00:47:49,900 --> 00:47:55,420 So the impetus for this is Love defines a couple of functions. 919 00:47:55,420 --> 00:48:00,190 It defines a function called love.keyboard.isdown, 920 00:48:00,190 --> 00:48:02,020 which takes in some key value, and you can 921 00:48:02,020 --> 00:48:06,700 use it to test for continuous input, which we did in the last lecture. 922 00:48:06,700 --> 00:48:10,600 We were saying hey, if up is down right now, or down is down, 923 00:48:10,600 --> 00:48:13,820 then we need to update our y velocity accordingly. 924 00:48:13,820 --> 00:48:16,540 But it doesn't have a mechanism like this for let's say, 925 00:48:16,540 --> 00:48:18,850 we want in some file other than main, to check 926 00:48:18,850 --> 00:48:22,090 for if a key was just pressed one time. 927 00:48:22,090 --> 00:48:24,880 It has this function, love.keyPressed, which takes a key, 928 00:48:24,880 --> 00:48:28,820 and that will trigger it, but we can't access this outside of this function 929 00:48:28,820 --> 00:48:31,870 because if we define this function in bird.lua, 930 00:48:31,870 --> 00:48:34,660 it's going to overwrite this implementation. 931 00:48:34,660 --> 00:48:37,900 And we don't necessarily want to have to worry about other files 932 00:48:37,900 --> 00:48:40,000 overwriting these functions because who knows-- 933 00:48:40,000 --> 00:48:41,380 if you're on a team, especially, who knows 934 00:48:41,380 --> 00:48:43,780 who's overwritten love.keyPressed, and what module in, 935 00:48:43,780 --> 00:48:47,620 and what order does it get loaded in, and what functions actually valid. 936 00:48:47,620 --> 00:48:51,220 We're going to take care of this problem by giving ourselves the ability 937 00:48:51,220 --> 00:48:54,520 to test for whether a key has been pressed on the last frame 938 00:48:54,520 --> 00:49:00,040 by implementing a function that we are also adding to the keyboard namespace, 939 00:49:00,040 --> 00:49:04,420 the keyboard table ourselves, called wasPressed. 940 00:49:04,420 --> 00:49:09,100 And it's going to take a key, and all it's going to do 941 00:49:09,100 --> 00:49:11,040 is check that table that we created before. 942 00:49:11,040 --> 00:49:18,250 It's going to say if love.keyboard.keysPressed key, then 943 00:49:18,250 --> 00:49:23,140 return true, else return false. 944 00:49:23,140 --> 00:49:27,220 And you could actually just return love.keyboard.keysPressed key, 945 00:49:27,220 --> 00:49:29,689 and it will be the exact same thing. 946 00:49:29,689 --> 00:49:31,480 And so what this has the effect of doing is 947 00:49:31,480 --> 00:49:35,860 saying, OK, because on the update, which we're about to see-- 948 00:49:35,860 --> 00:49:39,055 actually, I should probably do that before so this all gets tied together. 949 00:49:39,055 --> 00:49:42,600 950 00:49:42,600 --> 00:49:45,100 At the end of love.update, we're going to do one last thing, 951 00:49:45,100 --> 00:49:50,960 and that's reset that table because we want to just check frame by frame. 952 00:49:50,960 --> 00:49:55,470 So we have a table, a global table, that we've created 953 00:49:55,470 --> 00:49:57,330 to check for whether a key is pressed. 954 00:49:57,330 --> 00:50:00,615 We have a callback function that Love2D gives us that allows us to do that. 955 00:50:00,615 --> 00:50:02,490 So every time a key gets pressed, we're going 956 00:50:02,490 --> 00:50:07,230 to just add that key to that table, and set it to true. 957 00:50:07,230 --> 00:50:10,740 Now, we can just simply query that table anytime we want to with this function 958 00:50:10,740 --> 00:50:14,160 that we've created called love.keyboard.wasPressed key, which 959 00:50:14,160 --> 00:50:18,210 means on the last frame, was that key pressed? 960 00:50:18,210 --> 00:50:20,152 Basically return whether it's true or false. 961 00:50:20,152 --> 00:50:22,110 Now, the only problem is we're not flushing it. 962 00:50:22,110 --> 00:50:24,550 We're not ever setting that to false. 963 00:50:24,550 --> 00:50:27,540 That's has the effect of if we just press all the keys on our keyboard, 964 00:50:27,540 --> 00:50:32,370 those will always be true until we re-initialize the table 965 00:50:32,370 --> 00:50:35,040 to some empty value, which is what we do here. 966 00:50:35,040 --> 00:50:39,930 On the update, which takes place after all inputs been detected, 967 00:50:39,930 --> 00:50:44,580 we're going to just set that table to an empty table again. 968 00:50:44,580 --> 00:50:48,880 And on the next frame, it's going to-- whatever keys we pressed, those 969 00:50:48,880 --> 00:50:52,620 will get set to true, and then we can just query that table here as needed, 970 00:50:52,620 --> 00:50:55,320 and any update henceforth. 971 00:50:55,320 --> 00:51:00,262 So does anybody have any questions as to how this is operating? 972 00:51:00,262 --> 00:51:02,940 973 00:51:02,940 --> 00:51:06,542 And so the ultimate driving factor for us 974 00:51:06,542 --> 00:51:08,250 as to why we want to do this, why we want 975 00:51:08,250 --> 00:51:13,320 to put in the work to keep track of this global input table, 976 00:51:13,320 --> 00:51:23,550 is so that we can actually query input, single key input based in other files 977 00:51:23,550 --> 00:51:25,560 outside of main.lua because currently, all 978 00:51:25,560 --> 00:51:29,010 we can do to check for single key presses is look in main.lua, 979 00:51:29,010 --> 00:51:31,020 but that's not what we want to do. 980 00:51:31,020 --> 00:51:38,790 We're going to go ahead and go to our bird.lua, and in our update function, 981 00:51:38,790 --> 00:51:42,690 this is where we actually get to use our efforts, 982 00:51:42,690 --> 00:51:49,800 and say if love.keyboard.wasPressed space, 983 00:51:49,800 --> 00:51:54,190 which is the key that we want to actually allow us to jump, 984 00:51:54,190 --> 00:51:56,510 go ahead and set self dy to-- 985 00:51:56,510 --> 00:51:59,040 what should we set self.dy to when we press spacebar? 986 00:51:59,040 --> 00:52:01,990 987 00:52:01,990 --> 00:52:04,379 Should it be a positive or a negative value? 988 00:52:04,379 --> 00:52:05,170 AUDIENCE: Negative. 989 00:52:05,170 --> 00:52:06,910 COLTON OGDEN: A negative value. 990 00:52:06,910 --> 00:52:09,380 We'll set it to negative 5, and we should probably 991 00:52:09,380 --> 00:52:12,230 define this as an anti-gravity constant up here. 992 00:52:12,230 --> 00:52:16,010 But just for the sake of speed, we'll say self.dy gets negative 5. 993 00:52:16,010 --> 00:52:19,567 994 00:52:19,567 --> 00:52:20,650 And I did say that, right? 995 00:52:20,650 --> 00:52:21,750 I did say that. 996 00:52:21,750 --> 00:52:25,210 I'm going to go ahead, and go into bird4. 997 00:52:25,210 --> 00:52:28,472 Go ahead and run this example. 998 00:52:28,472 --> 00:52:32,950 And look at that, we're jumping. 999 00:52:32,950 --> 00:52:34,870 But we can still fall through the ground, 1000 00:52:34,870 --> 00:52:36,866 and we don't have any real game play. 1001 00:52:36,866 --> 00:52:37,990 But we've come a long ways. 1002 00:52:37,990 --> 00:52:41,410 Now we've taken single key input that we otherwise 1003 00:52:41,410 --> 00:52:43,750 didn't have the ability to do in Love2D, and we've 1004 00:52:43,750 --> 00:52:46,570 made it possible by just keeping track of our global input state, 1005 00:52:46,570 --> 00:52:49,780 and flushing it every update. 1006 00:52:49,780 --> 00:52:54,050 So does anybody have any questions as to how that works? 1007 00:52:54,050 --> 00:52:55,660 OK. 1008 00:52:55,660 --> 00:53:00,552 So the other big major visual component of Flappy Bird 1009 00:53:00,552 --> 00:53:02,510 are these pipes that we see here on the screen. 1010 00:53:02,510 --> 00:53:07,930 We have two pipes there, but the screen is filled with infinite pipes. 1011 00:53:07,930 --> 00:53:12,460 So does anybody have any instinct as to how we can implement this? 1012 00:53:12,460 --> 00:53:16,036 1013 00:53:16,036 --> 00:53:22,580 Well, we'll see before long, but suffice to say we'll need a new sprite. 1014 00:53:22,580 --> 00:53:25,710 We'll need some sort of way of keeping track 1015 00:53:25,710 --> 00:53:32,520 of when to spawn them because they'd sort of spawn after a period of time, 1016 00:53:32,520 --> 00:53:34,320 and that will be our gap. 1017 00:53:34,320 --> 00:53:40,354 And then what will happen if we just let it spawn forever and ever? 1018 00:53:40,354 --> 00:53:42,270 AUDIENCE: You have to destroy them as they go? 1019 00:53:42,270 --> 00:53:43,500 COLTON OGDEN: We do because if we don't do 1020 00:53:43,500 --> 00:53:45,780 that, after a certain period of time, we're allocating memory 1021 00:53:45,780 --> 00:53:46,950 for each of these pipes. 1022 00:53:46,950 --> 00:53:52,200 Not a ton of memory, just essentially, an x, a y, a width, and a height. 1023 00:53:52,200 --> 00:53:54,480 But because they all reference the same-- 1024 00:53:54,480 --> 00:53:57,639 they will reference the same sprite image, but given enough time, 1025 00:53:57,639 --> 00:54:00,180 eventually you're going to allocate a certain number of bytes 1026 00:54:00,180 --> 00:54:03,900 that will exceed your computer's memory, or the amount of allocated memory, 1027 00:54:03,900 --> 00:54:06,810 and you'll either hang infinitely, or crash. 1028 00:54:06,810 --> 00:54:11,950 And so we want to destroy them as they go as well. 1029 00:54:11,950 --> 00:54:16,650 So we're going to go ahead, and look at the final live coded example 1030 00:54:16,650 --> 00:54:20,120 just because from here on out, it's going to be a little bit much. 1031 00:54:20,120 --> 00:54:25,785 I'm going to go ahead, and go to main.lua first. 1032 00:54:25,785 --> 00:54:30,300 1033 00:54:30,300 --> 00:54:35,730 So just get my notes in order. 1034 00:54:35,730 --> 00:54:37,365 The first thing that we want to do-- 1035 00:54:37,365 --> 00:54:38,990 oh, I'm actually in the wrong repo too. 1036 00:54:38,990 --> 00:54:39,850 I apologize. 1037 00:54:39,850 --> 00:54:40,920 I was in the distro repo. 1038 00:54:40,920 --> 00:54:43,260 I want to be in the scratch repo. 1039 00:54:43,260 --> 00:54:52,510 So I'm going to go ahead, go into main, and I'm going to require pipe. 1040 00:54:52,510 --> 00:54:56,200 Now, we don't have a pipe yet, but this is a perfect example of how 1041 00:54:56,200 --> 00:54:58,990 we can keep abstracting our game. 1042 00:54:58,990 --> 00:55:02,110 We have a bird class, but we should also probably 1043 00:55:02,110 --> 00:55:05,810 have a pipe class because a pipe is a distinct type of entity in our game 1044 00:55:05,810 --> 00:55:06,310 world. 1045 00:55:06,310 --> 00:55:09,310 We can model it as a unit, we can give it functions, 1046 00:55:09,310 --> 00:55:13,280 we can give it data, and think about it in terms of it being a pipe, 1047 00:55:13,280 --> 00:55:17,770 not being a set of xy, width, height, et cetera. 1048 00:55:17,770 --> 00:55:20,530 Whatever data you want to ascribe to it, we 1049 00:55:20,530 --> 00:55:24,130 can abstract that out, and think in more abstract terms, which will 1050 00:55:24,130 --> 00:55:25,812 allow us to scale a little bit better. 1051 00:55:25,812 --> 00:55:28,520 So we're going to go ahead, and assume that we have a pipe class. 1052 00:55:28,520 --> 00:55:32,320 I'm going to go ahead, and add it to our folder here right now. 1053 00:55:32,320 --> 00:55:39,460 So do a new file, pipe.lua, and I'm going 1054 00:55:39,460 --> 00:55:42,370 to go ahead, and reference to notes here for just a second. 1055 00:55:42,370 --> 00:55:52,650 1056 00:55:52,650 --> 00:55:56,900 So the pipe class is actually quite simple, 1057 00:55:56,900 --> 00:55:59,115 just like the bird class was initially. 1058 00:55:59,115 --> 00:56:00,990 We don't need to keep track of a lot of data, 1059 00:56:00,990 --> 00:56:03,770 but we do want to keep track of a few things. 1060 00:56:03,770 --> 00:56:08,510 So the bird-- there's only ever going to be one bird out 1061 00:56:08,510 --> 00:56:11,900 at once, but with the pipes, we're going to be spawning them over, 1062 00:56:11,900 --> 00:56:13,170 and over again. 1063 00:56:13,170 --> 00:56:15,680 And so if we allocate them-- 1064 00:56:15,680 --> 00:56:20,030 for each pipe that we instantiate, if we allocate a new image, 1065 00:56:20,030 --> 00:56:22,550 this is probably not super efficient, right? 1066 00:56:22,550 --> 00:56:23,930 We're using the same exact data. 1067 00:56:23,930 --> 00:56:25,550 We have a bunch of pipes. 1068 00:56:25,550 --> 00:56:28,040 We only really need one sprite. 1069 00:56:28,040 --> 00:56:32,180 So outside of the init function-- so just below where 1070 00:56:32,180 --> 00:56:34,882 we're declaring that pipe is a class, we're 1071 00:56:34,882 --> 00:56:37,340 going to go ahead and create a local variable that is still 1072 00:56:37,340 --> 00:56:40,100 scoped to this file, but there's only ever going 1073 00:56:40,100 --> 00:56:44,280 to be one copy of this object. 1074 00:56:44,280 --> 00:56:46,750 We're going to go ahead and call it-- 1075 00:56:46,750 --> 00:56:50,120 say that we have pipe.png in this folder, 1076 00:56:50,120 --> 00:56:52,290 and this is separated out from the functions 1077 00:56:52,290 --> 00:56:54,331 that we're going to be defining in here, but this 1078 00:56:54,331 --> 00:56:59,180 has the effect of creating a semi global graphics object, 1079 00:56:59,180 --> 00:57:02,124 even though it's contained within this class file. 1080 00:57:02,124 --> 00:57:04,040 It's not accessible outside of this class file 1081 00:57:04,040 --> 00:57:05,390 because we don't need it to be. 1082 00:57:05,390 --> 00:57:09,290 But it's also not being instantiated every single time because recall, 1083 00:57:09,290 --> 00:57:16,430 if we look at bird.lua here, we're just setting it 1084 00:57:16,430 --> 00:57:20,600 as self.image gets love.graphics.newImage bird.png. 1085 00:57:20,600 --> 00:57:24,440 This will have the effect of allocating a new image every time 1086 00:57:24,440 --> 00:57:25,880 we create a bird object. 1087 00:57:25,880 --> 00:57:29,570 But we only ever create one bird object, so it's not really an important design 1088 00:57:29,570 --> 00:57:36,080 consideration for us to say, maybe we should create a semi global image up 1089 00:57:36,080 --> 00:57:37,250 here. 1090 00:57:37,250 --> 00:57:39,050 It's not important in this context. 1091 00:57:39,050 --> 00:57:43,670 Probably good style to do so anyway for larger projects, but just 1092 00:57:43,670 --> 00:57:44,837 a consideration for here. 1093 00:57:44,837 --> 00:57:46,670 Not really something we need to worry about. 1094 00:57:46,670 --> 00:57:51,020 But yes, definitely try to take an asset, 1095 00:57:51,020 --> 00:57:56,045 and reference it rather than allocate it as many times as possible. 1096 00:57:56,045 --> 00:57:59,090 1097 00:57:59,090 --> 00:58:00,950 We want our pipes to scroll. 1098 00:58:00,950 --> 00:58:02,330 So we need some sort of value. 1099 00:58:02,330 --> 00:58:04,039 Just like we did with the backgrounds, we 1100 00:58:04,039 --> 00:58:06,954 need some value that keeps track of whether these pipes are scrolling, 1101 00:58:06,954 --> 00:58:08,390 and it can be a constant value. 1102 00:58:08,390 --> 00:58:11,510 We're going to directly call it negative 60 this time, 1103 00:58:11,510 --> 00:58:18,360 and not negate it when we add it to our position later on. 1104 00:58:18,360 --> 00:58:24,590 So PIPE_SCROLL negative 60, we can just add it directly to our x, or to our-- 1105 00:58:24,590 --> 00:58:26,455 yeah, in this case, just to our x, and it 1106 00:58:26,455 --> 00:58:28,730 will have the-- times delta time, of course, 1107 00:58:28,730 --> 00:58:30,890 and that will have the effect of shifting 1108 00:58:30,890 --> 00:58:33,990 it left because it's a negative number. 1109 00:58:33,990 --> 00:58:35,490 We'll define the init function here. 1110 00:58:35,490 --> 00:58:37,760 So pipe init. 1111 00:58:37,760 --> 00:58:40,460 Within the init function, we're going to do a couple of things. 1112 00:58:40,460 --> 00:58:43,130 So it's x. 1113 00:58:43,130 --> 00:58:45,360 Where should the x be? 1114 00:58:45,360 --> 00:58:49,340 What should the x be set to, let's say, if we want the pipe to spawn 1115 00:58:49,340 --> 00:58:51,702 beyond the right edge of the screen? 1116 00:58:51,702 --> 00:58:55,630 AUDIENCE: [INAUDIBLE] 1117 00:58:55,630 --> 00:58:58,540 COLTON OGDEN: Virtual width, and you could also 1118 00:58:58,540 --> 00:59:01,420 say virtual width plus some number if you wanted to. 1119 00:59:01,420 --> 00:59:04,600 Because it's set to 0,0, it's going to have 1120 00:59:04,600 --> 00:59:07,660 the-- you won't see it on the frame that it gets instantiated, 1121 00:59:07,660 --> 00:59:12,310 but yes, virtual width or virtual width plus some constant value, or some 1122 00:59:12,310 --> 00:59:14,260 value that you've allocated ahead of time. 1123 00:59:14,260 --> 00:59:15,847 We'll just set it to virtual width. 1124 00:59:15,847 --> 00:59:18,430 So as soon as the pipe gets initialized, it will be invisible, 1125 00:59:18,430 --> 00:59:22,390 but it's going to be right on the right edge of the screen. 1126 00:59:22,390 --> 00:59:25,150 What about our y value? 1127 00:59:25,150 --> 00:59:29,260 First of all, let's take a look at what the image looks like so we can see. 1128 00:59:29,260 --> 00:59:31,210 It's going to be in our-- 1129 00:59:31,210 --> 00:59:33,775 I don't think I have the actual image in that directory. 1130 00:59:33,775 --> 00:59:34,960 So I'm going to come here. 1131 00:59:34,960 --> 00:59:36,520 I'm going to grab the pipe. 1132 00:59:36,520 --> 00:59:38,390 This is what the pipe looks like. 1133 00:59:38,390 --> 00:59:40,446 Let's see if I can expand it a little bit. 1134 00:59:40,446 --> 00:59:44,795 1135 00:59:44,795 --> 00:59:45,670 So it's kind of tall. 1136 00:59:45,670 --> 00:59:48,680 Where should we probably place it if we wanted 1137 00:59:48,680 --> 00:59:54,420 it to look similar to Flappy Bird? 1138 00:59:54,420 --> 00:59:58,050 Probably towards the lower end of the screen. 1139 00:59:58,050 --> 01:00:00,420 We can get fancy with it too, and we can even maybe 1140 01:00:00,420 --> 01:00:02,919 make it randomized just like Flappy Bird. 1141 01:00:02,919 --> 01:00:04,210 So we'll go ahead, and do that. 1142 01:00:04,210 --> 01:00:07,290 I'm going to go ahead, and copy this, and put it into our scratch folder 1143 01:00:07,290 --> 01:00:10,170 here. 1144 01:00:10,170 --> 01:00:14,190 Back in the init function, I'm going to go ahead, and set self.y too. 1145 01:00:14,190 --> 01:00:17,100 Because we want to talk about procedural generation, 1146 01:00:17,100 --> 01:00:19,850 this will be sort of our first foray into how we randomize this. 1147 01:00:19,850 --> 01:00:23,394 We'll be using the function that we used last week, 1148 01:00:23,394 --> 01:00:24,810 and this is a ubiquitous function. 1149 01:00:24,810 --> 01:00:29,440 You'll see this everywhere in any framework or game engine you use-- 1150 01:00:29,440 --> 01:00:31,900 math.random. 1151 01:00:31,900 --> 01:00:34,690 We want it to be the lower half of the screen. 1152 01:00:34,690 --> 01:00:38,700 So let's say virtual height divided by 4 is the upper bound, and maybe 1153 01:00:38,700 --> 01:00:44,950 virtual height minus 10 as the upper bound. 1154 01:00:44,950 --> 01:00:49,110 So that will have the effect of setting it to roughly a quarter of the screen. 1155 01:00:49,110 --> 01:00:52,694 Sorry, virtual height divided by 4 is towards the top end of the screen, 1156 01:00:52,694 --> 01:00:55,360 and then virtual height minus 10 is the lower end of the screen. 1157 01:00:55,360 --> 01:00:59,220 So it's actually going to cover anywhere from the first quarter below that, down 1158 01:00:59,220 --> 01:01:00,780 to about 10 pixels from the bottom. 1159 01:01:00,780 --> 01:01:03,902 AUDIENCE: Do you have to set the random seed in this [INAUDIBLE] 1160 01:01:03,902 --> 01:01:04,800 or do you do it main? 1161 01:01:04,800 --> 01:01:06,050 COLTON OGDEN: I do it in main. 1162 01:01:06,050 --> 01:01:11,430 So in this file, I am not sure if I did it for this demonstration. 1163 01:01:11,430 --> 01:01:13,395 It is definitely set in the repo. 1164 01:01:13,395 --> 01:01:16,245 1165 01:01:16,245 --> 01:01:18,750 I don't think I set it in this example, but yes, you 1166 01:01:18,750 --> 01:01:21,570 would set the random seed here if you wanted it to run every time. 1167 01:01:21,570 --> 01:01:22,080 Sorry. 1168 01:01:22,080 --> 01:01:26,850 And the question was should we set the random seed in the bird file, 1169 01:01:26,850 --> 01:01:29,130 or should we set it in main.lua? 1170 01:01:29,130 --> 01:01:32,590 Typically, you want to set it at the top level of your application. 1171 01:01:32,590 --> 01:01:36,210 So we're going to set it in-- 1172 01:01:36,210 --> 01:01:41,610 we're going to go ahead, and set it in main. 1173 01:01:41,610 --> 01:01:54,880 And the function itself is here, and I think it's starting in bird6, onwards. 1174 01:01:54,880 --> 01:01:55,610 So it will be-- 1175 01:01:55,610 --> 01:02:00,030 1176 01:02:00,030 --> 01:02:01,300 did I not set it? 1177 01:02:01,300 --> 01:02:04,254 I may not have set the random seed until later in the repo. 1178 01:02:04,254 --> 01:02:05,045 Let's check bird12. 1179 01:02:05,045 --> 01:02:09,810 1180 01:02:09,810 --> 01:02:13,170 So yes, math.randomseed, and then seed by os.time, 1181 01:02:13,170 --> 01:02:15,300 as we used last week in class. 1182 01:02:15,300 --> 01:02:18,160 1183 01:02:18,160 --> 01:02:20,850 I'll set it here. 1184 01:02:20,850 --> 01:02:23,370 Probably, we'll only run it once, but it'll have the effect. 1185 01:02:23,370 --> 01:02:27,780 Now we can run it several times just to see the difference in the pipes. 1186 01:02:27,780 --> 01:02:37,200 Let's go back to our pipe.lua here, and we have the x, we have the y. 1187 01:02:37,200 --> 01:02:39,700 So those are set accordingly. 1188 01:02:39,700 --> 01:02:41,340 We also want to set the width. 1189 01:02:41,340 --> 01:02:50,210 Does anybody recall what the function is to get a width of a graphics object 1190 01:02:50,210 --> 01:02:53,370 and the syntax for that? 1191 01:02:53,370 --> 01:02:56,420 So we have our image up here, pipe image. 1192 01:02:56,420 --> 01:02:58,700 It's love.graphic.newImage pipe.png. 1193 01:02:58,700 --> 01:03:01,480 1194 01:03:01,480 --> 01:03:02,944 AUDIENCE: [INAUDIBLE] 1195 01:03:02,944 --> 01:03:03,920 COLTON OGDEN: Exactly. 1196 01:03:03,920 --> 01:03:08,700 So we're going to go ahead, and set this to PIPE_IMAGE colon getWidth, 1197 01:03:08,700 --> 01:03:13,785 and that will become our new-- that will allow us to store our width for when 1198 01:03:13,785 --> 01:03:14,940 we will use it later. 1199 01:03:14,940 --> 01:03:18,430 1200 01:03:18,430 --> 01:03:20,770 And then we need a few other functions. 1201 01:03:20,770 --> 01:03:24,000 So the pipe will spawn, but it won't move because we haven't 1202 01:03:24,000 --> 01:03:26,700 applied any sort of scrolling to it. 1203 01:03:26,700 --> 01:03:29,520 We have the scrolling variable up on line five, 1204 01:03:29,520 --> 01:03:31,680 but we need to actually apply it to our pipe. 1205 01:03:31,680 --> 01:03:35,650 So we're going to go ahead, and create an update function. 1206 01:03:35,650 --> 01:03:38,580 And then in that update function, very similar to what 1207 01:03:38,580 --> 01:03:43,690 we've seen before already, PIPE_SCROLL times delta time. 1208 01:03:43,690 --> 01:03:46,560 And then lastly, we want to render our pipe. 1209 01:03:46,560 --> 01:03:50,640 So we're going to go ahead, and call a function that we've seen already today, 1210 01:03:50,640 --> 01:03:57,060 love.graphics.draw We're going to use the pipe image up above, 1211 01:03:57,060 --> 01:04:02,040 and then we're going to go ahead, and use self.x, 1212 01:04:02,040 --> 01:04:07,080 and self.y, and that's all we need for our pipe. 1213 01:04:07,080 --> 01:04:09,949 And let me make sure that that's all we really need. 1214 01:04:09,949 --> 01:04:12,990 So in main.lua-- we've got to go back to main.lua too because we actually 1215 01:04:12,990 --> 01:04:14,198 have to start spawning pipes. 1216 01:04:14,198 --> 01:04:17,790 1217 01:04:17,790 --> 01:04:20,990 So let's go ahead, and go to-- 1218 01:04:20,990 --> 01:04:23,790 let me pull up my code here one more time. 1219 01:04:23,790 --> 01:04:26,540 1220 01:04:26,540 --> 01:04:30,510 In main-- so on line 59-- 1221 01:04:30,510 --> 01:04:31,910 sorry, you won't see it. 1222 01:04:31,910 --> 01:04:34,200 You'll see it in line 59 in the actual distro code, 1223 01:04:34,200 --> 01:04:38,460 but for me, it's going to be slightly different. 1224 01:04:38,460 --> 01:04:41,210 We're going to go ahead, and create a new table 1225 01:04:41,210 --> 01:04:44,060 to keep track of all the pipes that we want to spawn because we 1226 01:04:44,060 --> 01:04:45,518 need a way to store them in memory. 1227 01:04:45,518 --> 01:04:48,080 We can't just set one variable to-- 1228 01:04:48,080 --> 01:04:53,690 basically, almost like a dynamic array in this case, or a linked list rather. 1229 01:04:53,690 --> 01:04:55,844 We're going to use this table just to hold them. 1230 01:04:55,844 --> 01:04:57,260 We're not going to give them keys. 1231 01:04:57,260 --> 01:04:59,270 We're just going to insert them like we would 1232 01:04:59,270 --> 01:05:03,830 do with just a linked list like in Python, for example. 1233 01:05:03,830 --> 01:05:07,880 We're going to go ahead, and what do we need 1234 01:05:07,880 --> 01:05:13,295 to do if we want to have them spawn after a certain period of time? 1235 01:05:13,295 --> 01:05:17,030 1236 01:05:17,030 --> 01:05:19,000 Probably want to have some sort of timer. 1237 01:05:19,000 --> 01:05:21,410 We want to keep track of how much time has passed, 1238 01:05:21,410 --> 01:05:23,720 and maybe have some sort of amount of time 1239 01:05:23,720 --> 01:05:26,340 that's our trigger to spawn of a pipe. 1240 01:05:26,340 --> 01:05:28,290 Let's say maybe 2 seconds. 1241 01:05:28,290 --> 01:05:32,700 So if we set a timer to 0, it's just start just at 0, 1242 01:05:32,700 --> 01:05:35,390 but we can add to this frame by frame. 1243 01:05:35,390 --> 01:05:40,370 We can just increase this timer by delta time, whatever that is, frame by frame. 1244 01:05:40,370 --> 01:05:42,560 It'll be about 1/60 of a second. 1245 01:05:42,560 --> 01:05:47,600 So after 60 frames have passed, we'll get one second. 1246 01:05:47,600 --> 01:05:50,480 After 120 frames have passed, we'll have two seconds. 1247 01:05:50,480 --> 01:05:54,180 At that point, we can then decide OK, now it's time to spawn a new pipe. 1248 01:05:54,180 --> 01:05:56,100 Let's go ahead and do that. 1249 01:05:56,100 --> 01:06:06,780 So I'm going to go ahead, and in our update function, 1250 01:06:06,780 --> 01:06:12,900 we want to handle the actual increasing of this timer. 1251 01:06:12,900 --> 01:06:16,290 So it's as simple as-- 1252 01:06:16,290 --> 01:06:18,915 and I make sure that I called it spawnTimer. 1253 01:06:18,915 --> 01:06:20,130 No, I just called it timer. 1254 01:06:20,130 --> 01:06:21,755 Let's go ahead, and call it spawnTimer. 1255 01:06:21,755 --> 01:06:24,670 Be a little more specific about what we want here. 1256 01:06:24,670 --> 01:06:25,702 So our spawnTimer. 1257 01:06:25,702 --> 01:06:27,660 And then we're going to go ahead in our update, 1258 01:06:27,660 --> 01:06:34,740 and set spawnTimer equal to spawnTimer plus delta time. 1259 01:06:34,740 --> 01:06:39,390 And then what we need to do is then check 1260 01:06:39,390 --> 01:06:42,277 is our spawn timer greater than-- because it 1261 01:06:42,277 --> 01:06:44,610 keeps track of time in seconds, delta time will give you 1262 01:06:44,610 --> 01:06:45,990 a fractional amount in seconds. 1263 01:06:45,990 --> 01:06:49,510 So it will be at 0.013, or something like that. 1264 01:06:49,510 --> 01:06:54,120 We want to keep track of whether spawnTimer has gone past two, right? 1265 01:06:54,120 --> 01:07:02,580 So if spawnTimer is greater than 2, we want to add a new pipe. 1266 01:07:02,580 --> 01:07:05,840 Does anybody remember the function for how to add to a table in Lua? 1267 01:07:05,840 --> 01:07:09,450 1268 01:07:09,450 --> 01:07:12,520 So it's table.insert. 1269 01:07:12,520 --> 01:07:15,600 So table.insert will take in a table. 1270 01:07:15,600 --> 01:07:19,770 So in this case, we want the pipes table that we allocated before. 1271 01:07:19,770 --> 01:07:23,450 And then we're going to put in a new pipe object. 1272 01:07:23,450 --> 01:07:27,040 This is how you instantiate an object for call, parentheses. 1273 01:07:27,040 --> 01:07:29,480 That will have the effect of now our pipes 1274 01:07:29,480 --> 01:07:33,094 table is going to-- every time we call this, it's going to get a new index. 1275 01:07:33,094 --> 01:07:34,260 So it's going to start at 1. 1276 01:07:34,260 --> 01:07:37,222 Lua tables are indexed at 1. 1277 01:07:37,222 --> 01:07:39,320 The first time it happens, index1 is going 1278 01:07:39,320 --> 01:07:42,810 to be equal to a new pipe object, which is going to start 1279 01:07:42,810 --> 01:07:45,530 its xy at the edge of the screen. 1280 01:07:45,530 --> 01:07:49,430 Then index2 will be the exact same thing, a new pipe that's 1281 01:07:49,430 --> 01:07:52,370 at the edge of the screen, and so on, and so forth every time we 1282 01:07:52,370 --> 01:07:53,540 call table.insert. 1283 01:07:53,540 --> 01:07:56,180 1284 01:07:56,180 --> 01:07:59,750 Once our spawn Timer has exceeded 2, if we want this to not 1285 01:07:59,750 --> 01:08:06,020 spawn a pipe every frame here after, which would quickly clog up our world, 1286 01:08:06,020 --> 01:08:07,880 we want to reset our spawn timer to 0. 1287 01:08:07,880 --> 01:08:11,360 So this will have the effect of now, it's going to wait another 2 seconds, 1288 01:08:11,360 --> 01:08:13,457 and then this condition will be true again, 1289 01:08:13,457 --> 01:08:15,290 and then we can add a new pipe to the scene. 1290 01:08:15,290 --> 01:08:19,180 1291 01:08:19,180 --> 01:08:23,654 Let's go ahead and look at-- 1292 01:08:23,654 --> 01:08:29,240 we're going to need to add a new set of logic here. 1293 01:08:29,240 --> 01:08:39,040 Actually, I'm going to put all of this above the bird.update, 1294 01:08:39,040 --> 01:08:42,119 and then below that, I'm going to go ahead, and do-- 1295 01:08:42,119 --> 01:08:43,910 I'm not sure if we've covered this already. 1296 01:08:43,910 --> 01:08:46,835 I don't think we have, but if we want to iterate over a table, 1297 01:08:46,835 --> 01:08:48,960 there's a function that Lua gives you called pairs. 1298 01:08:48,960 --> 01:08:52,750 It will give you all the key value pairs of a table 1299 01:08:52,750 --> 01:08:55,149 that you can then use while you're iterating over it. 1300 01:08:55,149 --> 01:08:57,700 Similar to enumerate in Python, if familiar, 1301 01:08:57,700 --> 01:09:02,120 except this will actually give you the keys rather than just the indices. 1302 01:09:02,120 --> 01:09:11,890 So we can do for k, pipe in pairs of pipes do some body of code, 1303 01:09:11,890 --> 01:09:14,890 and then we have access to the key and the pipe within this. 1304 01:09:14,890 --> 01:09:17,510 We can just iterate over it, and use it. 1305 01:09:17,510 --> 01:09:20,580 So the first thing we want to do is we want to update our pipe. 1306 01:09:20,580 --> 01:09:22,330 So for each pipe, update it. 1307 01:09:22,330 --> 01:09:26,770 Give it the delta time of the current frame. 1308 01:09:26,770 --> 01:09:28,929 And then what was the other important feature? 1309 01:09:28,929 --> 01:09:30,970 So this will have the effect of scrolling it now. 1310 01:09:30,970 --> 01:09:33,370 It's going to get its x shifted, but what 1311 01:09:33,370 --> 01:09:36,861 was the other important thing we needed to do with every pipe in our scene? 1312 01:09:36,861 --> 01:09:42,410 AUDIENCE: When x is less than 0, we have to [INAUDIBLE] 1313 01:09:42,410 --> 01:09:44,090 COLTON OGDEN: Yes. 1314 01:09:44,090 --> 01:09:45,600 That is exactly true. 1315 01:09:45,600 --> 01:09:50,680 So what we're going to do is if pipe.x is less than-- 1316 01:09:50,680 --> 01:09:55,331 so if we did less than 0, what do we think would happen? 1317 01:09:55,331 --> 01:09:58,850 AUDIENCE: [INAUDIBLE] 1318 01:09:58,850 --> 01:10:01,940 COLTON OGDEN: We would see it instantly disappear because they're 1319 01:10:01,940 --> 01:10:03,590 based on the top left coordinates. 1320 01:10:03,590 --> 01:10:07,830 So what we need to do is keep track of its width. 1321 01:10:07,830 --> 01:10:09,980 So what we'll do is we'll just say if pipe.x 1322 01:10:09,980 --> 01:10:15,500 is less than negative pipe.width, which will allow the pipe 1323 01:10:15,500 --> 01:10:19,860 to go all the way past the edge of the screen, 1324 01:10:19,860 --> 01:10:23,450 we'll call a function called table.remove, which takes a table, 1325 01:10:23,450 --> 01:10:26,600 in this case, pipes, and then it takes a key. 1326 01:10:26,600 --> 01:10:29,480 And the key we have access to up above on line 124. 1327 01:10:29,480 --> 01:10:32,090 We can just say k, and that will have the effect 1328 01:10:32,090 --> 01:10:36,560 of removing that pipe from the scene. 1329 01:10:36,560 --> 01:10:40,370 And then as soon as that's done, we're good to go. 1330 01:10:40,370 --> 01:10:42,817 The last thing that we need to do is currently, 1331 01:10:42,817 --> 01:10:44,900 we're not actually drawing the pipe to the screen. 1332 01:10:44,900 --> 01:10:50,750 So down below in our render function, we're going to go ahead, and up above-- 1333 01:10:50,750 --> 01:10:55,310 before we do the ground, because if we do it normally-- 1334 01:10:55,310 --> 01:10:57,764 if we do it after we render the ground, the pipes 1335 01:10:57,764 --> 01:11:00,680 are going look like they're just kind of layered on top of the ground. 1336 01:11:00,680 --> 01:11:03,620 We want it to look as if they're sticking out from the ground. 1337 01:11:03,620 --> 01:11:06,830 So what we want to do is have a correct render layer, 1338 01:11:06,830 --> 01:11:08,780 a render draw order to the screen. 1339 01:11:08,780 --> 01:11:11,810 We draw the background, we draw the pipes, then we draw the ground, 1340 01:11:11,810 --> 01:11:14,120 and this will have the effect of looking as if the pipes are sticking out 1341 01:11:14,120 --> 01:11:15,140 of the ground. 1342 01:11:15,140 --> 01:11:18,170 So what we'll do is we'll do the exact same thing we just did up 1343 01:11:18,170 --> 01:11:31,050 above by saying for k, pipe in pairs of pipes do pipe, 1344 01:11:31,050 --> 01:11:33,480 and then the render function that we defined in pipe. 1345 01:11:33,480 --> 01:11:35,230 And this will have the effect of iterating 1346 01:11:35,230 --> 01:11:37,530 through all the pipes in our scene every draw call, 1347 01:11:37,530 --> 01:11:41,160 and drawing them before it draws the ground, and before it draws the bird, 1348 01:11:41,160 --> 01:11:44,534 and that should be all that we need to illustrate this example. 1349 01:11:44,534 --> 01:11:46,075 Let me make sure everything is saved. 1350 01:11:46,075 --> 01:11:50,660 I'm going to go ahead, and go into bird5. 1351 01:11:50,660 --> 01:11:56,610 If I did everything correctly, this should, after a certain period of time, 1352 01:11:56,610 --> 01:11:59,820 drop pipes to the screen that are scrolling, and they're randomized. 1353 01:11:59,820 --> 01:12:02,760 Their y value is getting set to some value 1354 01:12:02,760 --> 01:12:05,610 between the top quarter of the screen. 1355 01:12:05,610 --> 01:12:09,190 So starting about right where Flappy Bird is right now, down 1356 01:12:09,190 --> 01:12:12,017 about 10 pixels above the width of the screen, which actually, 1357 01:12:12,017 --> 01:12:13,350 that looks like 10 pixels above. 1358 01:12:13,350 --> 01:12:14,570 So that's a slight bug. 1359 01:12:14,570 --> 01:12:18,030 It should probably be something along the lines of 30 or 40. 1360 01:12:18,030 --> 01:12:19,890 We won't encounter that in the final distro 1361 01:12:19,890 --> 01:12:22,980 because they're not set to spawn that low, 1362 01:12:22,980 --> 01:12:27,510 but you can see how this is sort of the beginning of our procedural level 1363 01:12:27,510 --> 01:12:31,480 generation system, and we have most all the components of our scene. 1364 01:12:31,480 --> 01:12:35,040 Now, normally in Flappy Bird, we have two pipes. 1365 01:12:35,040 --> 01:12:38,730 We have a pipe that's above, and then a pipe that's below, 1366 01:12:38,730 --> 01:12:39,666 and they're in pairs. 1367 01:12:39,666 --> 01:12:42,540 In the next example, we're actually going to start illustrating this. 1368 01:12:42,540 --> 01:12:45,360 We're going to have pairs of pipes that are joined together, 1369 01:12:45,360 --> 01:12:46,620 which scroll together. 1370 01:12:46,620 --> 01:12:51,240 That once you fly through them, you score a point. 1371 01:12:51,240 --> 01:12:53,130 But for now, we have all the pieces that we 1372 01:12:53,130 --> 01:13:00,310 need in order to have the basic visual sense of the game completed. 1373 01:13:00,310 --> 01:13:04,450 We're going to take like a five minute break now, and then once we come back, 1374 01:13:04,450 --> 01:13:07,850 we'll actually dive into how we can get pairs of pipes into our scene, 1375 01:13:07,850 --> 01:13:10,790 and start getting into scoring, and some other fun things like music. 1376 01:13:10,790 --> 01:13:14,050 1377 01:13:14,050 --> 01:13:15,250 All right, welcome back. 1378 01:13:15,250 --> 01:13:16,600 So the next part-- 1379 01:13:16,600 --> 01:13:20,790 so before we establish the bird, the background, the pipes, 1380 01:13:20,790 --> 01:13:23,940 we have all the visual aspects of our game ready to go. 1381 01:13:23,940 --> 01:13:26,190 The next important piece of the puzzle to really solve 1382 01:13:26,190 --> 01:13:29,550 is how can we start scoring our game, and also how can we 1383 01:13:29,550 --> 01:13:33,330 get the pipes matching the way that they're implemented in the actual game? 1384 01:13:33,330 --> 01:13:38,430 Which, recall, they're normally in pairs, as illustrated here. 1385 01:13:38,430 --> 01:13:41,430 And we also see on the right-hand side, as we've covered already so far, 1386 01:13:41,430 --> 01:13:44,130 we have the spawn zone for our pipes, and on the left, 1387 01:13:44,130 --> 01:13:46,050 we have what I've labeled the dead zone, where 1388 01:13:46,050 --> 01:13:49,980 pipes are de-instantiated once they've gone 1389 01:13:49,980 --> 01:13:52,410 past the negative width of themselves. 1390 01:13:52,410 --> 01:13:55,380 But pipes come in pairs, they get shifted, 1391 01:13:55,380 --> 01:13:58,941 and once the bird flies between these gaps, 1392 01:13:58,941 --> 01:14:00,690 is ultimately when they've scored a point. 1393 01:14:00,690 --> 01:14:07,620 And so we need a way to pair pipes together, and define this logic for how 1394 01:14:07,620 --> 01:14:11,010 can we tell whether the bird has gone past the gap, 1395 01:14:11,010 --> 01:14:14,500 and whether or not the pipes have been de-instantiated. 1396 01:14:14,500 --> 01:14:17,760 So we're going to go ahead, and I'm going to probably 1397 01:14:17,760 --> 01:14:20,760 stop live coding for the rest of the demonstrations 1398 01:14:20,760 --> 01:14:23,730 because they're going to be a little bit more complex. 1399 01:14:23,730 --> 01:14:28,190 But I believe my code editor is over here. 1400 01:14:28,190 --> 01:14:30,690 I'm going to go ahead, and open up-- 1401 01:14:30,690 --> 01:14:32,680 oh, this is my other editor. 1402 01:14:32,680 --> 01:14:37,030 So in the base repo now, we're going to go ahead, and look at the full example. 1403 01:14:37,030 --> 01:14:40,930 So in bird6, which is the pipe pair update-- 1404 01:14:40,930 --> 01:14:44,440 our current subfolder that we're looking at-- 1405 01:14:44,440 --> 01:14:45,660 we're going to start in main. 1406 01:14:45,660 --> 01:14:51,390 So on line 33 in main. 1407 01:14:51,390 --> 01:14:53,820 We can see that we're acquiring pipe pair, which 1408 01:14:53,820 --> 01:14:55,440 is a new class we're defining. 1409 01:14:55,440 --> 01:14:58,350 We're taking the pipe that we had before, 1410 01:14:58,350 --> 01:15:00,500 and we're creating a new composite class. 1411 01:15:00,500 --> 01:15:04,440 So we're going to take a class that encapsulates two pipes together, 1412 01:15:04,440 --> 01:15:08,400 a pair of pipes, and we're going to use this to think about our problem more 1413 01:15:08,400 --> 01:15:10,050 abstractly than we already are. 1414 01:15:10,050 --> 01:15:13,669 And this layering of abstractions is a very important concept 1415 01:15:13,669 --> 01:15:15,960 in computer science, generally speaking, but especially 1416 01:15:15,960 --> 01:15:19,470 in games where you might have objects that are composites of objects that 1417 01:15:19,470 --> 01:15:22,350 are composites of objects, and these abstract hierarchies are 1418 01:15:22,350 --> 01:15:24,600 sort of what keeps programmers sane when dealing 1419 01:15:24,600 --> 01:15:28,800 with such large levels of-- when you have thousands of lines of code, 1420 01:15:28,800 --> 01:15:32,550 it's sort of the only way you can really make sense of it. 1421 01:15:32,550 --> 01:15:35,430 So on line 65, if we look-- 1422 01:15:35,430 --> 01:15:39,060 now, instead of a table that's called pipes, we've renamed it to pipe pairs. 1423 01:15:39,060 --> 01:15:41,670 We're no longer going to store individual pipes in our scene. 1424 01:15:41,670 --> 01:15:44,100 We're going to take these pipe pairs that we're 1425 01:15:44,100 --> 01:15:51,270 going to create, and store them in our table as well as individual units. 1426 01:15:51,270 --> 01:15:56,390 On line 71, we need a variable to keep track of the-- 1427 01:15:56,390 --> 01:15:58,500 we're calling it last y. 1428 01:15:58,500 --> 01:16:00,930 The purpose of this variable is so that we 1429 01:16:00,930 --> 01:16:04,620 can keep track of where the last set of pipes spawned their gap. 1430 01:16:04,620 --> 01:16:08,060 Because if we made our gaps completely random, 1431 01:16:08,060 --> 01:16:12,840 it will have the effect of not looking continuous for one, 1432 01:16:12,840 --> 01:16:15,450 and also potentially being impossible to beat. 1433 01:16:15,450 --> 01:16:19,580 We want some sort of smooth contour to our gaps 1434 01:16:19,580 --> 01:16:21,640 so that we can fly through them reasonably, 1435 01:16:21,640 --> 01:16:24,810 and that it looks as if it was almost pre-made, and smooth. 1436 01:16:24,810 --> 01:16:27,270 So we're going to keep track of a variable called last y. 1437 01:16:27,270 --> 01:16:30,180 We're going to start it off at negative pipe height. 1438 01:16:30,180 --> 01:16:37,830 So up past the top of the screen, plus some sort of value between 1 and 80 1439 01:16:37,830 --> 01:16:39,030 and 20. 1440 01:16:39,030 --> 01:16:42,340 It's going to be roughly towards the top of the screen. 1441 01:16:42,340 --> 01:16:45,900 And this is important because the last y is 1442 01:16:45,900 --> 01:16:49,976 going to be-- we're going to end up flipping our sprite. 1443 01:16:49,976 --> 01:16:55,020 A flip on the y-axis has the result of the sprite looking 1444 01:16:55,020 --> 01:16:59,580 as if it's gone its whole height above where its actual y is, 1445 01:16:59,580 --> 01:17:02,400 and we'll see in more detail, shortly, why 1446 01:17:02,400 --> 01:17:05,690 this ends up working the way it does. 1447 01:17:05,690 --> 01:17:15,150 We're going to go down to line 132, and in our condition, 1448 01:17:15,150 --> 01:17:20,220 if our spawnTimer is greater than 2, what we're going to do 1449 01:17:20,220 --> 01:17:22,830 is this is where we spawned our pipes before, 1450 01:17:22,830 --> 01:17:26,070 but now we're spawning pairs of pipes. 1451 01:17:26,070 --> 01:17:29,460 So we're going to set a local variable y. 1452 01:17:29,460 --> 01:17:31,230 It's going to be-- 1453 01:17:31,230 --> 01:17:34,050 this is the clamp operation that we talked about last week 1454 01:17:34,050 --> 01:17:38,730 using math.max and math.min to apply some sort of operation. 1455 01:17:38,730 --> 01:17:42,060 In this case, we're going to add a random value between negative 20 1456 01:17:42,060 --> 01:17:44,850 and 20 to whatever our last y value was, which 1457 01:17:44,850 --> 01:17:49,690 is going to shift the gap effectively by negative 20 or 20 pixels. 1458 01:17:49,690 --> 01:17:52,530 We're going to clamp it between negative pipe height plus 10. 1459 01:17:52,530 --> 01:17:56,100 So about 10 pixels from the top of the screen, 1460 01:17:56,100 --> 01:18:02,520 and then we're going to set the upper bound to virtual height minus 90 1461 01:18:02,520 --> 01:18:03,420 minus pipe height. 1462 01:18:03,420 --> 01:18:07,800 And this minus pipe light is only because we're doing a flip operation 1463 01:18:07,800 --> 01:18:09,750 on our y-axis for our sprite. 1464 01:18:09,750 --> 01:18:11,850 I'll go into it in a little bit more detail 1465 01:18:11,850 --> 01:18:14,880 to try to make it clear as to why we're doing it, 1466 01:18:14,880 --> 01:18:17,760 and maybe I'll take out some codes to illustrate what it looks 1467 01:18:17,760 --> 01:18:20,190 like without that operation applied. 1468 01:18:20,190 --> 01:18:23,400 But basically, it has the effect of 90 pixels from the bottom 1469 01:18:23,400 --> 01:18:26,550 is where the gap could spawn. 1470 01:18:26,550 --> 01:18:29,540 So basically, the pipe at the very bottom. 1471 01:18:29,540 --> 01:18:31,600 Recall that this gap is where-- 1472 01:18:31,600 --> 01:18:35,370 this value is where the gap itself begins, not necessarily 1473 01:18:35,370 --> 01:18:38,680 where the pipe starts. 1474 01:18:38,680 --> 01:18:42,690 It'll be between negative pipe height plus 10. 1475 01:18:42,690 --> 01:18:47,820 Basically, effectively, between 10 pixels from the top of the screen, 1476 01:18:47,820 --> 01:18:50,560 between 90 pixels from the bottom of the screen, 1477 01:18:50,560 --> 01:18:55,530 and then we're going to apply a random permutation of this value. 1478 01:18:55,530 --> 01:18:58,590 We're going to add some value between negative 20 and 20, 1479 01:18:58,590 --> 01:19:03,000 and that will give us a contour, and it'll be a randomized contour. 1480 01:19:03,000 --> 01:19:06,540 1481 01:19:06,540 --> 01:19:09,960 Line 136, we have pipe pairs. 1482 01:19:09,960 --> 01:19:14,070 Table insert into that instead of pipes, and we're just adding a new pipe pair, 1483 01:19:14,070 --> 01:19:18,030 and we're setting it to the value y. 1484 01:19:18,030 --> 01:19:21,820 And then the pipe pair takes in a y value, 1485 01:19:21,820 --> 01:19:23,930 and that will be where the start of the gap is. 1486 01:19:23,930 --> 01:19:25,980 And what this will have the effect of doing 1487 01:19:25,980 --> 01:19:29,790 is it's going to flip a sprite above the gap 1488 01:19:29,790 --> 01:19:32,880 so that we have a pipe right above where the gap starts, and then it's 1489 01:19:32,880 --> 01:19:37,380 going to draw another pipe unflipped about 90 pixels below that, 1490 01:19:37,380 --> 01:19:39,735 and that will be how it puts the two together. 1491 01:19:39,735 --> 01:19:42,850 1492 01:19:42,850 --> 01:19:49,380 Line 144 is a loop that just updates our pairs instead of our pipes. 1493 01:19:49,380 --> 01:19:55,110 So all we've done here is just renamed it from pipe to pair, and instead 1494 01:19:55,110 --> 01:19:57,030 of pipes, we're using pipe pairs. 1495 01:19:57,030 --> 01:20:01,390 We are doing the same exact thing here on line 153. 1496 01:20:01,390 --> 01:20:05,490 We've done for k pair in pairs of pipe pairs. 1497 01:20:05,490 --> 01:20:08,310 And then line 150-- 1498 01:20:08,310 --> 01:20:12,210 sorry, line 175 is where we are-- 1499 01:20:12,210 --> 01:20:17,790 sorry, 170 is where we are rendering each pair instead of each pipe. 1500 01:20:17,790 --> 01:20:22,980 And so if we open up pipe pair here, we can take a look 1501 01:20:22,980 --> 01:20:25,060 at this class from scratch. 1502 01:20:25,060 --> 01:20:26,700 So it's a new class. 1503 01:20:26,700 --> 01:20:29,122 We're going to set our gap height to 90 pixels, 1504 01:20:29,122 --> 01:20:31,080 and so this is just some arbitrary value that I 1505 01:20:31,080 --> 01:20:33,879 felt was a pretty fair value in terms of size, 1506 01:20:33,879 --> 01:20:35,670 but you can tune this to whatever you want. 1507 01:20:35,670 --> 01:20:37,570 You could set this to-- 1508 01:20:37,570 --> 01:20:40,520 if you want to be really cruel, you could set it to something like 50. 1509 01:20:40,520 --> 01:20:42,420 Or if you wanted to be really generous to the player, 1510 01:20:42,420 --> 01:20:44,430 you could set it to something like 150, and make 1511 01:20:44,430 --> 01:20:46,410 it fairly easy for them to get through. 1512 01:20:46,410 --> 01:20:50,430 Or as part of the assignment, you could randomize it 1513 01:20:50,430 --> 01:20:54,420 so that it varies pair by pair, and you get more 1514 01:20:54,420 --> 01:20:56,445 of an organic looking obstacle course. 1515 01:20:56,445 --> 01:20:59,010 It's still shifted by negative 20 to 20 pixels, 1516 01:20:59,010 --> 01:21:02,939 but now your gap varies, and you can also randomize the shift amount 1517 01:21:02,939 --> 01:21:03,980 if you wanted to as well. 1518 01:21:03,980 --> 01:21:05,460 Let's say you wanted-- 1519 01:21:05,460 --> 01:21:08,970 maybe you want the gaps to be up to 40 pixels 1520 01:21:08,970 --> 01:21:11,190 difference instead of 20 pixels difference 1521 01:21:11,190 --> 01:21:12,755 on negative and positive value. 1522 01:21:12,755 --> 01:21:14,130 You could easily do that as well. 1523 01:21:14,130 --> 01:21:16,840 1524 01:21:16,840 --> 01:21:20,760 On line 18 we're just setting our x to just like 1525 01:21:20,760 --> 01:21:23,010 we did before, virtual width plus 32. 1526 01:21:23,010 --> 01:21:24,990 So we're setting it to the-- 1527 01:21:24,990 --> 01:21:27,090 actually, before we just set it to virtual width. 1528 01:21:27,090 --> 01:21:29,880 Now we're setting it to virtual width plus 32. 1529 01:21:29,880 --> 01:21:31,474 Both are pretty much equal. 1530 01:21:31,474 --> 01:21:33,390 This will just give it a little bit of a delay 1531 01:21:33,390 --> 01:21:36,780 before it ends up going onto the screen, but you can effectively just 1532 01:21:36,780 --> 01:21:38,190 do this, virtual width. 1533 01:21:38,190 --> 01:21:41,680 1534 01:21:41,680 --> 01:21:47,130 On the next line, 24, this is where we sort of bundled together 1535 01:21:47,130 --> 01:21:51,300 the pipes that we're going to end up actually rendering and updating 1536 01:21:51,300 --> 01:21:52,100 to the screen. 1537 01:21:52,100 --> 01:21:55,560 Instead of having just one pipe, a pipe pair is two pipes. 1538 01:21:55,560 --> 01:21:58,090 We can easily put this together in a table. 1539 01:21:58,090 --> 01:21:59,860 So we'll just create self.pipes. 1540 01:21:59,860 --> 01:22:03,420 We'll set it to a table that has two keys, upper and lower, 1541 01:22:03,420 --> 01:22:06,390 and the upper pipe is just a pipe. 1542 01:22:06,390 --> 01:22:08,490 And notice one thing is different about pipe. 1543 01:22:08,490 --> 01:22:10,890 Now, before, it took no arguments. 1544 01:22:10,890 --> 01:22:12,180 It was just a regular pipe. 1545 01:22:12,180 --> 01:22:13,740 Pipes had their own logic. 1546 01:22:13,740 --> 01:22:15,600 They set their own x and y. 1547 01:22:15,600 --> 01:22:19,620 They didn't need any sort of parametrization beyond that. 1548 01:22:19,620 --> 01:22:22,750 It was all taken care of for them randomly. 1549 01:22:22,750 --> 01:22:24,910 Now, they take a string. 1550 01:22:24,910 --> 01:22:28,140 So this top string means that this would be a top pipe. 1551 01:22:28,140 --> 01:22:30,330 So that means that if this pipe is a top pipe, 1552 01:22:30,330 --> 01:22:33,840 there's probably going to be logic in pipe that now checks 1553 01:22:33,840 --> 01:22:35,940 to see whether it's top or bottom. 1554 01:22:35,940 --> 01:22:39,180 If it's top, then we need to render it upside down. 1555 01:22:39,180 --> 01:22:44,400 We need to flip it along the y-axis, and then we're going to set it to self.y. 1556 01:22:44,400 --> 01:22:47,160 1557 01:22:47,160 --> 01:22:49,740 And recall that we set self.y-- 1558 01:22:49,740 --> 01:22:52,510 we passed in self.y in main. 1559 01:22:52,510 --> 01:22:54,390 Actually, I'm not sure if I touched on that. 1560 01:22:54,390 --> 01:22:56,770 Let's go back to main here. 1561 01:22:56,770 --> 01:22:58,153 So if we go to-- 1562 01:22:58,153 --> 01:23:02,320 1563 01:23:02,320 --> 01:23:07,580 I need to figure out where I actually instantiate the pipes. 1564 01:23:07,580 --> 01:23:11,090 Here on line 136, after we've calculated where 1565 01:23:11,090 --> 01:23:13,220 we want the gap to be for this pipe pair, 1566 01:23:13,220 --> 01:23:18,170 we're going to go ahead, and insert into pipe pairs, a pipe pair at y. 1567 01:23:18,170 --> 01:23:24,950 Y was the calculation between-- we basically took the last y value, 1568 01:23:24,950 --> 01:23:29,570 the last gap that we instantiated, and then shifted it by some negative 20 1569 01:23:29,570 --> 01:23:33,500 to 20 pixels randomly, and made sure it didn't go above or beyond-- 1570 01:23:33,500 --> 01:23:35,150 above or below the edges of the screen. 1571 01:23:35,150 --> 01:23:38,570 1572 01:23:38,570 --> 01:23:45,770 Back in pipe pair, we're going to go ahead, and look at line 30. 1573 01:23:45,770 --> 01:23:49,250 I'm sorry, actually let's take a look a little bit more closely here 1574 01:23:49,250 --> 01:23:50,840 at line 26. 1575 01:23:50,840 --> 01:23:53,360 So upper gets top and self.y. 1576 01:23:53,360 --> 01:23:59,250 That's where the gap is, and the sprite is going to be flipped upon that value. 1577 01:23:59,250 --> 01:24:02,990 The lower value is going to be a shift of that. 1578 01:24:02,990 --> 01:24:09,880 So the lower sprite needs to spawn below the top pipe by the gap amount 1579 01:24:09,880 --> 01:24:12,492 so that the two are top to bottom, but there 1580 01:24:12,492 --> 01:24:14,450 needs to be that space between the two of them. 1581 01:24:14,450 --> 01:24:17,840 So we need to take that pipe, shift it down, and then draw the next pipe. 1582 01:24:17,840 --> 01:24:25,760 So we're going to take self.y plus pipe height plus gap height, 1583 01:24:25,760 --> 01:24:29,630 and that'll have the effect-- remember, gap height was 90 pixels. 1584 01:24:29,630 --> 01:24:36,300 The pipe height is a result of flipping the y-axis, 1585 01:24:36,300 --> 01:24:41,160 and having to shift it down the actual position. 1586 01:24:41,160 --> 01:24:48,085 So if we go back to line 30. 1587 01:24:48,085 --> 01:24:51,241 1588 01:24:51,241 --> 01:24:56,890 This is an interesting illustration of what 1589 01:24:56,890 --> 01:25:00,072 happens when you edit a table while you're iterating over a table, 1590 01:25:00,072 --> 01:25:01,780 and I'll show you this in detail shortly. 1591 01:25:01,780 --> 01:25:05,620 But basically, on line 30, we're setting a flag called remove to false. 1592 01:25:05,620 --> 01:25:09,170 And what this is going to do is before we were just destroying the objects. 1593 01:25:09,170 --> 01:25:11,920 Whenever it got past the edge of the screen, we just destroyed it. 1594 01:25:11,920 --> 01:25:15,580 But if we're iterating over a table of values, 1595 01:25:15,580 --> 01:25:22,810 let's say a table of pipe pairs, when you do a removal in most programming 1596 01:25:22,810 --> 01:25:28,870 language-- in Lua, when you do a removal of a table value, and it's not indexed, 1597 01:25:28,870 --> 01:25:33,640 or it's non-keyed, which means that it's indexed by numerical indices, 1598 01:25:33,640 --> 01:25:36,470 this will shift every other value down. 1599 01:25:36,470 --> 01:25:38,470 And so when you're iterating it, and you shift 1600 01:25:38,470 --> 01:25:41,530 everything down, the value you are currently manipulating, 1601 01:25:41,530 --> 01:25:45,970 let's say it's equal to 1, if you remove that value, 1602 01:25:45,970 --> 01:25:48,760 you shift everything beyond it down by 1. 1603 01:25:48,760 --> 01:25:50,650 But then you're going to increment up to 2, 1604 01:25:50,650 --> 01:25:55,510 and you're skipping over what was previously just 2, and is now 1. 1605 01:25:55,510 --> 01:25:59,160 So you're effectively skipping over one of your entries, 1606 01:25:59,160 --> 01:26:02,560 and it has buggy behavior in a lot of scenarios. 1607 01:26:02,560 --> 01:26:06,070 In this case, it causes the graphics to glitch a little bit 1608 01:26:06,070 --> 01:26:10,090 because it doesn't apply a pixel shift on one frame, 1609 01:26:10,090 --> 01:26:16,840 and so whenever a pipe gets removed-- and I can actually show this visually, 1610 01:26:16,840 --> 01:26:20,292 the first pipe left after that pipe gets removed 1611 01:26:20,292 --> 01:26:22,000 ends up moving a little bit to the right, 1612 01:26:22,000 --> 01:26:26,120 and so you get weird pipes shifting to the left of the bird on each frame. 1613 01:26:26,120 --> 01:26:29,380 So whenever you edit a table in place, make 1614 01:26:29,380 --> 01:26:32,100 sure not to delete while you're iterating over it. 1615 01:26:32,100 --> 01:26:34,870 It's going to cause buggy behavior. 1616 01:26:34,870 --> 01:26:40,030 And like I said, I'll illustrate this for you very shortly. 1617 01:26:40,030 --> 01:26:46,000 On line 36, we are performing the update logic. 1618 01:26:46,000 --> 01:26:50,740 Now, a pipe pair has two pipes, each with their own render components, 1619 01:26:50,740 --> 01:26:52,480 and their own positions. 1620 01:26:52,480 --> 01:26:55,030 We're using the code that we wrote before for pipe, 1621 01:26:55,030 --> 01:26:57,370 and we're going to try to expand upon it a little bit. 1622 01:26:57,370 --> 01:27:04,200 So we still wanted to defer a lot of that code to the pipe class, 1623 01:27:04,200 --> 01:27:10,060 and we want to update the pipes based on whether-- 1624 01:27:10,060 --> 01:27:13,630 We want to still keep track of their own x, and their render functions, 1625 01:27:13,630 --> 01:27:18,310 and so we're going to see, basically, if our pipe pair 1626 01:27:18,310 --> 01:27:22,300 x is greater than negative pipe width, which is the same exact logic that we 1627 01:27:22,300 --> 01:27:24,670 were using before. 1628 01:27:24,670 --> 01:27:33,160 Set our own x to that minus pipe speed times 1629 01:27:33,160 --> 01:27:35,800 delta time, which is the same operation we were doing before. 1630 01:27:35,800 --> 01:27:42,320 But we are also editing the x of our self.pipes lower and upper, 1631 01:27:42,320 --> 01:27:45,910 and this will allow us to-- on line 46-- 1632 01:27:45,910 --> 01:27:47,920 render the pipes just as we were doing before 1633 01:27:47,920 --> 01:27:51,440 because they're getting their x values updated just as they were before. 1634 01:27:51,440 --> 01:27:55,180 So we're effectively deferring the render phase to our pipes, 1635 01:27:55,180 --> 01:27:58,820 and not really needing to add any additional logic for that in our code. 1636 01:27:58,820 --> 01:28:01,360 1637 01:28:01,360 --> 01:28:04,830 We've made changes to pipe.lua as well, so I'm 1638 01:28:04,830 --> 01:28:08,910 going to go ahead, and open up pipe here. 1639 01:28:08,910 --> 01:28:14,710 1640 01:28:14,710 --> 01:28:20,710 And we've set the height and width of it as contents here. 1641 01:28:20,710 --> 01:28:23,020 So pipe height gets 288, and then happens 1642 01:28:23,020 --> 01:28:24,640 to be about the size of the screen. 1643 01:28:24,640 --> 01:28:25,750 Pipe width gets 70. 1644 01:28:25,750 --> 01:28:28,270 1645 01:28:28,270 --> 01:28:32,230 On 31, we're sitting self.orientation gets orientation. 1646 01:28:32,230 --> 01:28:34,420 Notice our init function, which was previously just 1647 01:28:34,420 --> 01:28:38,710 empty, it took no parameters, now takes an orientation, and it takes a y value. 1648 01:28:38,710 --> 01:28:44,980 The orientation is going to allow us to ask, basically, is our code 1649 01:28:44,980 --> 01:28:46,190 a top or a bottom pipe? 1650 01:28:46,190 --> 01:28:50,050 And if it's top pipe, we need to flip it, draw it, and shift it. 1651 01:28:50,050 --> 01:28:53,800 If it's a bottom pipe, we're just going to draw it normal, 1652 01:28:53,800 --> 01:28:58,240 and not perform any sort of fancy sprite flipping or anything like that. 1653 01:28:58,240 --> 01:29:02,060 Down here in the render function is where this actually happens. 1654 01:29:02,060 --> 01:29:05,620 So on line 39, we're drawing the pipe image as usual 1655 01:29:05,620 --> 01:29:12,490 at x, but at y, because when you flip a sprite 1656 01:29:12,490 --> 01:29:18,250 it ends up completely flipping the y-- it basically performs a mirror on it, 1657 01:29:18,250 --> 01:29:20,020 but it not at 0,0. 1658 01:29:20,020 --> 01:29:24,040 It basically shifts it up by pipe height amount. 1659 01:29:24,040 --> 01:29:29,910 We need to keep track of that, and draw it at self.y plus pipe height. 1660 01:29:29,910 --> 01:29:34,324 Because if we draw it at just self.y, because it's going to be mirrored, 1661 01:29:34,324 --> 01:29:36,490 and it's going to get shifted by pipe height amount, 1662 01:29:36,490 --> 01:29:38,760 it's going to be beyond the top edge of the screen. 1663 01:29:38,760 --> 01:29:41,470 We need to account for that, account for the fact 1664 01:29:41,470 --> 01:29:45,125 that we're flipping it on the y-axis, and bring it down. 1665 01:29:45,125 --> 01:29:47,222 AUDIENCE: Where's the code where you flip it? 1666 01:29:47,222 --> 01:29:49,930 COLTON OGDEN: The question is where is the code where we flip it. 1667 01:29:49,930 --> 01:29:52,660 So that's actually here on this line. 1668 01:29:52,660 --> 01:29:57,250 On this condition, we're saying if self.orientation is equal to top, 1669 01:29:57,250 --> 01:29:58,350 then we want to-- 1670 01:29:58,350 --> 01:30:02,722 so the parameters here, I'll comment this just for clarification. 1671 01:30:02,722 --> 01:30:05,320 AUDIENCE: So the draw function has a flip function? 1672 01:30:05,320 --> 01:30:07,240 COLTON OGDEN: It does, I'll show you here. 1673 01:30:07,240 --> 01:30:09,610 So this 0, we've added a few new parameters 1674 01:30:09,610 --> 01:30:11,950 to our love.graphics.drawfunction. 1675 01:30:11,950 --> 01:30:13,120 Zero is rotation. 1676 01:30:13,120 --> 01:30:14,930 We're not going to rotate it at all. 1677 01:30:14,930 --> 01:30:17,560 This is the scale on the x-axis. 1678 01:30:17,560 --> 01:30:21,590 So x scale, and this is the scale on the y-axis. 1679 01:30:21,590 --> 01:30:24,070 So if we apply a scale operation of one, it's 1680 01:30:24,070 --> 01:30:28,250 the same thing as doing no scale at all. 1681 01:30:28,250 --> 01:30:30,230 It's going to draw it on the x-axis. 1682 01:30:30,230 --> 01:30:31,640 It's just going draw it normally. 1683 01:30:31,640 --> 01:30:39,340 But if it's top, if this pipe has been set to an orientation of top, 1684 01:30:39,340 --> 01:30:41,710 we're going to set the scale to negative 1. 1685 01:30:41,710 --> 01:30:43,540 When you said a sprite-- 1686 01:30:43,540 --> 01:30:48,770 its scale factor to negative 1, it flips it along that axis, effectively. 1687 01:30:48,770 --> 01:30:51,130 And so that's how you get mirroring. 1688 01:30:51,130 --> 01:30:56,470 Most engines that allow you to apply scale operations to 2D textures or 2D 1689 01:30:56,470 --> 01:31:01,120 sprites, a negative operation on an axis will mirror it on that axis, 1690 01:31:01,120 --> 01:31:02,620 and so that's what we're doing here. 1691 01:31:02,620 --> 01:31:05,680 So we're mirroring it if it's a top pipe, 1692 01:31:05,680 --> 01:31:07,660 and we're also shifting its draw location 1693 01:31:07,660 --> 01:31:10,710 as well because when we mirror it, it's going to-- 1694 01:31:10,710 --> 01:31:12,970 at 0,0 it's going to do the same-- it's going 1695 01:31:12,970 --> 01:31:17,112 to basically draw the same exact thing, but mirrored on the y-axis. 1696 01:31:17,112 --> 01:31:18,070 So it's going to need-- 1697 01:31:18,070 --> 01:31:21,100 if we want to draw at a given location, flipped-- 1698 01:31:21,100 --> 01:31:23,410 still draw it at 0,0, but have it be flipped, 1699 01:31:23,410 --> 01:31:27,040 we need to account for that flip, and shift it downwards if that makes sense. 1700 01:31:27,040 --> 01:31:29,740 1701 01:31:29,740 --> 01:31:33,970 So that's essentially all that's involved there. 1702 01:31:33,970 --> 01:31:38,150 And I think that's pretty much all of the code. 1703 01:31:38,150 --> 01:31:42,100 So we have our pipes now that are being flipped. 1704 01:31:42,100 --> 01:31:44,460 If its a top pipe, it's going to get drawn shifted. 1705 01:31:44,460 --> 01:31:48,040 It's going to have its other pipe shifted down by that amount, 1706 01:31:48,040 --> 01:31:54,100 and its y-axis is going to be increased by the gap height 1707 01:31:54,100 --> 01:31:56,680 so that it gets drawn 90 pixels, however many pixels you 1708 01:31:56,680 --> 01:31:59,260 want to set below that pipe. 1709 01:31:59,260 --> 01:32:02,100 So we're going to go into demonstrate this. 1710 01:32:02,100 --> 01:32:10,360 Go up to fifty bird, the actual repo now, and the actual distro code. 1711 01:32:10,360 --> 01:32:13,510 I'm going to go into bird6, and I'm going to run it. 1712 01:32:13,510 --> 01:32:16,450 1713 01:32:16,450 --> 01:32:19,750 And now we have pipes that are actually rendering. 1714 01:32:19,750 --> 01:32:24,700 But we're missing a couple of important things. 1715 01:32:24,700 --> 01:32:28,120 Foremost, among them being that now we don't have collision detection yet. 1716 01:32:28,120 --> 01:32:30,430 So we can just fly through this course infinitely, 1717 01:32:30,430 --> 01:32:35,110 but notice that they're being shifted by a random value between negative 20 1718 01:32:35,110 --> 01:32:37,210 and 20 pixels. 1719 01:32:37,210 --> 01:32:41,980 It looks more or less like it's being generated with some sort of goal 1720 01:32:41,980 --> 01:32:42,780 in mind. 1721 01:32:42,780 --> 01:32:44,199 It's not haphazard. 1722 01:32:44,199 --> 01:32:46,240 It's not all over the place, but you could easily 1723 01:32:46,240 --> 01:32:50,380 find ways to tweak this such that maybe the gap height is 1724 01:32:50,380 --> 01:32:53,470 some value between 60 and 120. 1725 01:32:53,470 --> 01:32:57,413 And so you have easy and difficult pipes, or maybe you have-- 1726 01:32:57,413 --> 01:33:02,760 I think I'm so far below the screen that I can't even get back up anymore. 1727 01:33:02,760 --> 01:33:06,490 Oh, OK, that's a physics error. 1728 01:33:06,490 --> 01:33:10,570 When your value gets to a certain point, I think that's actually what it's doing 1729 01:33:10,570 --> 01:33:13,839 is it's actually overflowing the value, and setting it to a negative. 1730 01:33:13,839 --> 01:33:16,130 Or underflowing it, and setting it to a negative value, 1731 01:33:16,130 --> 01:33:21,850 and then incrementing it because it's gotten so large. 1732 01:33:21,850 --> 01:33:26,480 But you could easily modulate parameters such as the width between the pipes 1733 01:33:26,480 --> 01:33:29,570 as you saw in the diagram before, or the height, 1734 01:33:29,570 --> 01:33:32,030 or even the speed at which they move, and find 1735 01:33:32,030 --> 01:33:37,880 ways to tune it to make game play that actually works for whatever goal 1736 01:33:37,880 --> 01:33:40,230 you have in mind-- making it easier or more difficult. 1737 01:33:40,230 --> 01:33:42,260 And that's actually a topic that they talked about in that article 1738 01:33:42,260 --> 01:33:44,270 that I linked to before, where they generated levels 1739 01:33:44,270 --> 01:33:46,561 programmatically, and then tested them programmatically 1740 01:33:46,561 --> 01:33:50,410 to determine what makes level in Flappy Bird difficult or easy. 1741 01:33:50,410 --> 01:33:55,190 And so basically, those are the parameters 1742 01:33:55,190 --> 01:33:58,160 you need to way as you're thinking of procedural generation. 1743 01:33:58,160 --> 01:34:00,259 And procedural generation, ultimately, is just 1744 01:34:00,259 --> 01:34:02,300 taking values that you construct your scene with, 1745 01:34:02,300 --> 01:34:05,460 and just finding ways to just manipulate them randomly. 1746 01:34:05,460 --> 01:34:11,780 Math.random some value, and that's how you make random levels in a nutshell. 1747 01:34:11,780 --> 01:34:16,344 Making good random levels is another question. 1748 01:34:16,344 --> 01:34:18,719 AUDIENCE: This guy made a lot of money doing very little. 1749 01:34:18,719 --> 01:34:19,927 COLTON OGDEN: He did, he did. 1750 01:34:19,927 --> 01:34:22,430 There was a big controversy around this game back in 2013. 1751 01:34:22,430 --> 01:34:25,580 AUDIENCE: He had like a nervous breakdown, too, right? 1752 01:34:25,580 --> 01:34:27,913 COLTON OGDEN: I don't know if I read too much into that, 1753 01:34:27,913 --> 01:34:31,310 but I was doing a little bit of research, and was reading about some 1754 01:34:31,310 --> 01:34:32,890 of that stuff. 1755 01:34:32,890 --> 01:34:35,300 I've got to give him props for banking on that. 1756 01:34:35,300 --> 01:34:38,692 1757 01:34:38,692 --> 01:34:39,650 Now we have pipe pairs. 1758 01:34:39,650 --> 01:34:42,920 That's arguably the most complex part of the program 1759 01:34:42,920 --> 01:34:46,740 because going forward now, as we get into collision, 1760 01:34:46,740 --> 01:34:49,460 and some more concepts, collision is actually something 1761 01:34:49,460 --> 01:34:54,840 that we touched on last week, and it's all basically the same stuff. 1762 01:34:54,840 --> 01:35:00,750 So if we go into bird7, the next iteration of our application. 1763 01:35:00,750 --> 01:35:07,420 I'm going to go ahead, and open up main.lua, 1764 01:35:07,420 --> 01:35:10,680 and then we're going to go to line 74. 1765 01:35:10,680 --> 01:35:14,040 1766 01:35:14,040 --> 01:35:19,510 And in order to test collision, we don't have scoring in place yet, 1767 01:35:19,510 --> 01:35:22,960 but we need some way to determine oh, we collided with a pipe. 1768 01:35:22,960 --> 01:35:24,730 We need some sort of feedback. 1769 01:35:24,730 --> 01:35:29,340 So what we're going to do is I've just decided we should just pause the game. 1770 01:35:29,340 --> 01:35:31,980 So once we collide with a pipe, let's just pause instantly 1771 01:35:31,980 --> 01:35:34,577 so we know immediately, oh, we collided with a pipe. 1772 01:35:34,577 --> 01:35:36,660 So I'm going to set some variable called scrolling 1773 01:35:36,660 --> 01:35:39,239 at the top of the program in main to true. 1774 01:35:39,239 --> 01:35:39,905 We're scrolling. 1775 01:35:39,905 --> 01:35:42,339 We're going to start scrolling, but when I 1776 01:35:42,339 --> 01:35:44,880 don't want to scroll any more, when I want to pause the game, 1777 01:35:44,880 --> 01:35:47,580 this should get set to false. 1778 01:35:47,580 --> 01:35:55,950 So on line 120, if scrolling, then do all of this update logic 1779 01:35:55,950 --> 01:35:58,091 that we did before. 1780 01:35:58,091 --> 01:36:00,840 And then at the very end of that, we're resetting our input table. 1781 01:36:00,840 --> 01:36:03,330 So we can still take input, but no updates 1782 01:36:03,330 --> 01:36:05,940 will take place if scrolling is set to false. 1783 01:36:05,940 --> 01:36:09,030 All of this stuff is within this if-- 1784 01:36:09,030 --> 01:36:10,710 excuse me, if scrolling, then. 1785 01:36:10,710 --> 01:36:11,477 So very simple. 1786 01:36:11,477 --> 01:36:13,560 We'll just encapsulate it all within some variable 1787 01:36:13,560 --> 01:36:16,990 that we can turn on and off. 1788 01:36:16,990 --> 01:36:21,870 And then on 152, within that chunk of code 1789 01:36:21,870 --> 01:36:25,280 that is being contained within that if condition, 1790 01:36:25,280 --> 01:36:28,920 we're just doing a very simple iteration. 1791 01:36:28,920 --> 01:36:33,420 For each pipe, this should be for l pair in pairs of-- 1792 01:36:33,420 --> 01:36:34,590 oh no, sorry. 1793 01:36:34,590 --> 01:36:39,870 For every pipe in the pairs of-- 1794 01:36:39,870 --> 01:36:41,490 it's a nested for loop in this case. 1795 01:36:41,490 --> 01:36:46,824 So basically, within the loop that looks over every single pair, to update it, 1796 01:36:46,824 --> 01:36:49,990 we're doing another loop that's looping through with the pipes in that pair. 1797 01:36:49,990 --> 01:36:53,820 So it's only a loop of two iterations with the upper and the lower pipe. 1798 01:36:53,820 --> 01:36:57,930 We could just also say if bird collides with upper-- 1799 01:36:57,930 --> 01:37:00,960 basically, if pair.upper or pair.lower, or pair.pipes 1800 01:37:00,960 --> 01:37:03,960 to upper pair.pipes.lower, but this is a little cleaner. 1801 01:37:03,960 --> 01:37:04,847 It's more scalable. 1802 01:37:04,847 --> 01:37:07,680 We can add more pipes if we want to, even though it wouldn't happen. 1803 01:37:07,680 --> 01:37:12,480 But for every pipe in pair.pipes, we have a function here that we 1804 01:37:12,480 --> 01:37:14,940 haven't defined yet called bird:collides. 1805 01:37:14,940 --> 01:37:19,030 So if bird collides pipe-- so it takes in a pipe. 1806 01:37:19,030 --> 01:37:21,990 So it's going to return a true or false value, we know that-- 1807 01:37:21,990 --> 01:37:23,220 set scrolling to false. 1808 01:37:23,220 --> 01:37:26,590 So we collide, scrolling set to false, update logic 1809 01:37:26,590 --> 01:37:28,090 is going to get shut off completely. 1810 01:37:28,090 --> 01:37:31,620 So we're going to have the effect of pausing the game. 1811 01:37:31,620 --> 01:37:34,230 We're going to go into bird.lua right now, 1812 01:37:34,230 --> 01:37:36,700 and we're going to actually see how we implement this, 1813 01:37:36,700 --> 01:37:40,600 and it's going look very familiar to what we did last week. 1814 01:37:40,600 --> 01:37:47,750 So in bird.lua, this function here, from 29 down to 45, 1815 01:37:47,750 --> 01:37:51,350 it's just an aabb collision detection test that we did last week. 1816 01:37:51,350 --> 01:37:54,740 We're just checking to make sure any edges are-- 1817 01:37:54,740 --> 01:37:58,310 right edge, make sure that is to the left 1818 01:37:58,310 --> 01:38:00,440 of the right edge of the second box. 1819 01:38:00,440 --> 01:38:02,740 Bottom edge of box one should be-- 1820 01:38:02,740 --> 01:38:05,570 or bottom edge of box one should be above bottom edge or top 1821 01:38:05,570 --> 01:38:06,590 edge of box two. 1822 01:38:06,590 --> 01:38:10,520 If all of these things hold true, then return true. 1823 01:38:10,520 --> 01:38:11,440 Else, return false. 1824 01:38:11,440 --> 01:38:13,070 That means we have a collision. 1825 01:38:13,070 --> 01:38:18,107 And notice that I've shifted everything here by a couple of constant values. 1826 01:38:18,107 --> 01:38:19,940 Does anybody have any instinct as to why I'm 1827 01:38:19,940 --> 01:38:26,060 saying self.x plus 2 instead of just self.x self.width minus 4? 1828 01:38:26,060 --> 01:38:30,620 Why we're checking for that offset for the bird in this case 1829 01:38:30,620 --> 01:38:32,240 when it is compared with the pipe. 1830 01:38:32,240 --> 01:38:34,004 AUDIENCE: It's half the size of the bird? 1831 01:38:34,004 --> 01:38:35,420 COLTON OGDEN: It's not quite half. 1832 01:38:35,420 --> 01:38:37,160 It's a few pixels smaller. 1833 01:38:37,160 --> 01:38:39,350 Do we know why we want to do this? 1834 01:38:39,350 --> 01:38:41,450 We're basically shrinking the box. 1835 01:38:41,450 --> 01:38:45,120 Why would we want to shrink the box? 1836 01:38:45,120 --> 01:38:49,405 AUDIENCE: There's a gap between the actual drawing [INAUDIBLE] 1837 01:38:49,405 --> 01:38:50,530 COLTON OGDEN: So not quite. 1838 01:38:50,530 --> 01:38:53,740 So there isn't an actual gap between the drawing. 1839 01:38:53,740 --> 01:39:00,355 It's more a question of how much we want to frustrate our users. 1840 01:39:00,355 --> 01:39:06,070 If we're pixel perfect colliding with the pipes, there's no give and take. 1841 01:39:06,070 --> 01:39:09,010 It's like you collide, and even if-- it might even 1842 01:39:09,010 --> 01:39:11,369 look as if you're not even colliding with the pipe, 1843 01:39:11,369 --> 01:39:12,910 and you're still getting a collision. 1844 01:39:12,910 --> 01:39:14,990 Your users are thinking, well, that's not fair. 1845 01:39:14,990 --> 01:39:16,570 That's really harsh. 1846 01:39:16,570 --> 01:39:19,812 We're shrinking our box so that even if they're just a pixel off, 1847 01:39:19,812 --> 01:39:21,520 they'll still get a little bit of leeway, 1848 01:39:21,520 --> 01:39:25,630 and it'll be a little bit less strict in terms of the collision, 1849 01:39:25,630 --> 01:39:29,890 and this is a very common thing in games when you have characters whose sprites 1850 01:39:29,890 --> 01:39:33,820 may not necessarily fill the entire box that you've allocated for them, 1851 01:39:33,820 --> 01:39:35,530 even though you're doing box collision. 1852 01:39:35,530 --> 01:39:39,250 Just give your users a couple of pixels deep, however many 1853 01:39:39,250 --> 01:39:42,220 you want, and they can overlap with whatever 1854 01:39:42,220 --> 01:39:44,980 they're colliding with just a tiny bit before it actually 1855 01:39:44,980 --> 01:39:48,610 triggers a true on the collision, and it makes your game feel more forgiving, 1856 01:39:48,610 --> 01:39:51,770 and then also more fun as a result of that. 1857 01:39:51,770 --> 01:39:53,200 So that's why we have-- 1858 01:39:53,200 --> 01:39:57,940 instead of testing directly on x0 of that box, we're testing x plus 2, 1859 01:39:57,940 --> 01:40:00,490 and then self.width minus 4 because when we shift, 1860 01:40:00,490 --> 01:40:03,480 we add width to a plus 2 value, we need minus 4 1861 01:40:03,480 --> 01:40:06,370 so that we get 2 off the right edge, and same thing 1862 01:40:06,370 --> 01:40:10,010 goes for the height, and the y value. 1863 01:40:10,010 --> 01:40:13,120 And so this just performs aabb collision detection. 1864 01:40:13,120 --> 01:40:17,230 Expects a pipe, which means that we need to ensure 1865 01:40:17,230 --> 01:40:20,470 that the pipe has an x and a y, a width and a height, which it does. 1866 01:40:20,470 --> 01:40:22,090 Actually, just a constant here. 1867 01:40:22,090 --> 01:40:24,261 We're just checking pipe width and pipe height. 1868 01:40:24,261 --> 01:40:25,510 We probably shouldn't do that. 1869 01:40:25,510 --> 01:40:29,071 It should be pipe.width, pipe.height in that case, 1870 01:40:29,071 --> 01:40:31,320 because then this couldn't necessarily just be a pipe. 1871 01:40:31,320 --> 01:40:34,990 It could be anything in our scene that has a xy, a width, and a height. 1872 01:40:34,990 --> 01:40:37,090 It could be a general purpose collision. 1873 01:40:37,090 --> 01:40:39,895 And actually, something you could also do if you wanted to is 1874 01:40:39,895 --> 01:40:44,260 just write a function called collides that takes in two things 1875 01:40:44,260 --> 01:40:46,354 that you know have bounding boxes, and will 1876 01:40:46,354 --> 01:40:48,520 allow you to perform collision detection on anything 1877 01:40:48,520 --> 01:40:50,890 in your scene between any two entities. 1878 01:40:50,890 --> 01:40:54,022 That would be a more scalable way, I guess, 1879 01:40:54,022 --> 01:40:56,230 of dealing with it, rather than necessarily having it 1880 01:40:56,230 --> 01:40:59,422 specifically defined as birds and pipes being the colliders. 1881 01:40:59,422 --> 01:41:01,630 But in this case, this is the only thing we're really 1882 01:41:01,630 --> 01:41:03,950 colliding with, except for the ground. 1883 01:41:03,950 --> 01:41:06,283 But when you collide with the ground, all you need to do 1884 01:41:06,283 --> 01:41:10,090 is just check to see whether your y position plus your height 1885 01:41:10,090 --> 01:41:12,700 has gone below the edge of the screen. 1886 01:41:12,700 --> 01:41:15,197 So any questions as to how that? 1887 01:41:15,197 --> 01:41:20,560 AUDIENCE: Why did you add 2n and subtract 4 instead of just subtract 2? 1888 01:41:20,560 --> 01:41:23,740 COLTON OGDEN: So the question was why did we add 2 and subtract 1889 01:41:23,740 --> 01:41:26,590 4 instead of just subtract 2? 1890 01:41:26,590 --> 01:41:31,090 Because when you add a-- 1891 01:41:31,090 --> 01:41:34,270 because we're doing self.x plus 2, basically we're 1892 01:41:34,270 --> 01:41:36,930 shifting the whole box, essentially, here in this part. 1893 01:41:36,930 --> 01:41:40,960 So self.x plus 2 brings the beginning of the box 1894 01:41:40,960 --> 01:41:43,930 that we're colliding with 2 pixels to the right. 1895 01:41:43,930 --> 01:41:49,780 But if we just do 2 pixels minus 2, then the box's right edge 1896 01:41:49,780 --> 01:41:51,460 is still the right edge of the box. 1897 01:41:51,460 --> 01:41:55,060 We want it to be shifted inwards by 2 pixels. 1898 01:41:55,060 --> 01:41:59,740 Because we've shifted at the start of our box, the x position, 2 pixels over, 1899 01:41:59,740 --> 01:42:04,790 we need to shift it 4 pixels inwards because that 1900 01:42:04,790 --> 01:42:10,221 will have the effect of our box being 2 pixels into the right edge. 1901 01:42:10,221 --> 01:42:11,694 Does that make sense? 1902 01:42:11,694 --> 01:42:16,120 1903 01:42:16,120 --> 01:42:18,895 OK, so I think that's everything for bird7. 1904 01:42:18,895 --> 01:42:23,960 1905 01:42:23,960 --> 01:42:28,070 We're going to go ahead, and run bird7 now. 1906 01:42:28,070 --> 01:42:33,920 And recall, if we hit a pipe, we should instantly pause. 1907 01:42:33,920 --> 01:42:36,870 So bouncing, bouncing, bouncing. 1908 01:42:36,870 --> 01:42:39,694 I'm going to go through one pair of pipes here, 1909 01:42:39,694 --> 01:42:41,610 and then I'm going to hit this one on purpose. 1910 01:42:41,610 --> 01:42:42,364 Oh, we paused. 1911 01:42:42,364 --> 01:42:44,280 And notice that we had a little bit of leeway. 1912 01:42:44,280 --> 01:42:47,330 We got a couple of pixels there just to give us-- in case we 1913 01:42:47,330 --> 01:42:50,330 accidentally-- and also, it takes into consideration you could move, 1914 01:42:50,330 --> 01:42:52,490 because of your velocity, a couple of pixels 1915 01:42:52,490 --> 01:42:57,530 beyond necessarily the strict hard edge of what you're colliding with 1916 01:42:57,530 --> 01:43:00,900 based on how many frames of passed. 1917 01:43:00,900 --> 01:43:04,177 Basically, essentially what your velocity is, and what your position is. 1918 01:43:04,177 --> 01:43:06,260 In this case, I think it looks like we're actually 1919 01:43:06,260 --> 01:43:08,759 three or four pixels above the edge because our velocity was 1920 01:43:08,759 --> 01:43:13,640 so high because we jumped, but as soon as it detected the collision, as 1921 01:43:13,640 --> 01:43:17,060 soon as we were on that frame where our position was such 1922 01:43:17,060 --> 01:43:19,830 that we did trigger true for our collision detection, 1923 01:43:19,830 --> 01:43:20,750 it paused the game. 1924 01:43:20,750 --> 01:43:22,040 Looping was set to false. 1925 01:43:22,040 --> 01:43:26,060 We no longer ran any update logic, and this is our basic way 1926 01:43:26,060 --> 01:43:27,960 of getting feedback about that. 1927 01:43:27,960 --> 01:43:33,230 However, it's not particularly compelling, gameplay wise, 1928 01:43:33,230 --> 01:43:37,430 and so we want to get into scoring. 1929 01:43:37,430 --> 01:43:41,390 Before we get to scoring though, and also associated with that, 1930 01:43:41,390 --> 01:43:42,690 different states of our game. 1931 01:43:42,690 --> 01:43:44,440 So if we get into scoring, clearly we want 1932 01:43:44,440 --> 01:43:47,489 to have a screen that tells us when we lost, and what our score was. 1933 01:43:47,489 --> 01:43:49,280 We should also probably have a title screen 1934 01:43:49,280 --> 01:43:51,830 because we're just jumping right into the gameplay. 1935 01:43:51,830 --> 01:43:54,710 We want a screen that lets us play through the game, 1936 01:43:54,710 --> 01:43:57,890 and as we'll see in a little bit, a screen that also gives us 1937 01:43:57,890 --> 01:44:01,340 some time, once we start the game, to count down. 1938 01:44:01,340 --> 01:44:04,290 Sort of say, oh, three, two, one, go, rather than just oh, 1939 01:44:04,290 --> 01:44:06,250 go, and oh, I don't know what I'm doing. 1940 01:44:06,250 --> 01:44:06,950 I'm bewildered. 1941 01:44:06,950 --> 01:44:10,220 So this is a diagram that sort of models the state 1942 01:44:10,220 --> 01:44:15,180 flow that we're going to be using in our program here, our game. 1943 01:44:15,180 --> 01:44:18,200 We're going to assume that we start on some sort of title screen state. 1944 01:44:18,200 --> 01:44:19,540 So going left to right. 1945 01:44:19,540 --> 01:44:22,860 A title screen state will transition to the count down state, 1946 01:44:22,860 --> 01:44:26,297 and then we can define, however we want, those transitions to be. 1947 01:44:26,297 --> 01:44:28,130 In this case, let's just say we press Enter. 1948 01:44:28,130 --> 01:44:30,290 The title screen state goes to countdown state. 1949 01:44:30,290 --> 01:44:32,550 Once countdown state has-- 1950 01:44:32,550 --> 01:44:36,872 once the transition has triggered for that, we should go to the play state. 1951 01:44:36,872 --> 01:44:39,080 And then once the transition triggers for play state, 1952 01:44:39,080 --> 01:44:40,970 we're going to go down to the score state. 1953 01:44:40,970 --> 01:44:44,630 And then score state should go back into countdown state, and this models 1954 01:44:44,630 --> 01:44:48,950 our entire application's flow-- 1955 01:44:48,950 --> 01:44:53,090 top to bottom, left to right, chronologically. 1956 01:44:53,090 --> 01:44:55,190 So let's go ahead, and take a look at some code 1957 01:44:55,190 --> 01:44:56,898 as to how we're going to accomplish this. 1958 01:44:56,898 --> 01:45:00,260 Last week, I alluded to taking us-- and actually earlier in lecture, us 1959 01:45:00,260 --> 01:45:04,220 going from this string based approach, to keeping track of our state with if 1960 01:45:04,220 --> 01:45:06,470 conditions, to a class based approach, and that's 1961 01:45:06,470 --> 01:45:08,140 what we're going to illustrate today. 1962 01:45:08,140 --> 01:45:11,080 So I'm going to go ahead, and open up bird8. 1963 01:45:11,080 --> 01:45:15,110 And in bird8, I'm going to go ahead, and start with main. 1964 01:45:15,110 --> 01:45:23,240 So in main, on line 36, we're acquiring a new class called state machine, 1965 01:45:23,240 --> 01:45:26,600 and a few other classes that we're defining called bay state, play 1966 01:45:26,600 --> 01:45:28,920 state, and title screen state. 1967 01:45:28,920 --> 01:45:31,630 And these are the components of our state machine, 1968 01:45:31,630 --> 01:45:35,120 and they've now, instead of being just blocks of code in our update function, 1969 01:45:35,120 --> 01:45:37,670 they're separate blocks, separate modules that 1970 01:45:37,670 --> 01:45:40,310 have their own logic, their own update and render logic, 1971 01:45:40,310 --> 01:45:41,890 and we'll see that very shortly. 1972 01:45:41,890 --> 01:45:45,560 On line 78, if we go down here-- 1973 01:45:45,560 --> 01:45:48,290 separate from that, I'm also instantiating a bunch of fonts. 1974 01:45:48,290 --> 01:45:49,610 We did this last week. 1975 01:45:49,610 --> 01:45:53,164 So love.graphics.newfont takes in a font file and then a size. 1976 01:45:53,164 --> 01:45:56,330 I've created a few different fonts here because we have a few different ways 1977 01:45:56,330 --> 01:45:58,490 of giving feedback to the user. 1978 01:45:58,490 --> 01:46:01,220 We want a small font for displaying press Enter 1979 01:46:01,220 --> 01:46:02,860 to a start, or something like that. 1980 01:46:02,860 --> 01:46:06,214 We want a medium font to display the name of the game, perhaps. 1981 01:46:06,214 --> 01:46:08,630 Or, I think actually, Flappy Font is responsible for that. 1982 01:46:08,630 --> 01:46:10,550 Medium font, I think, was for score. 1983 01:46:10,550 --> 01:46:12,170 Huge font for our countdown. 1984 01:46:12,170 --> 01:46:14,170 We want a big font right in the middle of screen 1985 01:46:14,170 --> 01:46:16,220 that says three, two, one, and then we start. 1986 01:46:16,220 --> 01:46:19,303 And then we're just going to start off by setting it to Flappy Font, which 1987 01:46:19,303 --> 01:46:20,750 is going to be our title font. 1988 01:46:20,750 --> 01:46:25,520 So nothing really new, but the beginning of our UI, so to speak. 1989 01:46:25,520 --> 01:46:29,240 On line 92, this is new, and actually this 1990 01:46:29,240 --> 01:46:32,660 is a demonstration of a type of naming convention 1991 01:46:32,660 --> 01:46:35,240 you'll see often in game code bases. 1992 01:46:35,240 --> 01:46:38,420 We haven't used it yet, but we will start using it in the future. 1993 01:46:38,420 --> 01:46:42,290 We prefix a global variable with a lower case g. 1994 01:46:42,290 --> 01:46:45,260 This lets you know when you're digging through a bunch of files 1995 01:46:45,260 --> 01:46:46,940 that oh, this is a global variable. 1996 01:46:46,940 --> 01:46:51,770 OK, so I should probably know it's probably not defined in this module. 1997 01:46:51,770 --> 01:46:54,892 Maybe it is, but I know it's global. 1998 01:46:54,892 --> 01:46:56,600 Other things you might see are lower case 1999 01:46:56,600 --> 01:47:03,770 m for member, which means that this is a member function, or a field of a class, 2000 01:47:03,770 --> 01:47:06,684 and you can instantly see at a glance, and know 2001 01:47:06,684 --> 01:47:08,600 OK, if I want to find the definition for this, 2002 01:47:08,600 --> 01:47:10,141 it looks like it's a member function. 2003 01:47:10,141 --> 01:47:13,280 So it's probably in this class, here at some line. 2004 01:47:13,280 --> 01:47:15,530 You can easily find it. 2005 01:47:15,530 --> 01:47:17,960 And so in future lectures, we'll be using 2006 01:47:17,960 --> 01:47:23,140 more of this lowercase g for global variables that we use module to module. 2007 01:47:23,140 --> 01:47:25,700 In this case, we're instantiating a StateMachine. 2008 01:47:25,700 --> 01:47:29,710 So we're using the class that we will take a look at in a second. 2009 01:47:29,710 --> 01:47:32,200 The StateMachine takes in a table with keys 2010 01:47:32,200 --> 01:47:36,890 that map to functions that will return our states. 2011 01:47:36,890 --> 01:47:43,910 So we can just call change some value, and it'll have in our state machine, 2012 01:47:43,910 --> 01:47:48,340 it will basically reference that key in this table here, 2013 01:47:48,340 --> 01:47:51,160 and it'll call that function based on-- it'll 2014 01:47:51,160 --> 01:47:57,340 basically set the current state of that StateMachine to whatever state 2015 01:47:57,340 --> 01:48:00,050 gets returned by the function at that key. 2016 01:48:00,050 --> 01:48:04,717 So in this case, change is going to trigger return new TitleScreenState, 2017 01:48:04,717 --> 01:48:06,550 and we're going to get-- the StateMachine is 2018 01:48:06,550 --> 01:48:08,620 going to be set to the title screen, effectively, 2019 01:48:08,620 --> 01:48:12,950 and we'll take a look at what the title screen looks like momentarily. 2020 01:48:12,950 --> 01:48:16,350 On line 96, yeah, we're changing to title screen. 2021 01:48:16,350 --> 01:48:20,290 On line 134, notice that we don't really have much update 2022 01:48:20,290 --> 01:48:22,060 logic in this application anymore. 2023 01:48:22,060 --> 01:48:25,480 We're still updating the scrolls because this is 2024 01:48:25,480 --> 01:48:27,280 behavior we want across all our states. 2025 01:48:27,280 --> 01:48:29,404 No matter what state we're in, we want to make sure 2026 01:48:29,404 --> 01:48:32,830 that our background and our ground scroll so that we have movement. 2027 01:48:32,830 --> 01:48:35,380 We don't need to duplicate this behavior state to state. 2028 01:48:35,380 --> 01:48:37,340 This is a global feature of our game. 2029 01:48:37,340 --> 01:48:40,960 So we're just keeping track of it here just as we would before, 2030 01:48:40,960 --> 01:48:44,110 but anything else in our game that need to be updated 2031 01:48:44,110 --> 01:48:47,620 can now be deferred to our StateMachine class. 2032 01:48:47,620 --> 01:48:51,490 And when we call gStateMachine update delta time, 2033 01:48:51,490 --> 01:48:53,745 it's going to look and see what's our current state, 2034 01:48:53,745 --> 01:48:55,870 and it's going to update that state. 2035 01:48:55,870 --> 01:48:59,270 And that's going to basically be that chunk, that if chunk 2036 01:48:59,270 --> 01:49:02,080 do this logic that we were doing from before last week when 2037 01:49:02,080 --> 01:49:05,360 we had a more primitive StateMachine. 2038 01:49:05,360 --> 01:49:08,140 Line 46, same exact thing. 2039 01:49:08,140 --> 01:49:11,470 Between the background and the ground, because those will always render scene 2040 01:49:11,470 --> 01:49:15,400 to scene, we want to render our current active state 2041 01:49:15,400 --> 01:49:19,120 using our StateMachine render function. 2042 01:49:19,120 --> 01:49:23,410 And so let's go ahead, and just look briefly at our state machine library. 2043 01:49:23,410 --> 01:49:25,420 It's a very simple code. 2044 01:49:25,420 --> 01:49:29,710 It's actually taken from the book I alluded to earlier in the lecture-- 2045 01:49:29,710 --> 01:49:31,000 How to Make an RPG. 2046 01:49:31,000 --> 01:49:34,900 They give you this state machine, which really cleanly, I think, 2047 01:49:34,900 --> 01:49:37,330 handles state transition. 2048 01:49:37,330 --> 01:49:40,840 Basically, it takes an init, and then a series of states. 2049 01:49:40,840 --> 01:49:45,440 It has an empty class, or empty table. 2050 01:49:45,440 --> 01:49:46,750 So all of these are just empty. 2051 01:49:46,750 --> 01:49:50,680 2052 01:49:50,680 --> 01:49:52,840 If there is no-- this is a thing you can do 2053 01:49:52,840 --> 01:49:56,110 in Lua, which just lets you initialize a variable if it's not 2054 01:49:56,110 --> 01:49:57,820 given a value in your function. 2055 01:49:57,820 --> 01:50:01,870 2056 01:50:01,870 --> 01:50:05,090 So self.states gets states or some value. 2057 01:50:05,090 --> 01:50:08,770 Which means that if states is equal to a false value, it's equal to nothing, 2058 01:50:08,770 --> 01:50:10,330 just set it to this empty table. 2059 01:50:10,330 --> 01:50:14,380 So it's just a shorthand for instead of saying if states equals nothing, 2060 01:50:14,380 --> 01:50:16,950 then set states to empty table. 2061 01:50:16,950 --> 01:50:23,680 Self.current is just an empty class or empty state. 2062 01:50:23,680 --> 01:50:26,670 So this is basically what a state is, it's just a set of methods-- 2063 01:50:26,670 --> 01:50:28,780 a render, update, enter, and exit function. 2064 01:50:28,780 --> 01:50:31,960 That's a state, and then you define all of the behavior 2065 01:50:31,960 --> 01:50:36,060 in each of these functions, and that compiles your state more or less. 2066 01:50:36,060 --> 01:50:39,430 Our change function takes in a name, and then also some optional parameters 2067 01:50:39,430 --> 01:50:41,080 that we can use to enter that state. 2068 01:50:41,080 --> 01:50:44,280 2069 01:50:44,280 --> 01:50:48,201 When we change the state, or call the exit function of whatever state 2070 01:50:48,201 --> 01:50:48,700 we're in. 2071 01:50:48,700 --> 01:50:50,260 So exit that state. 2072 01:50:50,260 --> 01:50:53,660 Maybe your function needs you to de-allocate some memory. 2073 01:50:53,660 --> 01:50:56,530 Set the current equal to taking that name, 2074 01:50:56,530 --> 01:50:58,155 and then call whatever functions there. 2075 01:50:58,155 --> 01:50:59,154 So it's going to return. 2076 01:50:59,154 --> 01:51:02,530 In that case, we saw earlier, it's going to return a new title screen state. 2077 01:51:02,530 --> 01:51:05,050 So that's going to be what current is. 2078 01:51:05,050 --> 01:51:09,116 With self.current, we're going to then enter that state machine. 2079 01:51:09,116 --> 01:51:11,740 So we're going to call the Enter function that we defined there 2080 01:51:11,740 --> 01:51:17,180 with whatever enter parameters we pass into change, which are optional. 2081 01:51:17,180 --> 01:51:22,600 And then here, StateMachineUpdate just updates whatever the current state is, 2082 01:51:22,600 --> 01:51:27,140 and render updates whatever the current state is as well. 2083 01:51:27,140 --> 01:51:29,962 And so I'm going to start going a little bit quickly 2084 01:51:29,962 --> 01:51:31,670 just because we're running short on time. 2085 01:51:31,670 --> 01:51:36,010 BaseState, all it does is just implements empty methods 2086 01:51:36,010 --> 01:51:38,809 so that you can just inherit this state, and you 2087 01:51:38,809 --> 01:51:40,600 can choose which methods you want to define 2088 01:51:40,600 --> 01:51:44,046 without throwing any errors because it blindly will call all these functions, 2089 01:51:44,046 --> 01:51:46,420 not checking to see whether they're actually implemented. 2090 01:51:46,420 --> 01:51:50,530 And so this is a way for you to just quickly avoid 2091 01:51:50,530 --> 01:51:52,960 a lot of boilerplate code, essentially. 2092 01:51:52,960 --> 01:52:00,760 The TitleScreenState here, this is your way of with the class library, 2093 01:52:00,760 --> 01:52:04,210 just including everything that belongs to BaseState. 2094 01:52:04,210 --> 01:52:06,700 So inheriting, if you're familiar with other languages 2095 01:52:06,700 --> 01:52:09,730 that use inheritance-- take an object, copy 2096 01:52:09,730 --> 01:52:12,760 everything from that object or that class, put it into this one, 2097 01:52:12,760 --> 01:52:14,170 and then add new stuff to it. 2098 01:52:14,170 --> 01:52:15,711 That's basically what inheritance is. 2099 01:52:15,711 --> 01:52:19,090 We're inheriting from BaseState so it has all the functions BaseState has, 2100 01:52:19,090 --> 01:52:22,800 and then on top of that, we're defining an update function. 2101 01:52:22,800 --> 01:52:27,410 So if we press Enter or Return, change the global state machine 2102 01:52:27,410 --> 01:52:28,670 to the play state. 2103 01:52:28,670 --> 01:52:31,400 And then for the render, we're just going to render fifty bird, 2104 01:52:31,400 --> 01:52:34,740 and press Enter halfway in the middle of the screen. 2105 01:52:34,740 --> 01:52:36,620 And then the PlayState, essentially to some-- 2106 01:52:36,620 --> 01:52:40,850 basically, what the PlayState is is all of the code that we ran before, only 2107 01:52:40,850 --> 01:52:44,330 now we're just putting it in the update function here, 2108 01:52:44,330 --> 01:52:49,760 and the render function here, and making bird, pipe pairs, timer, 2109 01:52:49,760 --> 01:52:53,642 and lastY member fields of this state object. 2110 01:52:53,642 --> 01:52:55,475 So we'll go ahead, and run this really fast. 2111 01:52:55,475 --> 01:53:01,010 2112 01:53:01,010 --> 01:53:02,410 This is our title screen state. 2113 01:53:02,410 --> 01:53:05,661 So at the very beginning, we change to title screen state. 2114 01:53:05,661 --> 01:53:07,910 All it does is render, and then the scrolling behavior 2115 01:53:07,910 --> 01:53:10,136 is throughout all classes, all states. 2116 01:53:10,136 --> 01:53:11,510 So we'll see that no matter what. 2117 01:53:11,510 --> 01:53:13,460 Once you press Enter, it'll trigger change 2118 01:53:13,460 --> 01:53:17,210 to play, which will return a play state, and then 2119 01:53:17,210 --> 01:53:21,820 now we're back where we were before, and we're seeing the difference now 2120 01:53:21,820 --> 01:53:24,140 in having a couple of different states. 2121 01:53:24,140 --> 01:53:27,480 So quickly, I'll go through the score update. 2122 01:53:27,480 --> 01:53:32,510 So this is a little bit more complicated than the last example. 2123 01:53:32,510 --> 01:53:36,230 But to summarize, in bird-- 2124 01:53:36,230 --> 01:53:38,360 sorry, we're in bird9. 2125 01:53:38,360 --> 01:53:45,350 So in bird9, if we go here, we're going to go to main. 2126 01:53:45,350 --> 01:53:50,810 So notice that in main, down where we define our StateMachine, 2127 01:53:50,810 --> 01:53:54,080 we're going to go ahead, and also note that we require a new score 2128 01:53:54,080 --> 01:53:57,810 state because now we want to display a score screen. 2129 01:53:57,810 --> 01:54:05,130 Down on line 96, score gets a function where we return a score state object. 2130 01:54:05,130 --> 01:54:08,960 So now we can change to score, and it will return that state, 2131 01:54:08,960 --> 01:54:12,320 and we can define all the behavior within ScoreState 2132 01:54:12,320 --> 01:54:16,160 that we need to display a score. 2133 01:54:16,160 --> 01:54:24,140 In PipePair, we have a new variable called self.scored. 2134 01:54:24,140 --> 01:54:25,766 Set it to true or false. 2135 01:54:25,766 --> 01:54:27,640 We're going to set it to true if the bird has 2136 01:54:27,640 --> 01:54:31,060 gone past the right edge of the pair of pipes. 2137 01:54:31,060 --> 01:54:34,910 That will have the effect of us scoring a point, effectively, 2138 01:54:34,910 --> 01:54:37,460 because all we need to do is just make sure the birds got 2139 01:54:37,460 --> 01:54:39,305 past that pair of pipes because otherwise it 2140 01:54:39,305 --> 01:54:40,430 will have collided with it. 2141 01:54:40,430 --> 01:54:45,980 If it does go past it, set it to true, and then add a point to our score. 2142 01:54:45,980 --> 01:54:53,130 And in our play state, we can see that we've added a point. 2143 01:54:53,130 --> 01:54:57,380 So if we go to our play state, 26 is where we actually 2144 01:54:57,380 --> 01:54:58,510 keep track of our score. 2145 01:54:58,510 --> 01:55:00,980 Self.score gets 0 in our play state. 2146 01:55:00,980 --> 01:55:03,530 We're going to go ahead, and go down to line 56. 2147 01:55:03,530 --> 01:55:06,637 So for every pair, if it's not been scored yet-- 2148 01:55:06,637 --> 01:55:09,470 because we don't need to calculate this if it's already been scored. 2149 01:55:09,470 --> 01:55:13,460 We should ignore it in terms of scoring once it's been scored. 2150 01:55:13,460 --> 01:55:19,520 If the x plus width is less than our bird.x, 2151 01:55:19,520 --> 01:55:22,820 meaning our bird is beyond the right edge of the pair of pipes, 2152 01:55:22,820 --> 01:55:25,190 increment our score, and set that pair to true. 2153 01:55:25,190 --> 01:55:28,237 We will then thereafter, because of this condition, ignore it, 2154 01:55:28,237 --> 01:55:30,070 and we're also going to increment our score. 2155 01:55:30,070 --> 01:55:33,000 So it's going to be kept track of. 2156 01:55:33,000 --> 01:55:36,350 On 83, notice that if we're colliding with a pipe, 2157 01:55:36,350 --> 01:55:38,920 we should transition to our score state now. 2158 01:55:38,920 --> 01:55:42,380 And we're also passing in score gets self.score as a table 2159 01:55:42,380 --> 01:55:45,350 because remember, we can pass in parameters when we call change, 2160 01:55:45,350 --> 01:55:49,110 and this will be passed into our enter function in our state, 2161 01:55:49,110 --> 01:55:51,260 and then score is going to equal self.score. 2162 01:55:51,260 --> 01:55:54,170 We'll have access to the score within that score state. 2163 01:55:54,170 --> 01:55:56,480 We don't have to keep track of it as a global variable 2164 01:55:56,480 --> 01:55:59,810 to see it in both locations. 2165 01:55:59,810 --> 01:56:02,360 And on 93, the same exact thing. 2166 01:56:02,360 --> 01:56:05,810 This is collision to check whether we've collided with the bottom of the screen. 2167 01:56:05,810 --> 01:56:09,530 If our y is greater than virtual height minus 15, do the exact same thing. 2168 01:56:09,530 --> 01:56:12,440 Transition to the score state, and pass it in our current score. 2169 01:56:12,440 --> 01:56:14,540 So another death condition. 2170 01:56:14,540 --> 01:56:17,390 And then 104, we're just going to set Flappy Font, 2171 01:56:17,390 --> 01:56:21,206 and then we're going to render our score at the top left of the screen at 8,8, 2172 01:56:21,206 --> 01:56:23,280 and that will have that effect. 2173 01:56:23,280 --> 01:56:26,735 And so lastly, here, our scores state is pretty simple. 2174 01:56:26,735 --> 01:56:29,197 2175 01:56:29,197 --> 01:56:32,030 All it is is we're going to get-- from those parameters we passed in 2176 01:56:32,030 --> 01:56:35,540 by a change, self.score equals params.score. 2177 01:56:35,540 --> 01:56:38,270 We're going to, when we press Enter, go back to play, 2178 01:56:38,270 --> 01:56:42,170 and then we're going to render 'you lost', and the score, 2179 01:56:42,170 --> 01:56:45,320 which we have access to-- self.score, and then press Enter to play again, 2180 01:56:45,320 --> 01:56:46,980 changing fonts along the way. 2181 01:56:46,980 --> 01:56:51,650 And so if we go back to bird9, and we run this, 2182 01:56:51,650 --> 01:56:54,380 notice that now we have a score in the top left. 2183 01:56:54,380 --> 01:57:01,220 And I'm going to get one point, and then die. 2184 01:57:01,220 --> 01:57:03,220 Then we go to our score screen now. 2185 01:57:03,220 --> 01:57:07,180 Remember, we passed score into it from our play state. 2186 01:57:07,180 --> 01:57:13,240 We passed it in as parameters, and then we 2187 01:57:13,240 --> 01:57:16,550 can press Enter again, go back to play state, and when we fall to the ground, 2188 01:57:16,550 --> 01:57:17,540 we do it as well. 2189 01:57:17,540 --> 01:57:21,610 So we've just taken a look at how to add scoring to our game, 2190 01:57:21,610 --> 01:57:24,070 but what if we want to add a count down screen? 2191 01:57:24,070 --> 01:57:28,810 Maybe we want the users to be prompted three, two, one before the actual game 2192 01:57:28,810 --> 01:57:30,660 starts throwing pipes at them. 2193 01:57:30,660 --> 01:57:32,752 Give them the time to sort of get acclimated. 2194 01:57:32,752 --> 01:57:34,710 We're going to go ahead, and take a look at how 2195 01:57:34,710 --> 01:57:38,870 we might do this using another state very similar to the last example. 2196 01:57:38,870 --> 01:57:41,350 We're going to add a new state called CountdownState, 2197 01:57:41,350 --> 01:57:44,350 which is shown here on line 38. 2198 01:57:44,350 --> 01:57:47,590 We're also going to, down in our state machine, 2199 01:57:47,590 --> 01:57:51,899 add a new key, which returns one of the new countdown states, just as before, 2200 01:57:51,899 --> 01:57:54,940 and then we're going to go ahead, and take a look at our actual countdown 2201 01:57:54,940 --> 01:57:56,180 state here. 2202 01:57:56,180 --> 01:58:04,510 So in our CountdownState.lua, which is in our states folder, as the others, 2203 01:58:04,510 --> 01:58:06,460 it inherits from BaseState. 2204 01:58:06,460 --> 01:58:09,160 We have initialized a countdown time to 0.75. 2205 01:58:09,160 --> 01:58:10,700 This time in seconds. 2206 01:58:10,700 --> 01:58:14,170 One second is a little long, so I made it 0.75 seconds. 2207 01:58:14,170 --> 01:58:16,810 We're going to initialize a count to three, and a timer to 0. 2208 01:58:16,810 --> 01:58:18,130 The count's going to start. 2209 01:58:18,130 --> 01:58:22,540 It's going to use a timer once the countdown time has elapsed, 2210 01:58:22,540 --> 01:58:24,040 right here, as this logic shows. 2211 01:58:24,040 --> 01:58:27,530 Increase the timer once the timer has gone past countdown time. 2212 01:58:27,530 --> 01:58:29,960 We want to go ahead, and set it to-- 2213 01:58:29,960 --> 01:58:31,630 we're going to modulo by countdown time. 2214 01:58:31,630 --> 01:58:34,930 So loop it back to 0 plus whatever amount beyond the countdown time 2215 01:58:34,930 --> 01:58:38,740 we went so that we have a smooth track of time. 2216 01:58:38,740 --> 01:58:41,650 We're going to set self.count minus itself 2217 01:58:41,650 --> 01:58:44,290 by 1 so that we go three, two, one. 2218 01:58:44,290 --> 01:58:46,632 And then if our count is 0, which means that we've 2219 01:58:46,632 --> 01:58:48,340 gone all the way down in our count, we're 2220 01:58:48,340 --> 01:58:51,640 going to go ahead and use our state machine, and change to the play state. 2221 01:58:51,640 --> 01:58:54,098 And here, we're setting our font to a font that we've set-- 2222 01:58:54,098 --> 01:58:56,770 hugeFont-- and then we're just twostring, 2223 01:58:56,770 --> 01:58:59,320 a little function that takes a string, or takes a number, 2224 01:58:59,320 --> 01:59:00,420 converts to a string. 2225 01:59:00,420 --> 01:59:05,110 We're displaying self.count at 0, 120, and then our-- 2226 01:59:05,110 --> 01:59:06,115 it's printf. 2227 01:59:06,115 --> 01:59:10,814 So we're basically starting at 0, y 120, virtual width alignment, 2228 01:59:10,814 --> 01:59:11,980 and then we're centering it. 2229 01:59:11,980 --> 01:59:15,520 So the one last piece of that that we need to change 2230 01:59:15,520 --> 01:59:21,010 is in our title screen state, instead of going straight to a play state 2231 01:59:21,010 --> 01:59:24,070 here on line 15, we're going to a countdown state. 2232 01:59:24,070 --> 01:59:29,710 And what this has the effect of doing, if we go into bird10, 2233 01:59:29,710 --> 01:59:34,300 is when we press Enter, notice that we're going three, two, one, 2234 01:59:34,300 --> 01:59:35,769 then going into our play state. 2235 01:59:35,769 --> 01:59:37,560 Not just going straight into the play state 2236 01:59:37,560 --> 01:59:40,990 as before, giving our user a little bit of time to catch their breath. 2237 01:59:40,990 --> 01:59:45,370 And then if we die, we go to our score state, but once we press Enter, 2238 01:59:45,370 --> 01:59:48,320 notice we're doing that as well. 2239 01:59:48,320 --> 01:59:53,450 So in our score state, we also are changing to the countdown state. 2240 01:59:53,450 --> 01:59:55,510 So that was how to make a countdown state. 2241 01:59:55,510 --> 01:59:58,720 Probably my favorite part of many of these examples, and of this example 2242 01:59:58,720 --> 02:00:02,680 as well, is adding audio to our application. 2243 02:00:02,680 --> 02:00:05,306 Music and sound effects, which really ties everything together. 2244 02:00:05,306 --> 02:00:07,430 So we're going to go ahead and take a look at this. 2245 02:00:07,430 --> 02:00:08,290 It's very simple. 2246 02:00:08,290 --> 02:00:12,400 Very similar to what we learned last week, even when we just did Pong. 2247 02:00:12,400 --> 02:00:18,490 So in main.lua of bird11, which is what we're going to look at now, 2248 02:00:18,490 --> 02:00:22,390 we're going to take a look at a table of sounds 2249 02:00:22,390 --> 02:00:24,580 that we've initialized on line 88. 2250 02:00:24,580 --> 02:00:26,230 We've given them all keys. 2251 02:00:26,230 --> 02:00:28,160 Jump, explosion, hurt, score. 2252 02:00:28,160 --> 02:00:31,870 These are all sound effects that I've generated with the BFX program 2253 02:00:31,870 --> 02:00:33,610 that we used last week, if you recall. 2254 02:00:33,610 --> 02:00:35,650 And then a music track that I found online 2255 02:00:35,650 --> 02:00:38,440 on FreeSound, which is free to use. 2256 02:00:38,440 --> 02:00:40,345 The link is here, if curious. 2257 02:00:40,345 --> 02:00:46,450 It's just a nice, happy soundtrack that I found for this game. 2258 02:00:46,450 --> 02:00:49,510 On line 99 to 100, we're going to do one additional step 2259 02:00:49,510 --> 02:00:50,890 before we start the music. 2260 02:00:50,890 --> 02:00:52,930 We're going to set looping on that to true 2261 02:00:52,930 --> 02:00:55,930 because in games that are infinite like this, 2262 02:00:55,930 --> 02:00:59,200 we don't want our music to just go, and then stop abruptly. 2263 02:00:59,200 --> 02:01:03,370 We want to have it loop. 2264 02:01:03,370 --> 02:01:05,220 Set looping to true. 2265 02:01:05,220 --> 02:01:08,620 I actually begin the play of that music outside of any of our states 2266 02:01:08,620 --> 02:01:14,060 because it's going to be a global music track, and then that's the music. 2267 02:01:14,060 --> 02:01:15,430 We also need sound effects. 2268 02:01:15,430 --> 02:01:18,340 2269 02:01:18,340 --> 02:01:22,480 If we look in our bird file here line 45, which 2270 02:01:22,480 --> 02:01:26,020 is where we have the logic for jumping, we're 2271 02:01:26,020 --> 02:01:30,400 also playing the jump sound effect that we've generated. 2272 02:01:30,400 --> 02:01:34,080 Additionally, in our play state, if we take a look there, 2273 02:01:34,080 --> 02:01:36,520 we can go ahead and see in our states folder here. 2274 02:01:36,520 --> 02:01:42,040 Go to play state, and take a look at line 58. 2275 02:01:42,040 --> 02:01:43,570 This is where we score a point. 2276 02:01:43,570 --> 02:01:46,630 So we should play our score sound effects here, simply put. 2277 02:01:46,630 --> 02:01:49,900 And then the same thing on line 80 to 81, 2278 02:01:49,900 --> 02:01:52,720 collide, the sound effect here, which is we're actually 2279 02:01:52,720 --> 02:01:55,840 layering two sounds on top of each other, which is a common thing 2280 02:01:55,840 --> 02:01:57,940 to do in sound design, and game design. 2281 02:01:57,940 --> 02:02:01,857 One sound, often, isn't all you need to accomplish a particular effect. 2282 02:02:01,857 --> 02:02:04,690 So I have an explosion sound, which is kind of a white noise effect, 2283 02:02:04,690 --> 02:02:10,190 and then a hurt sound effect, which is sort of like a downward sine wave 2284 02:02:10,190 --> 02:02:10,960 type of sound. 2285 02:02:10,960 --> 02:02:14,140 It would be the exact same here on 95 to 96. 2286 02:02:14,140 --> 02:02:17,560 Once we put all these pieces together, we're going to run bird11. 2287 02:02:17,560 --> 02:02:18,520 [MUSIC PLAYING] 2288 02:02:18,520 --> 02:02:19,230 We get music. 2289 02:02:19,230 --> 02:02:23,038 2290 02:02:23,038 --> 02:02:24,040 [BEEPING] 2291 02:02:24,040 --> 02:02:28,165 We get a jump sound effect. 2292 02:02:28,165 --> 02:02:31,362 And when we score a point, 2293 02:02:31,362 --> 02:02:32,760 [DING] 2294 02:02:32,760 --> 02:02:34,110 we get another sound effect. 2295 02:02:34,110 --> 02:02:35,020 [CRASH] 2296 02:02:35,020 --> 02:02:38,330 And then if we hit a pipe, notice that we have a sort of 2297 02:02:38,330 --> 02:02:39,002 [MIMICS NOISE] 2298 02:02:39,002 --> 02:02:42,790 and a white noise or an explosion effect layered together. 2299 02:02:42,790 --> 02:02:47,710 So that sort of brings everything together, creatively and artistically. 2300 02:02:47,710 --> 02:02:50,970 As an exercise to the viewer, in bird12-- 2301 02:02:50,970 --> 02:02:52,900 in the GitHub repo, we have some code that 2302 02:02:52,900 --> 02:02:55,652 allows you to actually add mouse clicks to the Flappy Bird 2303 02:02:55,652 --> 02:02:58,360 in order to make it a little bit more like the actual game, which 2304 02:02:58,360 --> 02:02:59,380 was an iOS game. 2305 02:02:59,380 --> 02:03:01,720 So it relied on taps. 2306 02:03:01,720 --> 02:03:06,040 The function that you might want to use is love.mousepressed x, y, button, 2307 02:03:06,040 --> 02:03:09,010 and I would encourage you to think about how we took input, and made 2308 02:03:09,010 --> 02:03:12,970 it global in the context of a keyboard in one of our earlier examples 2309 02:03:12,970 --> 02:03:20,140 so that we can call this was the mouse just pressed in our bird.lua file, 2310 02:03:20,140 --> 02:03:22,150 as opposed to the main file. 2311 02:03:22,150 --> 02:03:24,920 And so next time, we're going to be covering a few new concepts. 2312 02:03:24,920 --> 02:03:26,140 Or sprite sheets. 2313 02:03:26,140 --> 02:03:30,910 So taking a large file of images, and taking out chunks of that 2314 02:03:30,910 --> 02:03:33,250 so we don't have to have a million graphic files. 2315 02:03:33,250 --> 02:03:34,379 Procedural layouts. 2316 02:03:34,379 --> 02:03:36,420 This will be in the context of the game Breakout. 2317 02:03:36,420 --> 02:03:40,082 So we want to lay out all the bricks in our game, procedurally, 2318 02:03:40,082 --> 02:03:42,040 in sort of the same way that we've procedurally 2319 02:03:42,040 --> 02:03:45,220 created a pipe level in this game. 2320 02:03:45,220 --> 02:03:47,530 We'll be talking about separate levels, and having 2321 02:03:47,530 --> 02:03:50,950 them stored in memory as opposed to just one continuous level. 2322 02:03:50,950 --> 02:03:52,384 We'll be talking about health. 2323 02:03:52,384 --> 02:03:55,300 We'll be talking about particle systems, which is spawning little mini 2324 02:03:55,300 --> 02:03:59,170 graphics to accomplish various effects that are otherwise difficult to capture 2325 02:03:59,170 --> 02:04:01,780 in simple sprite animation. 2326 02:04:01,780 --> 02:04:04,570 A little bit fancier collision detection based on input 2327 02:04:04,570 --> 02:04:07,792 so that we can drive ball behavior the way we want to, 2328 02:04:07,792 --> 02:04:09,250 and then also persistent save data. 2329 02:04:09,250 --> 02:04:11,440 How can we take a high score, and not have 2330 02:04:11,440 --> 02:04:15,670 it refresh to 0 every time we run the application, but rather save it to disk 2331 02:04:15,670 --> 02:04:18,310 so that every time you run the program thereafter, we can see 2332 02:04:18,310 --> 02:04:21,400 what we've gotten scored in days past. 2333 02:04:21,400 --> 02:04:24,910 The first assignment, or other the second assignment, assignment one, 2334 02:04:24,910 --> 02:04:27,520 is going to be a little bit more complicated than last weeks, 2335 02:04:27,520 --> 02:04:28,970 but still fairly doable. 2336 02:04:28,970 --> 02:04:31,940 Make pipe gaps slightly random, being the first component of this. 2337 02:04:31,940 --> 02:04:34,240 So before, a pipe gap was set to a constant value. 2338 02:04:34,240 --> 02:04:36,190 Maybe make it some sort of random value. 2339 02:04:36,190 --> 02:04:37,360 Pipe intervals as well. 2340 02:04:37,360 --> 02:04:38,860 So we're spawning every two seconds. 2341 02:04:38,860 --> 02:04:40,651 Maybe we want to change that up, make pipes 2342 02:04:40,651 --> 02:04:44,020 spawn a little differently, a little more sporadically. 2343 02:04:44,020 --> 02:04:46,330 The more complicated aspect of this assignment 2344 02:04:46,330 --> 02:04:49,150 is going to be awarding players a medal based on their performance. 2345 02:04:49,150 --> 02:04:52,060 So have maybe a bronze, a silver, and a gold medal-- 2346 02:04:52,060 --> 02:04:55,930 an image that you display in the score screen in addition to just their score 2347 02:04:55,930 --> 02:04:58,602 just to give them a little bit of personal feedback, 2348 02:04:58,602 --> 02:05:01,060 and make them feel rewarded for their effort, and make them 2349 02:05:01,060 --> 02:05:03,640 strive to get that last medal. 2350 02:05:03,640 --> 02:05:06,640 And then lastly, you'll implement a pause feature, which we talked about 2351 02:05:06,640 --> 02:05:11,170 in class, so that when you press, for example, the key p, the game will stop. 2352 02:05:11,170 --> 02:05:13,720 But unlike that example, when we press p again, 2353 02:05:13,720 --> 02:05:16,990 the game should resume just as it was in its prior state. 2354 02:05:16,990 --> 02:05:19,154 So that will be it for Flappy Bird. 2355 02:05:19,154 --> 02:05:20,320 I'll see you guys next time. 2356 02:05:20,320 --> 02:05:22,170 Thanks a lot. 2357 02:05:22,170 --> 02:05:23,914