1 00:00:00,000 --> 00:00:00,660 COLTON OGDEN: Hey, guys. 2 00:00:00,660 --> 00:00:01,840 Thank you very much 8 coming. 3 00:00:01,840 --> 00:00:02,850 My name is Colton Ogden. 4 00:00:02,850 --> 00:00:05,160 I work full time with CS50. 5 00:00:05,160 --> 00:00:08,550 And I'm going to talk to you guys about a framework and language that I 6 00:00:08,550 --> 00:00:11,130 recently started using in hobbyist's game development 7 00:00:11,130 --> 00:00:13,075 that I really started to like. 8 00:00:13,075 --> 00:00:16,200 If I want you to follow along, I have the set of slides I'll be using today 9 00:00:16,200 --> 00:00:20,890 which have a lot of important links at cs50.ly/lua. 10 00:00:20,890 --> 00:00:24,030 Two of these links here are getting LOVE 2D itself which 11 00:00:24,030 --> 00:00:26,430 is the game framework we'll be using. 12 00:00:26,430 --> 00:00:28,830 It has a download link and then a wiki page for getting 13 00:00:28,830 --> 00:00:31,950 started which has a few tutorial pages and stuff just to provide you 14 00:00:31,950 --> 00:00:34,030 that on-ramp David was talking about. 15 00:00:34,030 --> 00:00:36,022 And then I also set up a GitHub repo. 16 00:00:36,022 --> 00:00:37,230 So you guys can follow along. 17 00:00:37,230 --> 00:00:39,092 I'll be running a bunch of demos today. 18 00:00:39,092 --> 00:00:40,800 And the nice thing I like about this repo 19 00:00:40,800 --> 00:00:43,600 is I set up several exercise folders as well. 20 00:00:43,600 --> 00:00:45,720 So if you guys want to try and implement a lot of the things we'll be talking 21 00:00:45,720 --> 00:00:47,344 about today, you can do so on your own. 22 00:00:47,344 --> 00:00:50,810 And I've written out like to-do's with hints and stuff to get you started. 23 00:00:50,810 --> 00:00:53,460 So let's talk about first what Lua is. 24 00:00:53,460 --> 00:00:56,250 So Lua is the language we'll be using today. 25 00:00:56,250 --> 00:00:59,160 It's a early 90s language invented in Brazil. 26 00:00:59,160 --> 00:01:01,230 It means moon in Portuguese. 27 00:01:01,230 --> 00:01:03,990 And it's a very light, small language that's 28 00:01:03,990 --> 00:01:06,420 focused around this data structure called a table. 29 00:01:06,420 --> 00:01:09,780 Which if you're familiar with Python, it's very similar to a dictionary. 30 00:01:09,780 --> 00:01:13,110 If you're familiar with JavaScript, it's very similar to a JavaScript object. 31 00:01:13,110 --> 00:01:16,260 And that's basically most of what you do in Lua revolves around the use 32 00:01:16,260 --> 00:01:18,900 of this table data structure. 33 00:01:18,900 --> 00:01:22,740 Lua was invented in the 90s mostly to provide a glue 34 00:01:22,740 --> 00:01:24,570 layer for larger applications. 35 00:01:24,570 --> 00:01:31,440 So back in the 90s and even in the 80s, game code bases were essentially 36 00:01:31,440 --> 00:01:34,950 written in compiled languages like C or C++, 37 00:01:34,950 --> 00:01:38,340 which could be very monolithic and compile times for such could take 38 00:01:38,340 --> 00:01:40,260 upwards of several hours. 39 00:01:40,260 --> 00:01:44,700 Rather than rely on editing the C or C++ code, 40 00:01:44,700 --> 00:01:49,140 designers at the time would much rather write in a dynamic programming language 41 00:01:49,140 --> 00:01:53,610 like Lua where you can have the game code exposed certain methods like 42 00:01:53,610 --> 00:01:57,630 drawing to the screen et cetera that Lua then called dynamically and not have 43 00:01:57,630 --> 00:01:59,970 to recompile your application. 44 00:01:59,970 --> 00:02:02,400 And Lua was very popular in the video game industry 45 00:02:02,400 --> 00:02:06,000 because it could provide this glue layer for large code bases. 46 00:02:06,000 --> 00:02:08,639 And if you use JavaScript at all, it's very similar to that. 47 00:02:08,639 --> 00:02:10,800 You'll see some similarities. 48 00:02:10,800 --> 00:02:14,415 And the nice thing about Lua is its main purpose-- 49 00:02:14,415 --> 00:02:17,760 well, one of its main purposes was also used as a config language. 50 00:02:17,760 --> 00:02:20,080 So it's excellent at storing data as well as code. 51 00:02:20,080 --> 00:02:22,750 And the two can sort of be paired together. 52 00:02:22,750 --> 00:02:25,470 So let's go ahead and look at a syntax of Lua that I've set here. 53 00:02:25,470 --> 00:02:27,840 If you're in the GitHub repo, it's at the parent level. 54 00:02:27,840 --> 00:02:31,940 It's called lua_demo.lua. demo Lua I'll just be going over some of the syntax 55 00:02:31,940 --> 00:02:32,440 here. 56 00:02:32,440 --> 00:02:35,020 So to create a variable, it's very simple. 57 00:02:35,020 --> 00:02:38,476 It's just some variable name, gets, and then some data type. 58 00:02:38,476 --> 00:02:40,350 Variables are dynamically typed, so you don't 59 00:02:40,350 --> 00:02:43,058 have to provide-- you don't have to say int or string or whatnot. 60 00:02:43,058 --> 00:02:45,182 You can just assign it whatever you want. 61 00:02:45,182 --> 00:02:46,890 There's two different types of variables. 62 00:02:46,890 --> 00:02:49,530 Global variables have no specifier in front of them. 63 00:02:49,530 --> 00:02:52,100 As opposed to local variables-- like here a local 64 00:02:52,100 --> 00:02:54,850 some variable-- and that just means that it's local to that scope. 65 00:02:54,850 --> 00:02:57,330 So if you're in the middle of like a for loop or a while loop, 66 00:02:57,330 --> 00:02:59,913 that variable is only going to be accessible within that loop. 67 00:02:59,913 --> 00:03:02,700 And if you don't specify local, you can use that variable anywhere 68 00:03:02,700 --> 00:03:04,990 in your application. 69 00:03:04,990 --> 00:03:06,810 This is how you define a function in Lua. 70 00:03:06,810 --> 00:03:10,050 So like in JavaScript, you say function and then 71 00:03:10,050 --> 00:03:13,129 some name with its arguments, its body, and then it 72 00:03:13,129 --> 00:03:15,420 ends with an end keyword, which is different than a lot 73 00:03:15,420 --> 00:03:16,740 of other languages. 74 00:03:16,740 --> 00:03:20,467 If you're familiar with Bash, it's sort of similar in that spirit. 75 00:03:20,467 --> 00:03:22,050 This is how you would call a function. 76 00:03:22,050 --> 00:03:24,120 Just the name of the function, and then whatever arguments 77 00:03:24,120 --> 00:03:25,078 you want to pass to it. 78 00:03:25,078 --> 00:03:27,090 In this case, hello and world are two variables. 79 00:03:27,090 --> 00:03:29,460 And we're concatenating them with this dot dot operator. 80 00:03:29,460 --> 00:03:34,210 That just means take two strings and put them together. 81 00:03:34,210 --> 00:03:36,090 And then we have if statements which take 82 00:03:36,090 --> 00:03:40,680 the form of if some condition then, which is a Lua 83 00:03:40,680 --> 00:03:43,230 keyword, and then execute this body. 84 00:03:43,230 --> 00:03:48,960 Else execute this body and then end like we did up above with the function. 85 00:03:48,960 --> 00:03:53,040 Loops are similar to other languages with the while form. 86 00:03:53,040 --> 00:03:56,680 While some condition, do this body. 87 00:03:56,680 --> 00:03:59,619 So in this case, we're setting i to 10. 88 00:03:59,619 --> 00:04:01,410 While it's greater than zero, decrement it. 89 00:04:01,410 --> 00:04:04,890 I get's i minus 1, print it and then end. 90 00:04:04,890 --> 00:04:07,110 And then a for loop is similar to other languages 91 00:04:07,110 --> 00:04:13,890 as well like Python and C. For j gets 10 until it equals 1, subtract 1, 92 00:04:13,890 --> 00:04:16,730 and then and at the end [INAUDIBLE]. 93 00:04:16,730 --> 00:04:19,000 And this negative 1 is actually optional. 94 00:04:19,000 --> 00:04:21,390 If you don't specify it by default it goes up by 1. 95 00:04:21,390 --> 00:04:23,340 But since in this case we're doing a 10 to 1, 96 00:04:23,340 --> 00:04:26,160 we need to specify that it's a negative step of 1. 97 00:04:26,160 --> 00:04:27,670 And then a repeat until-- 98 00:04:27,670 --> 00:04:30,030 less common, but this is the Lua equivalent 99 00:04:30,030 --> 00:04:32,790 of a do while loop like you would see in C. 100 00:04:32,790 --> 00:04:35,981 And then here, lastly, we'll have a demonstration of what a table is 101 00:04:35,981 --> 00:04:36,730 and how to use it. 102 00:04:36,730 --> 00:04:41,050 It's very simple if you especially if you use JavaScript or Python. 103 00:04:41,050 --> 00:04:44,580 An empty table takes the form of two curly brackets. 104 00:04:44,580 --> 00:04:47,190 To access fields in that table, you simply 105 00:04:47,190 --> 00:04:51,200 say that table's name dot sum field get some value. 106 00:04:51,200 --> 00:04:54,450 So in this case, person.name equals my name, age, and height. 107 00:04:54,450 --> 00:04:56,830 And then there's two ways of accessing the fields. 108 00:04:56,830 --> 00:04:59,890 Once you've specified them, you can say that table 109 00:04:59,890 --> 00:05:03,220 with square brackets and then a string like you would do in Python. 110 00:05:03,220 --> 00:05:06,070 Or you can access it with a dot like you would do in JavaScript. 111 00:05:06,070 --> 00:05:08,380 So it's kind of like a combination between the two. 112 00:05:08,380 --> 00:05:12,460 And then something that's specific to Lua but similar to Python 113 00:05:12,460 --> 00:05:16,570 is in order to iterate through a table to get all the keys and values, 114 00:05:16,570 --> 00:05:20,860 you would do for key, value in a function called pairs-- 115 00:05:20,860 --> 00:05:23,977 which gives you all the pairs of key values in that table. 116 00:05:23,977 --> 00:05:26,060 And then you have access to those within the loop, 117 00:05:26,060 --> 00:05:27,893 and you can do whatever you want with those. 118 00:05:27,893 --> 00:05:32,030 So any questions on Lua syntax thus far? 119 00:05:32,030 --> 00:05:34,010 OK, cool. 120 00:05:34,010 --> 00:05:35,115 So that's Lua. 121 00:05:35,115 --> 00:05:36,740 It's a very, very lightweight language. 122 00:05:36,740 --> 00:05:38,948 I think it's like something like 60 kilobyte runtime. 123 00:05:38,948 --> 00:05:40,910 It's very small, very fast. 124 00:05:40,910 --> 00:05:45,480 LOVE 2D is what we're going to actually be using to do our game development. 125 00:05:45,480 --> 00:05:48,920 So this exposes all of the functions that we need to draw things 126 00:05:48,920 --> 00:05:51,170 to the screen, to take keyboard input. 127 00:05:51,170 --> 00:05:55,400 It's got some math functions and audio, some physics stuff. 128 00:05:55,400 --> 00:05:57,367 It's free, it's very lightweight, and it's 129 00:05:57,367 --> 00:05:59,450 portable to all major desktops and mobile devices. 130 00:05:59,450 --> 00:06:02,180 So you can write once and then run it on any computer that 131 00:06:02,180 --> 00:06:04,490 supports this runtime. 132 00:06:04,490 --> 00:06:07,570 And the main thing that I really think is great for LOVE 2D 133 00:06:07,570 --> 00:06:10,430 is because it's so light and easy to rapidly develop in, 134 00:06:10,430 --> 00:06:12,679 it's great for prototyping. 135 00:06:12,679 --> 00:06:14,720 Whether or not you want to ship your game in LOVE 136 00:06:14,720 --> 00:06:16,636 or like port it to unity or something, I think 137 00:06:16,636 --> 00:06:19,790 it's really great and really fast and easy to write it in LOVE. 138 00:06:19,790 --> 00:06:23,130 So before we get into what we're going to be doing today 139 00:06:23,130 --> 00:06:25,130 in terms of the actual implementation, I thought 140 00:06:25,130 --> 00:06:27,500 I'd talk about some game concepts at large 141 00:06:27,500 --> 00:06:29,944 so we can understand how a game really works. 142 00:06:29,944 --> 00:06:32,360 A game is simply an application that's constantly running. 143 00:06:32,360 --> 00:06:33,980 It's in a while loop effectively. 144 00:06:33,980 --> 00:06:36,410 And it's running something that we call a game loop, which 145 00:06:36,410 --> 00:06:40,470 is a simplified three step process. 146 00:06:40,470 --> 00:06:42,710 The first step is process input. 147 00:06:42,710 --> 00:06:46,470 So while true, we need to say as the user pressed a key on the keyboard 148 00:06:46,470 --> 00:06:48,470 or moved their mouse or done anything like that, 149 00:06:48,470 --> 00:06:50,330 if so we need to keep track of that because that's 150 00:06:50,330 --> 00:06:53,038 going to have an effect on how we interact with the game like how 151 00:06:53,038 --> 00:06:54,050 it's going to update. 152 00:06:54,050 --> 00:06:56,360 The next stage of the game is the update phase 153 00:06:56,360 --> 00:06:59,030 and this takes into consideration everything in our game-- 154 00:06:59,030 --> 00:07:01,607 all the characters and items and the AI. 155 00:07:01,607 --> 00:07:04,190 Whatever you've implemented needs to update during this phase. 156 00:07:04,190 --> 00:07:05,750 It needs to get to its next stage. 157 00:07:05,750 --> 00:07:07,806 And sometimes this clock, it represents the fact 158 00:07:07,806 --> 00:07:09,680 that this can happen multiple times depending 159 00:07:09,680 --> 00:07:13,100 on how many frames have passed since the last rendered frame 160 00:07:13,100 --> 00:07:15,290 depending on how fast your computer is. 161 00:07:15,290 --> 00:07:18,629 And then the render phase takes all these updates into consideration 162 00:07:18,629 --> 00:07:20,420 and actually draws everything to the screen 163 00:07:20,420 --> 00:07:25,370 in whatever order and whatever style that you've determined it needs to. 164 00:07:25,370 --> 00:07:27,980 Specifically with 2D games, one other thing 165 00:07:27,980 --> 00:07:30,320 we need to take into consideration is the 2D coordinate 166 00:07:30,320 --> 00:07:32,600 system which is very simple. 167 00:07:32,600 --> 00:07:36,030 As we learned in geometry many years ago, x-axis-- 168 00:07:36,030 --> 00:07:38,150 to the right, positive; to the left, negative. 169 00:07:38,150 --> 00:07:41,390 Y-axis is inverted from Cartesian geometry. 170 00:07:41,390 --> 00:07:44,100 I believe it is positive up, negative down. 171 00:07:44,100 --> 00:07:47,180 But in this case, it's going to be positive down, negative up. 172 00:07:47,180 --> 00:07:50,200 It can vary depending on which platform you're using. 173 00:07:50,200 --> 00:07:51,870 I think unity starts bottom left. 174 00:07:51,870 --> 00:07:54,870 But in LOVE 2D, it starts at the top left. 175 00:07:54,870 --> 00:07:56,270 So (0, 0)'s origin-- 176 00:07:56,270 --> 00:07:56,806 top left. 177 00:07:56,806 --> 00:07:59,680 Anything that's positive on the x-axis is going to move to the right. 178 00:07:59,680 --> 00:08:03,150 If it's positive on the y-axis is going to move down and vise versa. 179 00:08:03,150 --> 00:08:05,300 And so this is how we draw things to the screen. 180 00:08:05,300 --> 00:08:08,990 Everything in our game needs to have an x,y coordinates so that it can be drawn 181 00:08:08,990 --> 00:08:11,750 appropriately, and we'll see this very soon. 182 00:08:11,750 --> 00:08:16,210 So I going to open up a very basic LOVE 2D demo. 183 00:08:16,210 --> 00:08:23,920 If we go to LOVE demo in the GitHub repo and we go to main.lua in that folder. 184 00:08:23,920 --> 00:08:30,320 Main.lua is a file that LOVE 2D needs to see in order to run the application. 185 00:08:30,320 --> 00:08:33,590 Right here, I'm going to sort of wave my hands at some of this stuff, 186 00:08:33,590 --> 00:08:36,590 but I will go into detail on some of the things that are important. 187 00:08:36,590 --> 00:08:39,760 In this case, we're using a virtual resolution library. 188 00:08:39,760 --> 00:08:42,010 So we don't need to worry too much about what this is. 189 00:08:42,010 --> 00:08:44,570 But essentially this will allow us to pretend 190 00:08:44,570 --> 00:08:50,760 like we're drawing to NES era screen while rendering our game in 1080p 191 00:08:50,760 --> 00:08:53,850 or 720p or whatever resolution we want. 192 00:08:53,850 --> 00:08:56,209 So here I have three variables-- sprite, x, and y. 193 00:08:56,209 --> 00:08:59,000 The sprite is going to be the actual graphic we draw to the screen. 194 00:08:59,000 --> 00:09:01,127 X and y are going to be its position. 195 00:09:01,127 --> 00:09:02,960 We have our virtual width and virtual height 196 00:09:02,960 --> 00:09:04,709 that we want to draw the screen at to make 197 00:09:04,709 --> 00:09:07,400 it look like it's an old game like a retro style NES game-- 198 00:09:07,400 --> 00:09:12,810 423 by 243 happens to be similar, but 16 by 9 resolution of that era's 199 00:09:12,810 --> 00:09:14,420 resolution. 200 00:09:14,420 --> 00:09:18,530 And then speed is a variable we're going to use to move our sprite once it's 201 00:09:18,530 --> 00:09:19,920 drawn to the screen. 202 00:09:19,920 --> 00:09:23,550 So the first core LOVE 2D function that we're going to look at is love.load. 203 00:09:23,550 --> 00:09:26,540 This is an initialization function that takes place 204 00:09:26,540 --> 00:09:29,730 at the very beginning of your application. 205 00:09:29,730 --> 00:09:31,730 This is where you set up all the things that you 206 00:09:31,730 --> 00:09:34,522 need to get going before the game loop actually begins. 207 00:09:34,522 --> 00:09:36,980 So in this case, we're setting a filter for nearest-nearest 208 00:09:36,980 --> 00:09:39,350 which just means whenever we draw texture don't blur it. 209 00:09:39,350 --> 00:09:42,936 Just make sure it's drawn as it looks on the image. 210 00:09:42,936 --> 00:09:47,210 We're initialising sprite to a new image-- love.graphics.newimage which 211 00:09:47,210 --> 00:09:51,590 is a LOVE 2D function that will look into that directory was specified 212 00:09:51,590 --> 00:09:54,920 for that image and it's going to set it to that image. 213 00:09:54,920 --> 00:09:58,400 We're setting x and y to roughly halfway in the middle of the screen taking 214 00:09:58,400 --> 00:10:00,574 into consideration the sprite's dimensions. 215 00:10:00,574 --> 00:10:02,990 And then we're setting up our screen with the push library 216 00:10:02,990 --> 00:10:06,667 with a virtual width and virtual height but the 640 by 480 217 00:10:06,667 --> 00:10:09,750 is the actual window width and height that we're going to be rendering to. 218 00:10:09,750 --> 00:10:13,790 So even though it's any less resolution, it's going to be a 640 by 480 window. 219 00:10:13,790 --> 00:10:16,230 So that information isn't necessarily important. 220 00:10:16,230 --> 00:10:17,390 The important thing is we know that we're 221 00:10:17,390 --> 00:10:19,140 getting a sprite, assigning it an x and y, 222 00:10:19,140 --> 00:10:21,270 and then we're drawing it to the screen. 223 00:10:21,270 --> 00:10:24,650 So the next important LOVE 2D function that we'll be looking at 224 00:10:24,650 --> 00:10:26,720 is the update function-- love.update. 225 00:10:26,720 --> 00:10:30,320 And it takes in a parameter called dt, this is short for delta time. 226 00:10:30,320 --> 00:10:33,290 And this is the number in seconds as a floating point that 227 00:10:33,290 --> 00:10:35,660 have elapsed since the last frame. 228 00:10:35,660 --> 00:10:39,530 One frame generally in 60 frames per second is about 17 milliseconds. 229 00:10:39,530 --> 00:10:42,530 So that's roughly how much time will have passed since the last frame. 230 00:10:42,530 --> 00:10:45,050 And any time you need to update anything in your game, 231 00:10:45,050 --> 00:10:46,910 you'll do that in this function, but you'll 232 00:10:46,910 --> 00:10:49,400 multiply whatever you need by delta time so 233 00:10:49,400 --> 00:10:55,370 that this occurs independent of whether or not your computer has slowed down. 234 00:10:55,370 --> 00:10:59,030 And then love.keypressed is the input phase. 235 00:10:59,030 --> 00:11:03,530 So that was effectively the update phase of the game loop we saw earlier. 236 00:11:03,530 --> 00:11:06,380 The input phase for the sake of this demonstration, this program 237 00:11:06,380 --> 00:11:08,450 is love.keypressed key. 238 00:11:08,450 --> 00:11:11,060 So this function basically looks at whatever key 239 00:11:11,060 --> 00:11:13,567 you've pressed on the keyboard, it gets that passed in. 240 00:11:13,567 --> 00:11:15,900 And then you can do any conditional logic you want here. 241 00:11:15,900 --> 00:11:18,220 So if he happens to be left for example, we're 242 00:11:18,220 --> 00:11:19,804 going to set x equal to x minus speed. 243 00:11:19,804 --> 00:11:21,261 We're going to subtract 10 from it. 244 00:11:21,261 --> 00:11:22,890 We're going to move 10 to the left. 245 00:11:22,890 --> 00:11:25,170 If key is right move to the right 10. 246 00:11:25,170 --> 00:11:27,314 And same thing with up and down, but on the y-axis. 247 00:11:27,314 --> 00:11:29,480 So we're going to move that sprite on the right axis 248 00:11:29,480 --> 00:11:31,490 based on what key we've selected. 249 00:11:31,490 --> 00:11:33,410 And if the key is escape, then we can call 250 00:11:33,410 --> 00:11:35,810 this function called love.event.quit which 251 00:11:35,810 --> 00:11:37,850 will actually quit the application. 252 00:11:37,850 --> 00:11:39,174 And then here at the bottom-- 253 00:11:39,174 --> 00:11:41,840 push apply start and we don't need to worry too much about that. 254 00:11:41,840 --> 00:11:44,900 That will just start and end our virtual resolution. 255 00:11:44,900 --> 00:11:47,990 The important thing to look at is this love.graphics.draw function. 256 00:11:47,990 --> 00:11:51,230 Which takes in a drawable object, in this case sprite, 257 00:11:51,230 --> 00:11:52,250 and then an x and a y. 258 00:11:52,250 --> 00:11:56,690 And it'll always draw the sprite at the coordinates that you pass in. 259 00:11:56,690 --> 00:11:59,198 And if we run this program-- 260 00:11:59,198 --> 00:12:01,000 let's see, what am I going to? 261 00:12:01,000 --> 00:12:04,070 LOVE demo. 262 00:12:04,070 --> 00:12:05,180 We get this on the screen. 263 00:12:05,180 --> 00:12:09,800 So it's loading the Mario graphic that's in graphics/mario.png and if I hit left 264 00:12:09,800 --> 00:12:13,910 and right, up and down on the keyboard, it moves 10 pixels in that direction. 265 00:12:13,910 --> 00:12:16,010 So very basic but most of the building blocks 266 00:12:16,010 --> 00:12:20,210 that we need for like the beginning of our game are effectively in place. 267 00:12:20,210 --> 00:12:23,402 So does anybody have any questions on how that works? 268 00:12:23,402 --> 00:12:24,326 AUDIENCE: I do. 269 00:12:24,326 --> 00:12:25,896 What is speed measured in? 270 00:12:25,896 --> 00:12:27,770 COLTON OGDEN: In this case, it's just pixels. 271 00:12:27,770 --> 00:12:35,690 So we're saying speed equals 10 and then when we only modify the sprite's (x, y) 272 00:12:35,690 --> 00:12:36,420 value here. 273 00:12:36,420 --> 00:12:39,390 It's literally just subtracting and adding pixels to it. 274 00:12:39,390 --> 00:12:42,792 AUDIENCE: In this case, if you just hold down the button, 275 00:12:42,792 --> 00:12:44,000 would it register each frame? 276 00:12:44,000 --> 00:12:45,083 COLTON OGDEN: It does not. 277 00:12:45,083 --> 00:12:47,200 So in this particular function, this is just 278 00:12:47,200 --> 00:12:49,700 a callback function that registers one time when you hit it. 279 00:12:49,700 --> 00:12:51,533 And we'll actually see another function that 280 00:12:51,533 --> 00:12:54,820 love offers that allows you to test for continuous input. 281 00:12:54,820 --> 00:12:56,628 Any other questions? 282 00:12:56,628 --> 00:13:01,080 AUDIENCE: How can we use a switch statement? 283 00:13:01,080 --> 00:13:03,845 COLTON OGDEN: I don't believe Lua actually has a switch statement. 284 00:13:03,845 --> 00:13:06,720 There are other clean ways we could probably do it with like a table. 285 00:13:06,720 --> 00:13:09,140 We could set a table to and just reference it like that. 286 00:13:09,140 --> 00:13:11,331 But also for simplicity, it's really easy 287 00:13:11,331 --> 00:13:13,830 to see what's going on here just for an early demonstration. 288 00:13:13,830 --> 00:13:15,860 So any other question? 289 00:13:15,860 --> 00:13:19,380 290 00:13:19,380 --> 00:13:19,880 All right. 291 00:13:19,880 --> 00:13:22,620 So our goal today is going to be a little more complicated. 292 00:13:22,620 --> 00:13:26,799 We're going to be implementing the very basics of what makes Super Mario Bros. 293 00:13:26,799 --> 00:13:29,340 We don't have enough time to really implement the whole game. 294 00:13:29,340 --> 00:13:32,464 But we will be doing a lot of the core will be having him jump and animate. 295 00:13:32,464 --> 00:13:35,150 We'll have a world rendering blocks. 296 00:13:35,150 --> 00:13:38,120 We won't have enough time for AI and creatures. 297 00:13:38,120 --> 00:13:41,000 But once we see how Mario works, it's sort of 298 00:13:41,000 --> 00:13:45,329 easy to guess how we might implement that. 299 00:13:45,329 --> 00:13:47,120 So today's scope, just to reiterate-- we'll 300 00:13:47,120 --> 00:13:50,120 have a tile-based map to hearken back to the NES era 301 00:13:50,120 --> 00:13:52,750 where everything was drawn with tiles. 302 00:13:52,750 --> 00:13:54,167 We'll control Mario with keyboard. 303 00:13:54,167 --> 00:13:55,250 He'll run around and jump. 304 00:13:55,250 --> 00:13:56,930 He'll animate appropriately. 305 00:13:56,930 --> 00:13:59,660 He'll have collision detection so he can't fall to the ground, 306 00:13:59,660 --> 00:14:02,450 and he can't like walk through pipes and stuff like that. 307 00:14:02,450 --> 00:14:05,780 And we'll have a very simple procedural map generation system which to which 308 00:14:05,780 --> 00:14:08,550 sounds semi complicated, but it's actually really easy. 309 00:14:08,550 --> 00:14:11,200 And I really like procedural generation. 310 00:14:11,200 --> 00:14:14,540 And we'll have very basic sound effects for jumping, hitting 311 00:14:14,540 --> 00:14:16,792 blocks and stuff like that. 312 00:14:16,792 --> 00:14:19,000 Goal number zero was getting a window up and running. 313 00:14:19,000 --> 00:14:19,730 We finished that. 314 00:14:19,730 --> 00:14:20,570 That's easy. 315 00:14:20,570 --> 00:14:23,930 Goal number one, we want to render a screen full of tiles. 316 00:14:23,930 --> 00:14:26,810 And so the first thing I think we should talk about is sprite sheets. 317 00:14:26,810 --> 00:14:30,260 So a sprite sheet is basically just a sheet 318 00:14:30,260 --> 00:14:33,200 a large image that contains a bunch of smaller 319 00:14:33,200 --> 00:14:36,080 images and we index into this image-- 320 00:14:36,080 --> 00:14:38,180 we basically take little chunks out of this 321 00:14:38,180 --> 00:14:40,530 to render individual pieces of our game at once. 322 00:14:40,530 --> 00:14:42,440 And this is how things were done in the NES. 323 00:14:42,440 --> 00:14:45,106 And this is all things are still done today often with 2D games. 324 00:14:45,106 --> 00:14:47,580 325 00:14:47,580 --> 00:14:51,110 We'll have to pair this spreadsheet with what's called a tile map. 326 00:14:51,110 --> 00:14:54,630 And so a tile map is the data representation of our map. 327 00:14:54,630 --> 00:14:58,490 So if we were to assign a value to each tile in the game-- 328 00:14:58,490 --> 00:15:04,100 like, if we wanted a block to be number one and a pipe to be number x. 329 00:15:04,100 --> 00:15:06,050 We need to be able to index that. 330 00:15:06,050 --> 00:15:09,650 So when we draw to the screen, we can say we can look at this big sheet, 331 00:15:09,650 --> 00:15:11,960 not render the whole thing-- just render a tiny chunk. 332 00:15:11,960 --> 00:15:14,150 And that's effectively what we'll be able to do with a tile map. 333 00:15:14,150 --> 00:15:16,280 So we're going to assign an integer value to each tile 334 00:15:16,280 --> 00:15:19,280 that we want, and then our game is going to take that into consideration 335 00:15:19,280 --> 00:15:21,570 and draw just a chunk of that sprite sheet. 336 00:15:21,570 --> 00:15:24,590 And this is what just the raw tiles might look like. 337 00:15:24,590 --> 00:15:29,890 If we were to envision like maybe zero is no tile and one is a brick. 338 00:15:29,890 --> 00:15:31,850 Like this is just a bunch of emptiness. 339 00:15:31,850 --> 00:15:34,820 And then this is a bunch of bricks on the ground. 340 00:15:34,820 --> 00:15:38,960 And then this might be what that looks like in the actual game. 341 00:15:38,960 --> 00:15:42,440 But it's just as simple fundamentally as having a map here 342 00:15:42,440 --> 00:15:46,190 and just writing a function that can loop over this and then draw 343 00:15:46,190 --> 00:15:48,710 each tile where it belongs. 344 00:15:48,710 --> 00:15:54,440 So we're going to look at a demo here of mario-1 if you're in the GitHub repo. 345 00:15:54,440 --> 00:15:57,780 And this is exactly what I just showed you in this slides. 346 00:15:57,780 --> 00:15:59,990 But this is an actual application, and all it's doing 347 00:15:59,990 --> 00:16:01,820 is it's doing what I just described. 348 00:16:01,820 --> 00:16:06,440 Where this is 14 tiles tall of just emptiness 349 00:16:06,440 --> 00:16:10,220 followed by 14 tiles of bricks. 350 00:16:10,220 --> 00:16:13,430 And it has the illusion we're starting to see that this looks like some world 351 00:16:13,430 --> 00:16:16,530 even though we're destroying little graphics over and over again. 352 00:16:16,530 --> 00:16:19,970 And this is how the NES and old hardware used to work back in the day. 353 00:16:19,970 --> 00:16:22,400 They were really good at drawing little tiles, 354 00:16:22,400 --> 00:16:25,220 and they took advantage of that fact. 355 00:16:25,220 --> 00:16:27,740 So we're going to go ahead and go into mario-1 356 00:16:27,740 --> 00:16:29,360 and just look at the main really fast. 357 00:16:29,360 --> 00:16:32,300 And we'll see that we have a new line here-- require map. 358 00:16:32,300 --> 00:16:36,957 So map is a file that we've written ourselves in map.lua. 359 00:16:36,957 --> 00:16:40,040 And we don't need to pay attention to some of the stuff-- this boilerplate 360 00:16:40,040 --> 00:16:40,670 code here. 361 00:16:40,670 --> 00:16:44,990 This effectively lets us abstract out what a map is in code. 362 00:16:44,990 --> 00:16:48,050 It's called object oriented programming if familiar. 363 00:16:48,050 --> 00:16:51,410 And it basically lets us think about maps in terms of code 364 00:16:51,410 --> 00:16:53,450 rather than an abstract concept. 365 00:16:53,450 --> 00:16:57,590 And we can call functions map get tile, map set tile, et cetera. 366 00:16:57,590 --> 00:17:00,770 And sort of build layer by layer our game 367 00:17:00,770 --> 00:17:03,627 and think about it in more high level terms. 368 00:17:03,627 --> 00:17:05,210 So we have a couple of constants here. 369 00:17:05,210 --> 00:17:07,579 Tile brick gets one, tile empty is 29. 370 00:17:07,579 --> 00:17:09,770 And these are the indices-- 371 00:17:09,770 --> 00:17:12,619 if we look back here at the sprite sheet. 372 00:17:12,619 --> 00:17:15,020 So lua is one indexed. 373 00:17:15,020 --> 00:17:17,960 So starting here, this is tile number one. 374 00:17:17,960 --> 00:17:21,660 If we go 29 over, we'll see that this is emptiness right here. 375 00:17:21,660 --> 00:17:26,690 So all this is saying is the tiles that we need to get started are 1 in 29. 376 00:17:26,690 --> 00:17:28,610 The first tile and the 29th tile-- 377 00:17:28,610 --> 00:17:30,380 the brick and empty space-- 378 00:17:30,380 --> 00:17:32,660 to get started drawing. 379 00:17:32,660 --> 00:17:35,990 In our map create function, this just basically sets our map object up. 380 00:17:35,990 --> 00:17:37,850 We're going to set a few fields here. 381 00:17:37,850 --> 00:17:40,880 So we need a reference to our sprite sheet. 382 00:17:40,880 --> 00:17:44,360 So graphic/tiles.png, so fundamentally the same function we used before to get 383 00:17:44,360 --> 00:17:45,230 Mario. 384 00:17:45,230 --> 00:17:47,680 We want to keep track of our tile width and tile height. 385 00:17:47,680 --> 00:17:50,660 We want to keep track of the width and height of our map, which 386 00:17:50,660 --> 00:17:51,767 would be important. 387 00:17:51,767 --> 00:17:54,350 And then we want to keep track of the actual tiles themselves. 388 00:17:54,350 --> 00:17:56,570 This table right here, this empty table, we're 389 00:17:56,570 --> 00:18:00,560 going to fill this with the ones and 29s that we need to represent our game 390 00:18:00,560 --> 00:18:02,840 world, or game map. 391 00:18:02,840 --> 00:18:04,490 And this function here, generate quads. 392 00:18:04,490 --> 00:18:07,750 So LOVE 2D's way of taking a piece of a sprite sheet 393 00:18:07,750 --> 00:18:09,610 is with a object called a quad. 394 00:18:09,610 --> 00:18:12,830 A quad is just a simple rectangle. 395 00:18:12,830 --> 00:18:15,246 You specify an x and y, a width and height. 396 00:18:15,246 --> 00:18:17,120 And it'll know to take a chunk of that sprite 397 00:18:17,120 --> 00:18:19,660 when you draw it, instead of drawing the sprite. 398 00:18:19,660 --> 00:18:22,630 And so I've written a utility function in util.lua. 399 00:18:22,630 --> 00:18:25,660 It's a little bit much, but all it does is 400 00:18:25,660 --> 00:18:29,170 loop through the entire sheet given its width and height 401 00:18:29,170 --> 00:18:32,926 and divide it up into those quads and store them in a table. 402 00:18:32,926 --> 00:18:34,300 So we can index that table later. 403 00:18:34,300 --> 00:18:37,090 404 00:18:37,090 --> 00:18:43,150 So if we go back down here, this line is unimportant, but just related 405 00:18:43,150 --> 00:18:44,290 to the actual-- 406 00:18:44,290 --> 00:18:47,300 it's Lua syntax for getting this to be an object. 407 00:18:47,300 --> 00:18:49,690 This is the important bit of code here. 408 00:18:49,690 --> 00:18:53,519 We're going to iterate through that empty table given a width and height-- 409 00:18:53,519 --> 00:18:56,560 we're going to iterate through our width and height with that empty table 410 00:18:56,560 --> 00:18:57,860 and fill it. 411 00:18:57,860 --> 00:19:01,279 In this case, we're going to start at the top, y gets one. 412 00:19:01,279 --> 00:19:03,820 And then we're going to go all the way down given the height, 413 00:19:03,820 --> 00:19:05,230 and then we're going to start at x 1-- it's 414 00:19:05,230 --> 00:19:07,313 a nested for loop essentially like Mario if you're 415 00:19:07,313 --> 00:19:09,210 familiar with the Mario pset in CS50. 416 00:19:09,210 --> 00:19:12,582 We're starting at the top left and then we're just going row by row, 417 00:19:12,582 --> 00:19:15,040 but we're going to set each tile using a function that I've 418 00:19:15,040 --> 00:19:16,450 written called set tile-- 419 00:19:16,450 --> 00:19:18,250 x,y to tile empty. 420 00:19:18,250 --> 00:19:20,630 And tile empty, recall, we defined as 29. 421 00:19:20,630 --> 00:19:23,110 So this is going to fill our map with 29. 422 00:19:23,110 --> 00:19:25,330 It's going to be just an empty map. 423 00:19:25,330 --> 00:19:29,530 And then this bit of code here is the same thing, but it starts halfway down. 424 00:19:29,530 --> 00:19:32,150 So we're starting y at map height divided by 2, 425 00:19:32,150 --> 00:19:36,759 but it's going to fill the bottom half with tile brick, which is number one. 426 00:19:36,759 --> 00:19:39,550 And then these functions here I referenced earlier get tile and set 427 00:19:39,550 --> 00:19:45,160 tile, they just taken the x and y and because our table is one dimensional, 428 00:19:45,160 --> 00:19:48,470 it needs to convert two dimensional x and y into one dimensional. 429 00:19:48,470 --> 00:19:50,540 So that's just the math you need to-- 430 00:19:50,540 --> 00:19:57,700 given a table-- insert and take out a tile at a particular x, y location. 431 00:19:57,700 --> 00:20:00,130 And then the important part of map here, the thing 432 00:20:00,130 --> 00:20:03,360 that actually draws it to the screen is a function called map render. 433 00:20:03,360 --> 00:20:05,380 This loops using a nested for loop-- 434 00:20:05,380 --> 00:20:11,410 y gets 1, x 1 every line will love.graphics.draw the sprite sheet 435 00:20:11,410 --> 00:20:15,520 but also taughtself.tilesprites, which I showed you earlier, 436 00:20:15,520 --> 00:20:18,140 is the table full of quads. 437 00:20:18,140 --> 00:20:22,750 All those little quads that represent the individual tiles, 438 00:20:22,750 --> 00:20:25,994 it's going to index that at-- 439 00:20:25,994 --> 00:20:27,410 we're going to get the tile there. 440 00:20:27,410 --> 00:20:32,830 So if at x and y tile is equal to a brick, 441 00:20:32,830 --> 00:20:36,730 it's going to look up the brick quad in self and draw the brick quad. 442 00:20:36,730 --> 00:20:39,050 And if it's empty, it's just going to draw nothing. 443 00:20:39,050 --> 00:20:42,320 It's going to draw the empty pit of that sprite sheet. 444 00:20:42,320 --> 00:20:45,560 And then we need to multiply our x minus 1 and y minus 1 445 00:20:45,560 --> 00:20:48,970 because we're one indexed but we still draw zero index. 446 00:20:48,970 --> 00:20:51,850 We're going to multiply that by the tile width 447 00:20:51,850 --> 00:20:56,040 and that'll have the effect of drawing that sprite at a particular x, y times 448 00:20:56,040 --> 00:20:58,000 16 in this case. 449 00:20:58,000 --> 00:21:02,350 So that's all the code that we really need to draw. 450 00:21:02,350 --> 00:21:05,180 And then in main, we have two-- 451 00:21:05,180 --> 00:21:07,180 first of all, what we're doing is we're creating 452 00:21:07,180 --> 00:21:09,520 the map-- map equals map create. 453 00:21:09,520 --> 00:21:13,600 And then down here in the actual draw function, 454 00:21:13,600 --> 00:21:17,020 we're using a function called love.graphics.clear given a color. 455 00:21:17,020 --> 00:21:19,660 In this case, this is just the Mario blue sky color. 456 00:21:19,660 --> 00:21:23,310 108, 140, 55 rgb, width 255 alpha. 457 00:21:23,310 --> 00:21:24,697 So it's completely opaque. 458 00:21:24,697 --> 00:21:27,280 And then we're calling map render, and the map render function 459 00:21:27,280 --> 00:21:28,529 takes care of all the drawing. 460 00:21:28,529 --> 00:21:32,240 So most of what we've done is offloaded to the map file. 461 00:21:32,240 --> 00:21:35,390 We're just calling that in our main.lua file. 462 00:21:35,390 --> 00:21:37,660 So any questions on how this works? 463 00:21:37,660 --> 00:21:47,050 464 00:21:47,050 --> 00:21:49,530 We have a pretty basic scaffolding here. 465 00:21:49,530 --> 00:21:52,710 It's not terribly interesting, we're just trying some tiles to the screen. 466 00:21:52,710 --> 00:21:55,690 Our first step into getting it to actually look visually interesting, 467 00:21:55,690 --> 00:21:57,270 I think, is getting it to scroll. 468 00:21:57,270 --> 00:21:59,190 Because games of that era-- 469 00:21:59,190 --> 00:22:02,290 Super Mario Bros.-- were called to 2D side scrollers. 470 00:22:02,290 --> 00:22:04,457 Your job is to go through the level, reach the end. 471 00:22:04,457 --> 00:22:06,540 And in order to do that, we have to have some sort 472 00:22:06,540 --> 00:22:08,010 of notion of a camera in our game. 473 00:22:08,010 --> 00:22:12,190 We have to look at where we are and move and shift our scene. 474 00:22:12,190 --> 00:22:13,890 So we'll effectively have some-- 475 00:22:13,890 --> 00:22:17,339 it's not going to be technically a camera. 476 00:22:17,339 --> 00:22:19,630 In our code, we're going to pretend like it's a camera. 477 00:22:19,630 --> 00:22:22,379 We're going to keep track of some sort of top left corner of where 478 00:22:22,379 --> 00:22:23,950 we might have our camera. 479 00:22:23,950 --> 00:22:27,600 And then we want to scroll our x and y on that camera 480 00:22:27,600 --> 00:22:30,970 whenever we want to move in a particular direction. 481 00:22:30,970 --> 00:22:33,930 And the way that we actually accomplish this in LOVE 2D 482 00:22:33,930 --> 00:22:36,480 is a function called love.graphics.translate, 483 00:22:36,480 --> 00:22:37,590 and it takes an x and y. 484 00:22:37,590 --> 00:22:42,240 So this has the effect simply of translating literally everything we're 485 00:22:42,240 --> 00:22:44,230 drawing in the window. 486 00:22:44,230 --> 00:22:47,970 And this makes it look like we're moving but we're not actually moving yet. 487 00:22:47,970 --> 00:22:51,330 So if we call love.graphics.translate x 100, 488 00:22:51,330 --> 00:22:54,000 we're going to shift 100 to the right and that's 489 00:22:54,000 --> 00:22:57,250 going to have the effect of actually having moved backwards. 490 00:22:57,250 --> 00:23:00,420 So if you want to make it look like we're moving forwards given an offset, 491 00:23:00,420 --> 00:23:03,120 we need to actually translate it to the negative direction. 492 00:23:03,120 --> 00:23:06,280 And that'll make us look like we're moving forward to the level. 493 00:23:06,280 --> 00:23:09,960 So let's go ahead and look at mario-2, I'm going to run the example here. 494 00:23:09,960 --> 00:23:13,780 495 00:23:13,780 --> 00:23:17,630 And we see that we have a very simple scrolling background now, instead 496 00:23:17,630 --> 00:23:18,770 of a static background. 497 00:23:18,770 --> 00:23:21,470 And all this is doing is just calling love.graphics.translate 498 00:23:21,470 --> 00:23:24,830 on some constantly updating x value. 499 00:23:24,830 --> 00:23:27,050 And I'll show you here in mario-2. 500 00:23:27,050 --> 00:23:30,800 If we go to map, something that we fundamentally 501 00:23:30,800 --> 00:23:34,370 changed is now our update function in map 502 00:23:34,370 --> 00:23:38,390 is modifying some value that we've called self.cam x. 503 00:23:38,390 --> 00:23:43,640 We're doing self.cam x equals self.cam x plus delta time times the scroll speed. 504 00:23:43,640 --> 00:23:48,009 Recall, delta time was the value that LOVE is going to create every frame. 505 00:23:48,009 --> 00:23:49,800 It's going to be the amount of seconds that 506 00:23:49,800 --> 00:23:54,380 have elapsed since the last frame of rendered output. 507 00:23:54,380 --> 00:23:59,780 And see up here, we have initialized cam x to zero and cam y to negative three. 508 00:23:59,780 --> 00:24:03,140 So this has the effect of cumulatively adding 509 00:24:03,140 --> 00:24:05,579 to self.cam x times scroll speed. 510 00:24:05,579 --> 00:24:07,370 And scroll speed also we've defined up here 511 00:24:07,370 --> 00:24:09,830 to be 62, which is some arbitrary value that I found 512 00:24:09,830 --> 00:24:12,186 was a nice rate of movement. 513 00:24:12,186 --> 00:24:14,060 And that's effectively going to be multiplied 514 00:24:14,060 --> 00:24:18,050 by some 0.0 something value that's number of seconds 515 00:24:18,050 --> 00:24:21,440 since the last frame of about. 516 00:24:21,440 --> 00:24:25,459 If we go back to main, we have made one other change 517 00:24:25,459 --> 00:24:28,250 and that's that we actually have to update the map with that update 518 00:24:28,250 --> 00:24:29,160 function. 519 00:24:29,160 --> 00:24:33,690 So all your updates must be funneled through this love.update dt function. 520 00:24:33,690 --> 00:24:36,934 So in here, all we do is we call a map update dt, 521 00:24:36,934 --> 00:24:40,100 and the dt when we get from love.update update will get passed into to there 522 00:24:40,100 --> 00:24:41,120 and our map will update. 523 00:24:41,120 --> 00:24:46,310 The camera is constantly be moving to the right, self.cam x. 524 00:24:46,310 --> 00:24:52,220 And we can see here that cam x gets used in the actual translate function-- 525 00:24:52,220 --> 00:24:54,200 love.graphics.translate. 526 00:24:54,200 --> 00:24:58,100 Math.floor is simply a function that converts a floating point 527 00:24:58,100 --> 00:25:00,380 number to its lowest integer form, which is just 528 00:25:00,380 --> 00:25:02,840 important for a very pixelated games. 529 00:25:02,840 --> 00:25:05,420 We want things to be rendered in discrete-- 530 00:25:05,420 --> 00:25:09,140 basically, if you're rendering to a virtual resolution-- so in this case, 531 00:25:09,140 --> 00:25:11,560 we have in any case look to our game. 532 00:25:11,560 --> 00:25:14,420 We're essentially rendering to a virtual texture to do this. 533 00:25:14,420 --> 00:25:16,130 This Isn't necessarily important but just 534 00:25:16,130 --> 00:25:18,800 for a low level understanding of how it works. 535 00:25:18,800 --> 00:25:22,760 If you render to a texture something that 536 00:25:22,760 --> 00:25:27,470 is low resolution but at a sort of a point between 0-- 537 00:25:27,470 --> 00:25:31,437 something that's not discrete like a floating point value like 10.5. 538 00:25:31,437 --> 00:25:33,020 It's going to artifact really strange. 539 00:25:33,020 --> 00:25:36,019 It's going to look like it's got extra lines and be weird and pixelated. 540 00:25:36,019 --> 00:25:38,990 So we just want to make sure any value that gets passed into to here 541 00:25:38,990 --> 00:25:40,190 is an integer value. 542 00:25:40,190 --> 00:25:42,380 And that's all that that essentially does. 543 00:25:42,380 --> 00:25:46,160 It floors it, and then we're also rounding it 544 00:25:46,160 --> 00:25:48,431 by adding 0.5 before we do that. 545 00:25:48,431 --> 00:25:50,930 And then notice that we we're passing in the negative camera 546 00:25:50,930 --> 00:25:52,010 x, not the positive one. 547 00:25:52,010 --> 00:25:53,480 Because if we do the positive one, it's going 548 00:25:53,480 --> 00:25:54,938 to look like we're going backwards. 549 00:25:54,938 --> 00:25:58,010 So we do the negative one because the scene is shifting negative, 550 00:25:58,010 --> 00:26:01,419 so it's making us look like we're going forward if that makes sense. 551 00:26:01,419 --> 00:26:03,710 Does anybody have any questions on how this code works? 552 00:26:03,710 --> 00:26:08,740 553 00:26:08,740 --> 00:26:12,270 So goal number three, we have something kind of cool up on the screen. 554 00:26:12,270 --> 00:26:13,740 But we have a lot farther to go. 555 00:26:13,740 --> 00:26:15,060 We want to actually have input. 556 00:26:15,060 --> 00:26:16,570 So we've got like the update phase working. 557 00:26:16,570 --> 00:26:17,790 We've got the render phase working, but we 558 00:26:17,790 --> 00:26:19,610 don't have the input phase working yet. 559 00:26:19,610 --> 00:26:21,120 It's not interactive. 560 00:26:21,120 --> 00:26:23,580 So to do that we're going to use a couple of functions. 561 00:26:23,580 --> 00:26:24,240 We've seen one. 562 00:26:24,240 --> 00:26:26,070 We saw love.keypressed key which is simply 563 00:26:26,070 --> 00:26:30,360 a callback function that executes immediately upon you pressing a key, 564 00:26:30,360 --> 00:26:33,360 but only does it that moment and only does it one time. 565 00:26:33,360 --> 00:26:37,140 The other function that we need to use is love.keyboard.isDown key 566 00:26:37,140 --> 00:26:42,540 which will return true whenever that key is being pressed down constantly. 567 00:26:42,540 --> 00:26:45,000 And then that's how we get constant movement. 568 00:26:45,000 --> 00:26:47,135 Because if we just press a key, we want Mario 569 00:26:47,135 --> 00:26:49,384 to keep moving even though we're holding the key down. 570 00:26:49,384 --> 00:26:51,930 We want to keep pressing the key down for him to keep moving. 571 00:26:51,930 --> 00:26:54,040 We're going look at a demo of mario-3 here. 572 00:26:54,040 --> 00:26:55,665 I'm going to go ahead and run it first. 573 00:26:55,665 --> 00:27:02,170 574 00:27:02,170 --> 00:27:04,540 So we have basically the same scene that we saw before, 575 00:27:04,540 --> 00:27:09,760 but if I press the keys on the keyboard, we start moving right, left, up, down, 576 00:27:09,760 --> 00:27:10,870 any direction we want. 577 00:27:10,870 --> 00:27:13,300 We can also now see that our map is actually half filled 578 00:27:13,300 --> 00:27:17,320 with tiles and half filled the space. 579 00:27:17,320 --> 00:27:18,980 This is the beginning of our game. 580 00:27:18,980 --> 00:27:20,230 We have something interactive. 581 00:27:20,230 --> 00:27:24,910 This is how we get started with more complicated interactivity. 582 00:27:24,910 --> 00:27:29,230 So let's go ahead and look at our code in mario-3. 583 00:27:29,230 --> 00:27:34,990 If we pull up the map file, the fundamental update that we've made here 584 00:27:34,990 --> 00:27:39,460 is we've modified map.update to take in keyboard input now. 585 00:27:39,460 --> 00:27:45,130 So we see if love.keyboard.isDown left then we're going to modify our cam x. 586 00:27:45,130 --> 00:27:48,980 And if we're pressing right, we're going to increment it. 587 00:27:48,980 --> 00:27:51,940 The same thing for up and down on the y-axis, our cam y, 588 00:27:51,940 --> 00:27:54,040 decremented or incremented. 589 00:27:54,040 --> 00:27:56,710 The math.max, math.min-- these are effectively 590 00:27:56,710 --> 00:28:00,830 ways for us to constrain our movement to the maps boundaries. 591 00:28:00,830 --> 00:28:02,980 We don't need to worry about it in too much depth, 592 00:28:02,980 --> 00:28:06,100 but math.max returns the greater of two values. 593 00:28:06,100 --> 00:28:08,140 Math.min returns the lesser of two values. 594 00:28:08,140 --> 00:28:10,780 And we're making the greater and lesser of those values 595 00:28:10,780 --> 00:28:14,240 the boundaries of our map top, down, left, and right. 596 00:28:14,240 --> 00:28:16,240 That's all that's fundamentally changed. 597 00:28:16,240 --> 00:28:19,240 And because of that, now when we press keyboard input, rather than 598 00:28:19,240 --> 00:28:23,290 it doing autonomously, we can see our map move in any direction. 599 00:28:23,290 --> 00:28:24,880 So any questions on how this works? 600 00:28:24,880 --> 00:28:27,910 601 00:28:27,910 --> 00:28:30,270 OK, cool. 602 00:28:30,270 --> 00:28:32,257 So next goal. 603 00:28:32,257 --> 00:28:33,840 Our maps a little bland at the moment. 604 00:28:33,840 --> 00:28:35,136 It's just a bunch of tiles. 605 00:28:35,136 --> 00:28:37,260 But it'll be really cool to start seeing the things 606 00:28:37,260 --> 00:28:40,920 that we know from Mario like pipes and clouds and bushes and stuff like that. 607 00:28:40,920 --> 00:28:46,780 Just go ahead and pull up mario-4 and see what that looks like. 608 00:28:46,780 --> 00:28:51,490 So here we have the exact same base of code, 609 00:28:51,490 --> 00:28:54,540 but now we're rendering a lot more interesting output. 610 00:28:54,540 --> 00:28:56,010 We have gaps in our level. 611 00:28:56,010 --> 00:28:57,210 We have bushes. 612 00:28:57,210 --> 00:28:59,840 We have those question mark blocks and pipes. 613 00:28:59,840 --> 00:29:01,320 We have a whole bunch of stuff. 614 00:29:01,320 --> 00:29:03,360 It's starting actually look like Super Mario 615 00:29:03,360 --> 00:29:06,014 Bros., which I think is pretty cool. 616 00:29:06,014 --> 00:29:08,430 And to do that, I'm going to go ahead and pull up mario-4. 617 00:29:08,430 --> 00:29:11,360 618 00:29:11,360 --> 00:29:16,060 And what's changed about this code here is our create function for map 619 00:29:16,060 --> 00:29:19,400 where we've nationalized all the data in the map, now we're doing-- 620 00:29:19,400 --> 00:29:21,400 and we don't need to spend too much time on this 621 00:29:21,400 --> 00:29:23,108 because it's kind of an optional segment. 622 00:29:23,108 --> 00:29:29,860 But this is an example of very, very basic procedural generation. 623 00:29:29,860 --> 00:29:33,460 We're saying if math.random 20, which turns a random value within the range 624 00:29:33,460 --> 00:29:37,390 1 to 20, if it's equal to 1, which means we have a 5% chance, 625 00:29:37,390 --> 00:29:41,130 then start a cloud at some point in our height 626 00:29:41,130 --> 00:29:42,790 that's above the middle of the map. 627 00:29:42,790 --> 00:29:45,490 And then set these six tiles, which together 628 00:29:45,490 --> 00:29:50,380 form the cloud because if we look at the example again, 629 00:29:50,380 --> 00:29:52,210 these are all individual tiles. 630 00:29:52,210 --> 00:29:55,150 And every tile in the game is 16 by 16 pixels, 631 00:29:55,150 --> 00:29:57,110 but this pipe is not 16 pixels wide. 632 00:29:57,110 --> 00:29:59,800 And this cloud is not 16 pixels tall by 16 pixels wide. 633 00:29:59,800 --> 00:30:02,080 These are actually comprised of multiple pieces 634 00:30:02,080 --> 00:30:06,040 and to get it to look like we have a pipe or a cloud or a bush, 635 00:30:06,040 --> 00:30:10,150 when we create one of these things, we have to lay out several pieces together 636 00:30:10,150 --> 00:30:13,660 in a way that they all fit together. 637 00:30:13,660 --> 00:30:17,740 Fun fact, the NES was pretty constrained and the developers actually 638 00:30:17,740 --> 00:30:23,080 used the same sprites for the Bush and the cloud. 639 00:30:23,080 --> 00:30:26,690 We're not doing that in this case, but this is how you got by back in the day. 640 00:30:26,690 --> 00:30:29,862 They would pass this gray scale image-- 641 00:30:29,862 --> 00:30:31,570 it wouldn't even be blue back in the day. 642 00:30:31,570 --> 00:30:32,903 It would be like shades of gray. 643 00:30:32,903 --> 00:30:36,610 And they would pass it into the NES hardware with a particular palette, 644 00:30:36,610 --> 00:30:37,930 and it would make it green. 645 00:30:37,930 --> 00:30:39,930 They could make it whatever color they wanted to 646 00:30:39,930 --> 00:30:44,130 and that's a basic asset recycling. 647 00:30:44,130 --> 00:30:47,440 And so what we're defectively doing is just 648 00:30:47,440 --> 00:30:51,070 choosing a random chance like every-- 649 00:30:51,070 --> 00:30:55,540 I should also say we're doing this on a line by line level of our map. 650 00:30:55,540 --> 00:30:58,030 So we have to go through our entire map and generate it. 651 00:30:58,030 --> 00:31:00,790 So the best way to do it is to-- 652 00:31:00,790 --> 00:31:03,864 at least, the best way I found was to do it in vertical scan lines. 653 00:31:03,864 --> 00:31:05,530 So starting at the left, we have no map. 654 00:31:05,530 --> 00:31:10,360 If we go down, we have a chance to generate ground and/or maybe a pipe 655 00:31:10,360 --> 00:31:11,740 or a cloud or a bush. 656 00:31:11,740 --> 00:31:15,129 So do it, go to the next line, keep doing it. 657 00:31:15,129 --> 00:31:16,420 Do we want to generate a cloud? 658 00:31:16,420 --> 00:31:17,650 Maybe, let's generate a cloud. 659 00:31:17,650 --> 00:31:20,500 If we do generate a cloud, we need to lay out the pieces of the cloud, 660 00:31:20,500 --> 00:31:21,333 and then keep going. 661 00:31:21,333 --> 00:31:24,027 And then sometimes we can say, optionally, hey, 662 00:31:24,027 --> 00:31:26,110 maybe we want to break in look in the ground here. 663 00:31:26,110 --> 00:31:29,890 So don't actually draw any ground tiles, just keep going. 664 00:31:29,890 --> 00:31:33,730 Just go all the way down and go the next one, draw ground, draw ground-- 665 00:31:33,730 --> 00:31:34,900 oh, don't draw ground here. 666 00:31:34,900 --> 00:31:39,270 So we have these obstacles we're generating as well as a result of this. 667 00:31:39,270 --> 00:31:43,450 And here in the code, we have, here, this is clouds and this is pipes. 668 00:31:43,450 --> 00:31:46,100 5% chance to generate pipe. 669 00:31:46,100 --> 00:31:48,090 10% chance to generate to a bush. 670 00:31:48,090 --> 00:31:52,360 So you tweak these values as you think makes for a good algorithm. 671 00:31:52,360 --> 00:31:58,630 If you want to see more bushes increase the percent chance per math.random 672 00:31:58,630 --> 00:32:01,630 that you're looking for one. 673 00:32:01,630 --> 00:32:04,180 And then that will give you a higher chance. 674 00:32:04,180 --> 00:32:06,370 Like if we made this five, for example, then 675 00:32:06,370 --> 00:32:10,759 we have a 20% chance to generate a bush, not a 10-- or a gap, sorry. 676 00:32:10,759 --> 00:32:11,800 In this case, it's a gap. 677 00:32:11,800 --> 00:32:13,030 We have a 20% chance. 678 00:32:13,030 --> 00:32:17,260 So we'll have a lot more gaps in our map if we do that. 679 00:32:17,260 --> 00:32:20,320 And this is all we're doing, we're just going in vertical scan lines 680 00:32:20,320 --> 00:32:21,250 throughout our-- 681 00:32:21,250 --> 00:32:26,530 we're going for y gets one and then we're going all the way down y 682 00:32:26,530 --> 00:32:29,774 and then increment x and then increment x, go all the way down. 683 00:32:29,774 --> 00:32:31,940 And that's effectively how we're generating our map. 684 00:32:31,940 --> 00:32:32,536 Yeah. 685 00:32:32,536 --> 00:32:40,757 AUDIENCE: [INAUDIBLE] 686 00:32:40,757 --> 00:32:42,590 COLTON OGDEN: You can do a couple of things. 687 00:32:42,590 --> 00:32:44,460 I think I took that into consideration. 688 00:32:44,460 --> 00:32:46,785 689 00:32:46,785 --> 00:32:48,910 One way that I can think of off the top of my head, 690 00:32:48,910 --> 00:32:54,020 keep track of some sort of flag in your code that says if gap created 691 00:32:54,020 --> 00:32:56,440 and set that to true when you start a gap. 692 00:32:56,440 --> 00:32:58,260 And you can tailor it to however you want. 693 00:32:58,260 --> 00:33:00,426 But let's say you want all your gaps to be two wide, 694 00:33:00,426 --> 00:33:04,967 if gap created equal true then on the next one, you'll test for that you say, 695 00:33:04,967 --> 00:33:05,550 oh, it's true. 696 00:33:05,550 --> 00:33:08,091 So on the last scan when it created a gap, create another gap 697 00:33:08,091 --> 00:33:09,510 but then set it to false. 698 00:33:09,510 --> 00:33:12,190 And then on the next loop, it's going to be false. 699 00:33:12,190 --> 00:33:15,530 You could then set gap just created so that you don't like accidentally 700 00:33:15,530 --> 00:33:18,109 redo another gap and make it like too wide. 701 00:33:18,109 --> 00:33:20,150 But there's a bunch of other ways you could do it 702 00:33:20,150 --> 00:33:22,566 but, yeah, I think a flag would probably make sense there. 703 00:33:22,566 --> 00:33:23,085 Yes? 704 00:33:23,085 --> 00:33:26,200 AUDIENCE: When are we going to ever have clouds to lighting? 705 00:33:26,200 --> 00:33:29,160 It seems like the way it's set up because a cloud is 706 00:33:29,160 --> 00:33:31,660 greater than a vertical scan line. 707 00:33:31,660 --> 00:33:36,904 It seems like you could on the very next scan have another cloud. 708 00:33:36,904 --> 00:33:39,160 Or maybe you have seen clouds to lighting? 709 00:33:39,160 --> 00:33:42,490 COLTON OGDEN: I think I might have set that in the code. 710 00:33:42,490 --> 00:33:43,750 I'm not sure. 711 00:33:43,750 --> 00:33:46,990 It's been a little while since I looked at this. 712 00:33:46,990 --> 00:33:48,700 It could just be the random chance. 713 00:33:48,700 --> 00:33:51,700 I ran it several times and I might just have not stumbled upon that. 714 00:33:51,700 --> 00:33:54,408 That's something that you could also keep track of what the flag. 715 00:33:54,408 --> 00:33:55,970 Just say cloud just created. 716 00:33:55,970 --> 00:33:59,470 And then because a cloud spans three vertical scan lines, 717 00:33:59,470 --> 00:34:02,952 you could say is true, is true, or keep a counter in place. 718 00:34:02,952 --> 00:34:03,910 Do something like that. 719 00:34:03,910 --> 00:34:07,477 720 00:34:07,477 --> 00:34:09,310 At that point, probably start having a table 721 00:34:09,310 --> 00:34:12,370 with all of the different structures that you want just 722 00:34:12,370 --> 00:34:15,159 for organizational purpose. 723 00:34:15,159 --> 00:34:18,550 Maybe the table would be called just created and you can index it-- cloud, 724 00:34:18,550 --> 00:34:21,100 pipe, bush, et cetera-- and then do it that way. 725 00:34:21,100 --> 00:34:23,980 That's probably the cleaner way to do it. 726 00:34:23,980 --> 00:34:27,944 Any questions on how this is being implemented at the moment? 727 00:34:27,944 --> 00:34:28,860 Cool. 728 00:34:28,860 --> 00:34:31,550 All right, so things are going to look pretty spicy. 729 00:34:31,550 --> 00:34:36,380 But we don't have a character that we can control and that's important. 730 00:34:36,380 --> 00:34:38,929 So the next step is going to be actually adding Mario. 731 00:34:38,929 --> 00:34:41,300 And Mario fundamentally isn't a whole lot 732 00:34:41,300 --> 00:34:44,510 different in terms of getting him started drawing on the screen 733 00:34:44,510 --> 00:34:48,110 than it was with our sprite sheet, with our tiles. 734 00:34:48,110 --> 00:34:49,370 We have another sprite sheet. 735 00:34:49,370 --> 00:34:51,120 This is Mario's sprite sheet here. 736 00:34:51,120 --> 00:34:54,770 And these are going to be quads just like we did with the tiles. 737 00:34:54,770 --> 00:34:57,860 Except in this case, he is two tiles tall by one tile 738 00:34:57,860 --> 00:35:00,190 wide so he stands out a little bit more. 739 00:35:00,190 --> 00:35:04,730 And so we're going to need a new object, a new class 740 00:35:04,730 --> 00:35:08,060 called an animation so that-- because when he moves, for example, 741 00:35:08,060 --> 00:35:09,280 his frame changes. 742 00:35:09,280 --> 00:35:11,030 It's not going to be just one frame that's 743 00:35:11,030 --> 00:35:12,290 constantly being drawn to the screen. 744 00:35:12,290 --> 00:35:14,480 Depending on what he's doing, whether he's jumping or walking or running, 745 00:35:14,480 --> 00:35:15,402 et cetera-- 746 00:35:15,402 --> 00:35:16,360 that's going to change. 747 00:35:16,360 --> 00:35:18,320 And so we're going to need some construct for that. 748 00:35:18,320 --> 00:35:21,445 But just to get him started drawing to the screen, it's a lot of the pieces 749 00:35:21,445 --> 00:35:23,310 that we've already used. 750 00:35:23,310 --> 00:35:25,640 So I'm going to go ahead and run mario-5 here. 751 00:35:25,640 --> 00:35:28,290 752 00:35:28,290 --> 00:35:32,460 And we see Mario now drawn to the screen, and in this limited example, 753 00:35:32,460 --> 00:35:34,180 he can also move his head left and right. 754 00:35:34,180 --> 00:35:39,060 So we can see this shows us that we have him now flipping his sprite. 755 00:35:39,060 --> 00:35:42,690 So it doesn't make sense for us to have a sprite 756 00:35:42,690 --> 00:35:44,700 for him facing both directions. 757 00:35:44,700 --> 00:35:47,664 We can easily just flip it based on what direction he's facing 758 00:35:47,664 --> 00:35:49,330 and then save ourselves half the frames. 759 00:35:49,330 --> 00:35:51,030 AUDIENCE: You stepped on a cloud up there. 760 00:35:51,030 --> 00:35:51,870 COLTON OGDEN: Oh, there we go. 761 00:35:51,870 --> 00:35:52,590 See, exactly. 762 00:35:52,590 --> 00:35:54,890 I guess I haven't taken it into consideration. 763 00:35:54,890 --> 00:35:55,780 We'll do it again. 764 00:35:55,780 --> 00:35:59,370 So now we can see it spawning different levels every time I do it. 765 00:35:59,370 --> 00:36:01,590 [INAUDIBLE] He's standing above the ground. 766 00:36:01,590 --> 00:36:04,071 No collision detection yet. 767 00:36:04,071 --> 00:36:06,570 So I haven't taken that into consideration for this example. 768 00:36:06,570 --> 00:36:09,240 But if I were to go back and fix it, which I probably should, 769 00:36:09,240 --> 00:36:13,530 I would have some sort of table of the different pieces I've created 770 00:36:13,530 --> 00:36:16,870 and just keep track of whether they were created on the previous x scan line 771 00:36:16,870 --> 00:36:19,510 depending on how wide the structure is. 772 00:36:19,510 --> 00:36:26,580 So if you go into mario-5, we have a new a class called player.lua. 773 00:36:26,580 --> 00:36:30,050 And this is going to contain all of the information we need for our player. 774 00:36:30,050 --> 00:36:31,260 He's got an x and y. 775 00:36:31,260 --> 00:36:34,230 He's got a width and height, an x offset and a y offset. 776 00:36:34,230 --> 00:36:37,440 And these are important because we need to tell 777 00:36:37,440 --> 00:36:43,560 LOVE to scale our sprite on the x-axis, depending on which direction he's 778 00:36:43,560 --> 00:36:44,790 facing to get it to flip. 779 00:36:44,790 --> 00:36:48,810 In 2D, to get a texture to flip on any given axis, 780 00:36:48,810 --> 00:36:51,660 you need to scale it negatively on that axis. 781 00:36:51,660 --> 00:36:55,320 782 00:36:55,320 --> 00:36:58,830 And the origin of that scale needs to be in the middle of that sprite. 783 00:36:58,830 --> 00:37:02,520 In this case, since Mario is 16 pixels wide by 32 pixels tall, 784 00:37:02,520 --> 00:37:05,485 the center of origin should be (8, 16). 785 00:37:05,485 --> 00:37:08,610 We're going to need a reference to the map so that he can interact with it. 786 00:37:08,610 --> 00:37:12,450 We're going to give him a texture, mario-1.png. 787 00:37:12,450 --> 00:37:17,979 Frames of animation, just this table, a current frame, state. 788 00:37:17,979 --> 00:37:21,270 The state is going to be very important, and we'll talk about as state machines 789 00:37:21,270 --> 00:37:22,240 in just a moment. 790 00:37:22,240 --> 00:37:25,323 This is going to be how we get Mario to do different things based on input 791 00:37:25,323 --> 00:37:27,700 into transition between those things. 792 00:37:27,700 --> 00:37:31,290 A direction for him to face so that we can say if you're facing to the right 793 00:37:31,290 --> 00:37:34,470 flip your sprite otherwise render it normally. 794 00:37:34,470 --> 00:37:38,190 And then a delta x and delta y, which is the standard way 795 00:37:38,190 --> 00:37:43,890 of representing velocity on your x and y-axis in animation. 796 00:37:43,890 --> 00:37:46,200 So we're going to initialize x and y. 797 00:37:46,200 --> 00:37:49,710 We're going to put him right above the middle of the map 798 00:37:49,710 --> 00:37:52,170 and then about 10 tiles to the right. 799 00:37:52,170 --> 00:37:54,420 And then give him just one frame for now. 800 00:37:54,420 --> 00:37:57,870 And this is the quad that I was referencing earlier. 801 00:37:57,870 --> 00:38:00,805 To make a quad, just love.graphics.newquad, the x and the y 802 00:38:00,805 --> 00:38:03,930 in that sprite sheet you want to start the quad at and then the dimensions. 803 00:38:03,930 --> 00:38:06,810 In this case, Mario is 16 pixels wide by 32 pixels tall. 804 00:38:06,810 --> 00:38:10,620 So at the very beginning at 16 pixels and 32 pixels, 805 00:38:10,620 --> 00:38:13,420 create the quad and it needs the texture dimensions as well, 806 00:38:13,420 --> 00:38:16,860 which you can use with to get dimensions function there. 807 00:38:16,860 --> 00:38:17,900 Set his current frame. 808 00:38:17,900 --> 00:38:20,490 There's only one frame in this .frame so Lua is one index. 809 00:38:20,490 --> 00:38:22,200 So this .frame is one. 810 00:38:22,200 --> 00:38:23,950 And then this is an important part here. 811 00:38:23,950 --> 00:38:25,590 This behaviors stable. 812 00:38:25,590 --> 00:38:29,820 This is going to be where we put all of our states that Mario can be in. 813 00:38:29,820 --> 00:38:32,460 And this is one thing I really like about Lua as a language 814 00:38:32,460 --> 00:38:34,560 and things like JavaScript as well. 815 00:38:34,560 --> 00:38:37,202 Having anonymous functions makes it just very easy 816 00:38:37,202 --> 00:38:38,910 to package together bits of code that you 817 00:38:38,910 --> 00:38:41,070 might need to swap on a given basis. 818 00:38:41,070 --> 00:38:43,290 In this case, this table-- 819 00:38:43,290 --> 00:38:46,080 we're saying at index idle, we're just going to store a function. 820 00:38:46,080 --> 00:38:48,944 Literally a function that's unnamed, an anonymous function. 821 00:38:48,944 --> 00:38:50,110 And it's an update function. 822 00:38:50,110 --> 00:38:51,300 It takes in a dt. 823 00:38:51,300 --> 00:38:53,230 And then we want this behavior. 824 00:38:53,230 --> 00:38:57,790 And then we're going to call this idle function in our code in just a second. 825 00:38:57,790 --> 00:39:00,450 But we can see it's as simple as right now, if we 826 00:39:00,450 --> 00:39:02,840 pressed left, his direction is left. 827 00:39:02,840 --> 00:39:05,350 And if we press right, his direction is right. 828 00:39:05,350 --> 00:39:07,060 This will be used in just a moment. 829 00:39:07,060 --> 00:39:09,227 So in the update function, we're referencing 830 00:39:09,227 --> 00:39:10,810 this behavior's table we just created. 831 00:39:10,810 --> 00:39:14,900 We're saying in self.behaviors at our state-- 832 00:39:14,900 --> 00:39:20,010 and his state, recall, we set to idle. 833 00:39:20,010 --> 00:39:22,500 We're going to just call it like a function. 834 00:39:22,500 --> 00:39:25,410 Just index the table and then call it passing in dt. 835 00:39:25,410 --> 00:39:28,890 And then this, we can just swap in and out depending on his state. 836 00:39:28,890 --> 00:39:31,120 Super simple. 837 00:39:31,120 --> 00:39:33,570 We need to scale x in our render function 838 00:39:33,570 --> 00:39:36,720 because this is how we're going to get them to actually flip. 839 00:39:36,720 --> 00:39:39,980 If his direction is right, his scale x is normal. 840 00:39:39,980 --> 00:39:43,380 But if it's not right, so it's left. 841 00:39:43,380 --> 00:39:44,910 It's going to be negative one. 842 00:39:44,910 --> 00:39:49,890 And so if we scale anything by negative one, it's going to flip it in 2D. 843 00:39:49,890 --> 00:39:54,600 So love.graphics.draw, the texture, the frame, put him at the x and y 844 00:39:54,600 --> 00:39:55,710 that we need. 845 00:39:55,710 --> 00:39:59,580 Zero rotation and then scale x here. 846 00:39:59,580 --> 00:40:02,130 And this will have the effect of whether-- 847 00:40:02,130 --> 00:40:04,020 because this is going to run every frame, 848 00:40:04,020 --> 00:40:06,680 if he's right based on what we've input up here 849 00:40:06,680 --> 00:40:11,370 because the update function calls this update anonymous function. 850 00:40:11,370 --> 00:40:14,420 He's going to be looking to the left or to the right. 851 00:40:14,420 --> 00:40:16,899 And so that that has the effect, one more time, 852 00:40:16,899 --> 00:40:18,690 when I press left-- direction set to right. 853 00:40:18,690 --> 00:40:19,774 Direction is set to left. 854 00:40:19,774 --> 00:40:21,690 Direction set to right, direction set to left. 855 00:40:21,690 --> 00:40:24,810 So you have the beginnings of Mario on the screen. 856 00:40:24,810 --> 00:40:26,670 So any questions as to how this works? 857 00:40:26,670 --> 00:40:30,970 858 00:40:30,970 --> 00:40:32,200 All right. 859 00:40:32,200 --> 00:40:35,250 So it's not all that interesting, he's stationary. 860 00:40:35,250 --> 00:40:38,670 So now we're going to add movement to him and this is a crucial step 861 00:40:38,670 --> 00:40:40,420 in to actually accomplishing what we want. 862 00:40:40,420 --> 00:40:43,140 So how are we going to move Mario? 863 00:40:43,140 --> 00:40:45,209 In this case, it's actually can be very easy-- 864 00:40:45,209 --> 00:40:47,250 delta x and delta y is probably more than we even 865 00:40:47,250 --> 00:40:49,666 need to worry about here because it's not going to change. 866 00:40:49,666 --> 00:40:52,560 But in 2D game development, it's common for delta x and delta y 867 00:40:52,560 --> 00:40:56,040 to sort of be a function over time rather than some discrete value. 868 00:40:56,040 --> 00:40:59,520 So when you move Mario, you might want him to start moving slowly 869 00:40:59,520 --> 00:41:00,594 and then move faster. 870 00:41:00,594 --> 00:41:02,760 And that's where your velocity is actually relevant. 871 00:41:02,760 --> 00:41:04,210 In our examples here, we're not going to do that. 872 00:41:04,210 --> 00:41:06,010 It's just going to be one concrete value. 873 00:41:06,010 --> 00:41:07,676 So his movement is going to be the same. 874 00:41:07,676 --> 00:41:12,300 But to get a certain feel in your 2D game to get it to feel right, 875 00:41:12,300 --> 00:41:17,170 you're going to need to tune the graph of your movement accordingly. 876 00:41:17,170 --> 00:41:20,910 And so we're just going to give him a positive dx and a negative dx 877 00:41:20,910 --> 00:41:26,560 and that's going to manipulate his actual x and y value per frame. 878 00:41:26,560 --> 00:41:28,570 And we'll see that here in mario-6. 879 00:41:28,570 --> 00:41:30,278 So I'm going to go ahead and run mario-6. 880 00:41:30,278 --> 00:41:33,002 881 00:41:33,002 --> 00:41:35,710 He's in the middle of the pipe because there is no collision yet, 882 00:41:35,710 --> 00:41:37,960 but if we move him left and right. 883 00:41:37,960 --> 00:41:41,200 MC Hammer style, he's moving. 884 00:41:41,200 --> 00:41:45,700 And he's not animating yet, but we have effectively the very beginnings 885 00:41:45,700 --> 00:41:48,040 of the core gameplay of Mario. 886 00:41:48,040 --> 00:41:52,450 He's moving through a level, and we see the environment and no collision yet, 887 00:41:52,450 --> 00:41:53,800 but we'll get to that. 888 00:41:53,800 --> 00:41:57,880 Let's go ahead and actually see how we have this working in mario-6. 889 00:41:57,880 --> 00:42:03,670 If you go to player, we see that we've changed the idle behavior 890 00:42:03,670 --> 00:42:06,550 function that we created earlier. 891 00:42:06,550 --> 00:42:10,810 In this case, if keyboard is down left, direction is left as before, 892 00:42:10,810 --> 00:42:14,290 but we're going to see his delta x to negative 80. 893 00:42:14,290 --> 00:42:17,440 And this is some value that I found was a good speed for him to move. 894 00:42:17,440 --> 00:42:21,040 And then same thing for keyboard is right, we'll say his delta to eight. 895 00:42:21,040 --> 00:42:21,791 Yes? 896 00:42:21,791 --> 00:42:27,090 AUDIENCE: Do you need the delta x on this [INAUDIBLE].. 897 00:42:27,090 --> 00:42:29,520 COLTON OGDEN: You don't, no. 898 00:42:29,520 --> 00:42:32,290 Because actually, if I go back-- 899 00:42:32,290 --> 00:42:35,040 that one was because we were just moving the camera. 900 00:42:35,040 --> 00:42:38,820 I believe we've changed it now so that the-- 901 00:42:38,820 --> 00:42:43,110 902 00:42:43,110 --> 00:42:47,285 in map, I believe cam x now gets the Mario's current position. 903 00:42:47,285 --> 00:42:51,050 904 00:42:51,050 --> 00:42:55,141 Yeah, so here we have our update function has changed such that-- 905 00:42:55,141 --> 00:42:57,640 and I apologize for skimming over some of these details just 906 00:42:57,640 --> 00:42:59,440 for the sake of time. 907 00:42:59,440 --> 00:43:03,880 But cam x now is actually going to get self.player x. 908 00:43:03,880 --> 00:43:05,960 It's going to get the cent-- 909 00:43:05,960 --> 00:43:11,800 math.min, it's going to basically ensure that we don't center on him such 910 00:43:11,800 --> 00:43:13,240 that we get clipped out of bounds. 911 00:43:13,240 --> 00:43:16,750 That's what the math.min are taking into consideration. 912 00:43:16,750 --> 00:43:20,320 But now it's a function not of some constant 62 value, 913 00:43:20,320 --> 00:43:24,770 but it's actually his x and y value. 914 00:43:24,770 --> 00:43:30,910 If we go back to player y, that's all that we really needed to change. 915 00:43:30,910 --> 00:43:33,640 Now because we have the camera following him, 916 00:43:33,640 --> 00:43:37,240 our delta x and delta y are changing based on input. 917 00:43:37,240 --> 00:43:41,050 We have the result of him simply moving. 918 00:43:41,050 --> 00:43:43,890 919 00:43:43,890 --> 00:43:47,050 The functions are in place such that-- notice that even moving to the left 920 00:43:47,050 --> 00:43:50,384 here, the camera is not going any farther than this 921 00:43:50,384 --> 00:43:53,300 because this would just all be blackness, and that's not what we want. 922 00:43:53,300 --> 00:43:56,174 We want to confine it, and that's the way that the actual game works. 923 00:43:56,174 --> 00:44:00,290 We want to make sure that we confine the camera always to what's visible. 924 00:44:00,290 --> 00:44:04,325 And so you just need to put constraints on where your camera can look. 925 00:44:04,325 --> 00:44:06,700 All right, any more questions on how we got that working? 926 00:44:06,700 --> 00:44:08,658 AUDIENCE: Sorry, the constraints on the camera, 927 00:44:08,658 --> 00:44:11,440 that's something you [INAUDIBLE]. 928 00:44:11,440 --> 00:44:22,860 COLTON OGDEN: So if we go to map.lua right here, camera x is going to-- 929 00:44:22,860 --> 00:44:26,520 this bit of code is going to ensure that we don't go. 930 00:44:26,520 --> 00:44:31,920 It's going to return the greater of 0 so that we don't go any farther left less 931 00:44:31,920 --> 00:44:38,280 than 0 or whatever this value here, which is the player's coordinates. 932 00:44:38,280 --> 00:44:43,700 And also math.min so that we don't go past the right edge as well. 933 00:44:43,700 --> 00:44:45,540 AUDIENCE: So that is your logic? 934 00:44:45,540 --> 00:44:46,782 COLTON OGDEN: Yes, yes. 935 00:44:46,782 --> 00:44:47,490 That's the logic. 936 00:44:47,490 --> 00:44:52,590 This bit of code simultaneously tracks Mario and prevents the camera 937 00:44:52,590 --> 00:44:54,820 from clipping out of bounds. 938 00:44:54,820 --> 00:44:58,580 OK, any further questions? 939 00:44:58,580 --> 00:45:00,740 All right. 940 00:45:00,740 --> 00:45:03,317 So he's not animating so that's a core feature 941 00:45:03,317 --> 00:45:05,900 and that's something that will make it look nice and polished. 942 00:45:05,900 --> 00:45:08,108 So we're going to start implementing basic animation. 943 00:45:08,108 --> 00:45:12,470 And to do this, we need to start talking about state. 944 00:45:12,470 --> 00:45:14,630 State machines, in general, are a very clean way 945 00:45:14,630 --> 00:45:19,550 of modeling transitions for almost anything in your game, 946 00:45:19,550 --> 00:45:23,480 but typically for entities like characters and enemies. 947 00:45:23,480 --> 00:45:26,030 We can see here these boxes are all separate states. 948 00:45:26,030 --> 00:45:28,040 So standing, for example, that's a state. 949 00:45:28,040 --> 00:45:30,560 Jumping is a state, ducking is a state, diving is a state. 950 00:45:30,560 --> 00:45:34,550 This isn't exclusive to Mario, this is some other example, but the gist of it 951 00:45:34,550 --> 00:45:36,500 remains true. 952 00:45:36,500 --> 00:45:39,930 If we're standing, these arrows model the sort 953 00:45:39,930 --> 00:45:41,930 of transitions between states and the conditions 954 00:45:41,930 --> 00:45:43,430 that satisfy those transitions. 955 00:45:43,430 --> 00:45:47,460 If we're standing, and we press B, we should be in the jumping state. 956 00:45:47,460 --> 00:45:50,240 And so if we're jumping, and we press down, in this example, 957 00:45:50,240 --> 00:45:53,464 we should go into what they determined to be the diving state. 958 00:45:53,464 --> 00:45:55,880 If we're standing in the press down, we should be ducking. 959 00:45:55,880 --> 00:45:58,970 And if we're ducking and release down, we should be standing. 960 00:45:58,970 --> 00:46:02,810 And this actually ties into our behaviors table that we saw earlier, 961 00:46:02,810 --> 00:46:04,120 and I'll show you right now. 962 00:46:04,120 --> 00:46:10,070 So if we go to mario-7, and we run mario-7. 963 00:46:10,070 --> 00:46:13,580 And we move him, we now see that he's got his running animation. 964 00:46:13,580 --> 00:46:17,152 And not only that, but if we let go of left or right, 965 00:46:17,152 --> 00:46:18,610 he goes back to his idle animation. 966 00:46:18,610 --> 00:46:20,568 Notice that it's a separate frame of animation. 967 00:46:20,568 --> 00:46:22,820 We actually have two states in use now. 968 00:46:22,820 --> 00:46:35,050 If we go to mario-7's code, what we fundamentally changed here is 969 00:46:35,050 --> 00:46:37,090 our behavior table now has two states-- 970 00:46:37,090 --> 00:46:39,730 idle and walking. 971 00:46:39,730 --> 00:46:42,730 And ideally, you would probably engineer your code in such a way 972 00:46:42,730 --> 00:46:45,340 that you separate out your states [INAUDIBLE] different files. 973 00:46:45,340 --> 00:46:46,940 It depends on the length of your code. 974 00:46:46,940 --> 00:46:49,648 But for the sake of being able to show how we've built things up, 975 00:46:49,648 --> 00:46:51,567 I'm sort of putting everything into one file. 976 00:46:51,567 --> 00:46:54,900 But just know that there are cleaner and better ways to engineer code like this. 977 00:46:54,900 --> 00:46:57,640 978 00:46:57,640 --> 00:46:59,410 Idle is changed now. 979 00:46:59,410 --> 00:47:02,147 So if you're idle and you press left, for example. 980 00:47:02,147 --> 00:47:03,730 You want to start walking to the left. 981 00:47:03,730 --> 00:47:05,646 Direction gets set left, we know that already. 982 00:47:05,646 --> 00:47:08,650 Delta x gets set to negative, and we've separated-- instead of, 983 00:47:08,650 --> 00:47:11,640 having a hard coded value, we've now made it a constant walking speed. 984 00:47:11,640 --> 00:47:13,090 So we can change it. 985 00:47:13,090 --> 00:47:14,530 Delta x gets that value. 986 00:47:14,530 --> 00:47:16,270 State now needs to change. 987 00:47:16,270 --> 00:47:18,670 We're no longer in the idle state, we're now walking. 988 00:47:18,670 --> 00:47:20,750 So set the state to walking. 989 00:47:20,750 --> 00:47:23,980 And then we have now a set of animations. 990 00:47:23,980 --> 00:47:26,440 So an animation-- if you look at animation.lua, 991 00:47:26,440 --> 00:47:29,170 it's a new file we've created. 992 00:47:29,170 --> 00:47:31,960 I won't go into too much detail, but conceptually 993 00:47:31,960 --> 00:47:35,080 what an animation is, is a table of frames-- 994 00:47:35,080 --> 00:47:37,570 all your quads for a given animation-- and the interval 995 00:47:37,570 --> 00:47:39,520 of time between those frames. 996 00:47:39,520 --> 00:47:41,860 You keep track of how much time has passed with a timer 997 00:47:41,860 --> 00:47:43,090 that you set to zero. 998 00:47:43,090 --> 00:47:47,500 And if every frame you add to that, if we've surpassed the interval, 999 00:47:47,500 --> 00:47:50,186 then we know that we need to go to the next frame of animation. 1000 00:47:50,186 --> 00:47:52,810 And then we need to reset our timer and just keep track of this 1001 00:47:52,810 --> 00:47:55,800 over and over again each frame. 1002 00:47:55,800 --> 00:47:57,130 And we do all of that here. 1003 00:47:57,130 --> 00:48:00,010 And it even has a logic in here as well to make sure 1004 00:48:00,010 --> 00:48:03,160 that if we, for example, enough time has passed between our last frame 1005 00:48:03,160 --> 00:48:06,700 that two frames of animation have elapsed, we should take care of that. 1006 00:48:06,700 --> 00:48:09,070 And all that logic is contained therein. 1007 00:48:09,070 --> 00:48:12,170 I won't go into too much detail on how it works for the sake of time. 1008 00:48:12,170 --> 00:48:16,330 But this is fundamentally how we're getting Mario to animate. 1009 00:48:16,330 --> 00:48:20,440 We're giving him an animation where we're assigning a name to an animation, 1010 00:48:20,440 --> 00:48:23,080 giving it a set of frames in that sprite sheet, 1011 00:48:23,080 --> 00:48:25,754 and a time interval that elapses between those frames, 1012 00:48:25,754 --> 00:48:26,920 and then just keeping track. 1013 00:48:26,920 --> 00:48:27,961 How much time has passed? 1014 00:48:27,961 --> 00:48:30,760 Has 0.05 seconds elapsed? 1015 00:48:30,760 --> 00:48:34,075 If so, go to the next frame, reset your timer and rinse and repeat. 1016 00:48:34,075 --> 00:48:35,950 And also make sure loop back to the beginning 1017 00:48:35,950 --> 00:48:39,240 of that because there's only four or five frames of animation, 1018 00:48:39,240 --> 00:48:40,990 you need to constantly cycle through that. 1019 00:48:40,990 --> 00:48:44,170 And that's what gives him the appearance of walking. 1020 00:48:44,170 --> 00:48:46,640 We can see here in our animations table. 1021 00:48:46,640 --> 00:48:47,900 This is a new table. 1022 00:48:47,900 --> 00:48:52,450 We have idle, which is animation create gets a texture and then 1023 00:48:52,450 --> 00:48:53,689 a set of frames. 1024 00:48:53,689 --> 00:48:56,230 Recall these are just quads because the texture needs to know 1025 00:48:56,230 --> 00:48:59,200 what quads to render to the screen. 1026 00:48:59,200 --> 00:49:01,660 Idle's just one frame, that's him standing still. 1027 00:49:01,660 --> 00:49:03,380 Walking is four frames. 1028 00:49:03,380 --> 00:49:06,482 And these are just the particular offsets 1029 00:49:06,482 --> 00:49:08,440 because the sprite sheet is a little strange in 1030 00:49:08,440 --> 00:49:11,650 that Mario has different sizes depending on what frame is being rendered, 1031 00:49:11,650 --> 00:49:14,530 so I had to manually figure out what those numbers are. 1032 00:49:14,530 --> 00:49:16,670 Fortunately, you only have to do it one time. 1033 00:49:16,670 --> 00:49:20,170 But notice that I also have interval gets 0.07 seconds, 1034 00:49:20,170 --> 00:49:22,600 and this happens to be the length of time between each 1035 00:49:22,600 --> 00:49:24,433 of Mario's running frames that I found makes 1036 00:49:24,433 --> 00:49:29,080 it look like he's running close to the original game. 1037 00:49:29,080 --> 00:49:34,240 And we need to make sure that animation gets set to animations index idle 1038 00:49:34,240 --> 00:49:36,790 and that we get the current frame from that animation. 1039 00:49:36,790 --> 00:49:40,510 And every time we render, we want to ask whatever his current animation is 1040 00:49:40,510 --> 00:49:41,470 for its current frame. 1041 00:49:41,470 --> 00:49:44,440 And that gets stored in the animation class that it should be before. 1042 00:49:44,440 --> 00:49:51,280 So if we're back to our behaviors table, when a state gets changed to walking, 1043 00:49:51,280 --> 00:49:54,520 the actual walking animation needs to restart. 1044 00:49:54,520 --> 00:49:57,700 It doesn't need to be caught in the middle because if we stop him walking 1045 00:49:57,700 --> 00:49:58,533 and go back to idle. 1046 00:49:58,533 --> 00:50:00,700 And then we restart him walking at a weird frame, 1047 00:50:00,700 --> 00:50:03,700 it might not be clear in this example, but for certain animations, 1048 00:50:03,700 --> 00:50:06,574 you want to restart the animation every time that state gets changed. 1049 00:50:06,574 --> 00:50:11,170 Especially if they like start by moving their arms up or something obvious. 1050 00:50:11,170 --> 00:50:15,650 And the opposite happens for the right direction, but it's same logic. 1051 00:50:15,650 --> 00:50:19,360 Notice that we have his walking statement here. 1052 00:50:19,360 --> 00:50:23,020 If we're pressing to the left, move right and down. 1053 00:50:23,020 --> 00:50:26,170 And then it's effectively the same thing-- 1054 00:50:26,170 --> 00:50:28,510 idle and walking-- that we've done before, 1055 00:50:28,510 --> 00:50:30,370 but now they're just separate states. 1056 00:50:30,370 --> 00:50:36,580 And given his state, he can just call this and update it really easily, 1057 00:50:36,580 --> 00:50:37,250 really cleanly. 1058 00:50:37,250 --> 00:50:39,910 You don't have to keep track of hey, is he walking? 1059 00:50:39,910 --> 00:50:41,080 If he is do this. 1060 00:50:41,080 --> 00:50:44,110 If Mario is doing this-- 1061 00:50:44,110 --> 00:50:46,300 the nice thing about states is let's say, 1062 00:50:46,300 --> 00:50:48,100 you are in a game where you have like a-- 1063 00:50:48,100 --> 00:50:50,380 let's say you're walking but you also have a weapon. 1064 00:50:50,380 --> 00:50:51,490 And the weapon has its own states. 1065 00:50:51,490 --> 00:50:52,350 Like, it can be firing. 1066 00:50:52,350 --> 00:50:53,100 It can be smoking. 1067 00:50:53,100 --> 00:50:55,540 I can be all this stuff. 1068 00:50:55,540 --> 00:50:58,600 You don't have to keep track of if player is jumping 1069 00:50:58,600 --> 00:51:00,116 and player just shot-- 1070 00:51:00,116 --> 00:51:02,740 you can just have two states, weapon state and character state. 1071 00:51:02,740 --> 00:51:05,300 And then just modify both those states simultaneously 1072 00:51:05,300 --> 00:51:10,660 and then that will simplify the whole process of updating your character. 1073 00:51:10,660 --> 00:51:12,730 So we were updating the animation in here 1074 00:51:12,730 --> 00:51:15,027 as well, because that needs to update the timer. 1075 00:51:15,027 --> 00:51:17,110 We need to keep track of how much time has passed, 1076 00:51:17,110 --> 00:51:18,730 so that we can add it to the timer. 1077 00:51:18,730 --> 00:51:21,279 And then if the interval has been surpassed then 1078 00:51:21,279 --> 00:51:23,320 go to next frame animation and then possibly loop 1079 00:51:23,320 --> 00:51:29,750 back, get the current frame and then manipulate his x value. 1080 00:51:29,750 --> 00:51:34,230 And then here we can see if we're just drawing self.currentframe. 1081 00:51:34,230 --> 00:51:37,690 Whatever his current animation is, it's always going to get that current frame. 1082 00:51:37,690 --> 00:51:40,270 So any questions on how this works? 1083 00:51:40,270 --> 00:51:43,710 1084 00:51:43,710 --> 00:51:45,520 OK. 1085 00:51:45,520 --> 00:51:48,580 So he can walk, he's got two states. 1086 00:51:48,580 --> 00:51:53,230 And those two states, we can swap in and out really easily and pretty cleanly. 1087 00:51:53,230 --> 00:51:56,470 Our next goal is what Mario's probably the most iconic thing to do 1088 00:51:56,470 --> 00:51:58,060 is which is jumping. 1089 00:51:58,060 --> 00:52:00,550 And so jumping-- up to this point, we've only 1090 00:52:00,550 --> 00:52:04,420 been manipulating our x-axis, left and right, for moving to the level. 1091 00:52:04,420 --> 00:52:08,230 But jumping relies on the y-axis being manipulated. 1092 00:52:08,230 --> 00:52:11,570 So to get someone to jump, you give him an initial y velocity 1093 00:52:11,570 --> 00:52:14,710 of something really high right like 400 or something. 1094 00:52:14,710 --> 00:52:17,631 He's in a ship into the air, but as soon as he starts jumping, 1095 00:52:17,631 --> 00:52:19,130 we don't want to keep going forever. 1096 00:52:19,130 --> 00:52:21,340 We need him to sort of come down. 1097 00:52:21,340 --> 00:52:25,090 So what we need to do is apply gravity to his y velocity. 1098 00:52:25,090 --> 00:52:28,000 We'll start his y velocity at 400, but we're 1099 00:52:28,000 --> 00:52:30,910 going to add a negative 400 to be specific 1100 00:52:30,910 --> 00:52:34,550 because recall y is negative going up. 1101 00:52:34,550 --> 00:52:38,410 If is y is negative 400, we need that to decrease 1102 00:52:38,410 --> 00:52:40,030 and eventually become positive. 1103 00:52:40,030 --> 00:52:43,700 So that he comes up and then eventually comes back down to the ground. 1104 00:52:43,700 --> 00:52:48,057 So we'll see how we do that in mario-8. 1105 00:52:48,057 --> 00:52:49,390 Let's go ahead and load that up. 1106 00:52:49,390 --> 00:52:51,694 1107 00:52:51,694 --> 00:52:54,610 So here we have the same thing that we saw before, we can move around, 1108 00:52:54,610 --> 00:52:56,520 but if I press spacebar-- 1109 00:52:56,520 --> 00:52:57,270 he jumps. 1110 00:52:57,270 --> 00:53:01,170 And notice also he's got another frame of animation which is as simple as just 1111 00:53:01,170 --> 00:53:06,180 one new animation object that we created earlier just with that one frame. 1112 00:53:06,180 --> 00:53:08,767 And when he's in that state, he can still move. 1113 00:53:08,767 --> 00:53:11,100 So when he's in the jumping state, we know OK, you still 1114 00:53:11,100 --> 00:53:12,305 need a test for input. 1115 00:53:12,305 --> 00:53:15,180 And notice, there's no collisions so I can still walk over the tiles, 1116 00:53:15,180 --> 00:53:17,597 but we'll be taking care of that very shortly. 1117 00:53:17,597 --> 00:53:20,680 But, I mean, this is starting to look a lot like Super Mario Bros., right? 1118 00:53:20,680 --> 00:53:24,660 Like, we've got most of the pieces here in place. 1119 00:53:24,660 --> 00:53:28,230 So we're going to go ahead and look at mario-8 now 1120 00:53:28,230 --> 00:53:30,330 to see how we implemented this. 1121 00:53:30,330 --> 00:53:32,970 So we have the idle state and the walking state. 1122 00:53:32,970 --> 00:53:35,640 These have both been modified because now when we're walking 1123 00:53:35,640 --> 00:53:38,100 and when we're idle, we want to be able to jump because you can jump out 1124 00:53:38,100 --> 00:53:39,280 of both those states. 1125 00:53:39,280 --> 00:53:44,305 So in the idle state, if we press the spacebar, his dy-- 1126 00:53:44,305 --> 00:53:47,430 now we're modifying his dy-- it's going to equal to negative jump velocity. 1127 00:53:47,430 --> 00:53:49,650 Jump velocity happens to be a value I set to 400. 1128 00:53:49,650 --> 00:53:57,660 So his y velocity should be set to negative 400. 1129 00:53:57,660 --> 00:54:01,290 In So here, we have delta y negative jump 1130 00:54:01,290 --> 00:54:03,320 velocity, set his state to jumping. 1131 00:54:03,320 --> 00:54:06,690 And this will have the effect of on the next frame jumping is going to update. 1132 00:54:06,690 --> 00:54:10,110 So it's going to go to this function instead of the other function 1133 00:54:10,110 --> 00:54:12,850 that was called which was walking. 1134 00:54:12,850 --> 00:54:14,850 We're going to set his state to jumping and then 1135 00:54:14,850 --> 00:54:17,490 we're going to set his animation to a jumping animation. 1136 00:54:17,490 --> 00:54:19,289 We've added a jumping animation here. 1137 00:54:19,289 --> 00:54:21,330 It's just one frame just like the idle animation. 1138 00:54:21,330 --> 00:54:26,820 Very simple and happens to be 88 pixels to the right at zero pixel 1139 00:54:26,820 --> 00:54:28,380 on the y-axis-- 1140 00:54:28,380 --> 00:54:31,590 same dimensions. 1141 00:54:31,590 --> 00:54:33,720 And we essentially do the same exact thing 1142 00:54:33,720 --> 00:54:38,330 when he's walking because the behavior is fundamentally the same. 1143 00:54:38,330 --> 00:54:40,659 Jumping-- we can see that we can still move here. 1144 00:54:40,659 --> 00:54:43,200 So when jumping gets updated, when we're in the jumping state 1145 00:54:43,200 --> 00:54:45,840 and this is being called, we can move left and right still, 1146 00:54:45,840 --> 00:54:48,570 which you can in the regular game. 1147 00:54:48,570 --> 00:54:53,310 And as delta x is set to-- it's just same as walking speed, hasn't changed. 1148 00:54:53,310 --> 00:54:57,000 This is the part that lets us actually prevent him 1149 00:54:57,000 --> 00:54:58,820 from shooting up into the sky forever. 1150 00:54:58,820 --> 00:55:03,540 This .dy gets this .dy plus this .map.gravity. 1151 00:55:03,540 --> 00:55:09,510 And if we go to map, we can see that we have a value gravity here 1152 00:55:09,510 --> 00:55:10,350 which is set to 15. 1153 00:55:10,350 --> 00:55:12,475 And this is just some value you can tweak depending 1154 00:55:12,475 --> 00:55:14,790 on how fast you want-- either the less this is, 1155 00:55:14,790 --> 00:55:16,350 the more it's going to feel like he's jumping on the moon. 1156 00:55:16,350 --> 00:55:19,590 Or the more it's going to feel like he's on some really dense place. 1157 00:55:19,590 --> 00:55:23,340 But 15 felt like the number that was very close to how 1158 00:55:23,340 --> 00:55:25,470 he jumps in the actual game. 1159 00:55:25,470 --> 00:55:28,890 And so this gets added to his dy. 1160 00:55:28,890 --> 00:55:31,110 And then this will be affect of he's going to go up, 1161 00:55:31,110 --> 00:55:32,820 but he's going to go up less and less, and eventually he's 1162 00:55:32,820 --> 00:55:33,840 going to come back down. 1163 00:55:33,840 --> 00:55:36,461 And then when it comes back down, and he gets roughly 1164 00:55:36,461 --> 00:55:38,460 to the middle, which is what this takes care of. 1165 00:55:38,460 --> 00:55:42,885 So if his y value is about-- this is our very primitive collision detection. 1166 00:55:42,885 --> 00:55:44,760 It's not collision detection, but it makes it 1167 00:55:44,760 --> 00:55:47,370 seem as if we have collision detection. 1168 00:55:47,370 --> 00:55:53,270 If it's equal to about roughly halfway down the map minus Mario's height, 1169 00:55:53,270 --> 00:55:59,640 set his dy to zero, make him idle, and then set his animation to idle. 1170 00:55:59,640 --> 00:56:02,730 So that's all we essentially need to get him to jump. 1171 00:56:02,730 --> 00:56:06,420 So just gravity, y velocity, and just modify the states 1172 00:56:06,420 --> 00:56:09,150 that we already have to get it to work. 1173 00:56:09,150 --> 00:56:13,759 And notice that we don't have to mess with a bunch of if statements and messy 1174 00:56:13,759 --> 00:56:15,050 conditions to get this to work. 1175 00:56:15,050 --> 00:56:17,970 We just have discrete blocks of logic that 1176 00:56:17,970 --> 00:56:20,580 work as a very simple state machine. 1177 00:56:20,580 --> 00:56:22,500 So any questions on how this work? 1178 00:56:22,500 --> 00:56:28,460 1179 00:56:28,460 --> 00:56:30,230 So our next goal-- 1180 00:56:30,230 --> 00:56:32,944 he hasn't been able to interact with any of the environment yet. 1181 00:56:32,944 --> 00:56:35,360 No collision detection, but this is our first introduction 1182 00:56:35,360 --> 00:56:37,190 to collision detection here. 1183 00:56:37,190 --> 00:56:40,730 And to get this to work, what we need to do is using our map, 1184 00:56:40,730 --> 00:56:44,030 we need to check the pixels of where Mario's head is 1185 00:56:44,030 --> 00:56:46,250 at any point in which he's jumping and say 1186 00:56:46,250 --> 00:56:49,910 is there a tile there that is collidable that we should hit. 1187 00:56:49,910 --> 00:56:55,070 If so, set y velocity to instantly be zero. 1188 00:56:55,070 --> 00:56:59,210 Gravity will then every frame add to that and make him go down. 1189 00:56:59,210 --> 00:57:01,370 And maybe trigger some sort of visual feedback, 1190 00:57:01,370 --> 00:57:03,800 so we know that we hit the tile. 1191 00:57:03,800 --> 00:57:06,560 And that's all we really have to do. 1192 00:57:06,560 --> 00:57:08,600 And then another thing that we have to do 1193 00:57:08,600 --> 00:57:11,960 is Mario can be potentially between two bricks at once. 1194 00:57:11,960 --> 00:57:14,780 In that case, we need to check not only the brick that 1195 00:57:14,780 --> 00:57:18,050 happens to be to the left of him, but also the brick to the right of him 1196 00:57:18,050 --> 00:57:21,260 so that they both get triggered, which is how it works in the original game. 1197 00:57:21,260 --> 00:57:23,570 And all we need to do effectively is look for whatever 1198 00:57:23,570 --> 00:57:28,250 brick exists at his origin point, his top left point, and the brick 1199 00:57:28,250 --> 00:57:29,780 to the right based on his width. 1200 00:57:29,780 --> 00:57:32,250 So 16 pixels to the right. 1201 00:57:32,250 --> 00:57:35,630 So let's go ahead look at mario-9 and see what this looks like. 1202 00:57:35,630 --> 00:57:40,690 See if I can generate a level that has two bricks right next to each other 1203 00:57:40,690 --> 00:57:42,194 before we-- 1204 00:57:42,194 --> 00:57:42,860 well, maybe not. 1205 00:57:42,860 --> 00:57:49,370 But if I do that, we get some very basic collision detection. 1206 00:57:49,370 --> 00:57:52,940 All we're doing is saying when were jumping is there a brick 1207 00:57:52,940 --> 00:57:55,340 right where our head is? 1208 00:57:55,340 --> 00:58:01,790 If there is then gravity gets zero, and also manipulate the-- 1209 00:58:01,790 --> 00:58:07,130 in this case, we're also changing this tile in our map which is maybe, 1210 00:58:07,130 --> 00:58:08,150 I think, it's 26. 1211 00:58:08,150 --> 00:58:11,570 The ID for the yellow one, set that to like 27. 1212 00:58:11,570 --> 00:58:14,499 And then next time the map gets rendered, 27 or whatever value 1213 00:58:14,499 --> 00:58:16,040 it is happens to be the dark version. 1214 00:58:16,040 --> 00:58:18,110 And that gives us the appearance of having 1215 00:58:18,110 --> 00:58:20,540 modified our game map in some way. 1216 00:58:20,540 --> 00:58:23,870 So we can go ahead, that's conceptually how it works. 1217 00:58:23,870 --> 00:58:28,960 If you look at mario-9, we can see-- 1218 00:58:28,960 --> 00:58:32,030 and this is where it's necessary to have a reference to map here, 1219 00:58:32,030 --> 00:58:33,821 which is why we've had this the whole time. 1220 00:58:33,821 --> 00:58:36,827 Now, we actually need to look in our map based on our input 1221 00:58:36,827 --> 00:58:39,410 and see what we're doing to the game map and see whether we're 1222 00:58:39,410 --> 00:58:41,450 colliding with a given tile. 1223 00:58:41,450 --> 00:58:45,080 So what's changed is in our update function, 1224 00:58:45,080 --> 00:58:48,506 I haven't added this as a style decision. 1225 00:58:48,506 --> 00:58:50,130 I didn't add this to any of the states. 1226 00:58:50,130 --> 00:58:51,380 It's in his update phase. 1227 00:58:51,380 --> 00:58:56,420 It could be in his jumping state, and that's up to you. 1228 00:58:56,420 --> 00:58:58,940 But just for the sake of separating it out for now 1229 00:58:58,940 --> 00:59:02,840 so we can see it on its own, it's an update function. 1230 00:59:02,840 --> 00:59:07,310 If his delta y is less than zero, which means he's going up, 1231 00:59:07,310 --> 00:59:11,251 then we're going to use a function called tile at his x and y values. 1232 00:59:11,251 --> 00:59:12,500 So it's going be his top left. 1233 00:59:12,500 --> 00:59:15,500 If it's not equal to empty, we've hit something. 1234 00:59:15,500 --> 00:59:20,060 1235 00:59:20,060 --> 00:59:24,390 That's his origin, but also, his width minus 1 plus his width minus 1. 1236 00:59:24,390 --> 00:59:28,400 So both tiles right above him instantly set his dy to zero. 1237 00:59:28,400 --> 00:59:31,640 So now he's going to freeze in the air and then gravity is going to apply 1238 00:59:31,640 --> 00:59:34,460 and he's going to fall. 1239 00:59:34,460 --> 00:59:37,160 We're going to just change that block to a different block. 1240 00:59:37,160 --> 00:59:41,720 So what we're doing is we're saying, hey, if the tile at (x, y) 1241 00:59:41,720 --> 00:59:46,190 is equal to that tile question, which is like 26 or something like that, 1242 00:59:46,190 --> 00:59:49,620 then we're going to set that tile to tile question dark. 1243 00:59:49,620 --> 00:59:52,370 And then we don't need to worry about refresh sprite batch, that's 1244 00:59:52,370 --> 00:59:54,950 just a performance consideration. 1245 00:59:54,950 --> 00:59:58,724 If self.map tile at x and this is the same thing, but for the tile 1246 00:59:58,724 --> 01:00:00,140 that's to the right and above him. 1247 01:00:00,140 --> 01:00:02,210 We're going to set that to tile question dark. 1248 01:00:02,210 --> 01:00:04,760 And this has the effect of it still will make 1249 01:00:04,760 --> 01:00:07,130 his dy set to zero even if he hits a tile that's 1250 01:00:07,130 --> 01:00:11,370 not that question that yellow question mark. 1251 01:00:11,370 --> 01:00:15,290 But he'll hit it, but it won't change it. 1252 01:00:15,290 --> 01:00:18,020 If, for example, he jumps and hits the brown one, 1253 01:00:18,020 --> 01:00:20,150 it still collides, but it doesn't actually 1254 01:00:20,150 --> 01:00:22,207 perform any sort of changes on the game map. 1255 01:00:22,207 --> 01:00:23,540 And that will just set the tile. 1256 01:00:23,540 --> 01:00:25,970 And then map if we go to tile at-- 1257 01:00:25,970 --> 01:00:28,790 tile at is a very simple function. 1258 01:00:28,790 --> 01:00:35,000 All it does effectively is just divide the x-coordinate that we pass in by 16 1259 01:00:35,000 --> 01:00:36,210 and add one to it. 1260 01:00:36,210 --> 01:00:42,180 And then the same thing for y divided by [INAUDIBLE] and add one to it. 1261 01:00:42,180 --> 01:00:44,870 So very simple. 1262 01:00:44,870 --> 01:00:49,440 And that has the effect of let's say we're at 48 or whatever pixels wide, 1263 01:00:49,440 --> 01:00:54,200 and we divide that by 16, it'll be 3 because it floors it. 1264 01:00:54,200 --> 01:01:00,440 All right, and we add one because Lua is one indexed 1265 01:01:00,440 --> 01:01:03,450 not zero indexed with its tables. 1266 01:01:03,450 --> 01:01:03,950 So 1267 01:01:03,950 --> 01:01:07,147 AUDIENCE: Doesn't that mean you could hit a cloud? 1268 01:01:07,147 --> 01:01:08,480 COLTON OGDEN: In this case, yes. 1269 01:01:08,480 --> 01:01:09,240 I think it could. 1270 01:01:09,240 --> 01:01:12,170 But in the next example, it gets-- actually, 1271 01:01:12,170 --> 01:01:17,150 I don't think so because I believe the way that I have set the generation, 1272 01:01:17,150 --> 01:01:19,670 the clouds all spawn of Mario's max jump height. 1273 01:01:19,670 --> 01:01:23,660 But we do get rid of that problem because in the next example, 1274 01:01:23,660 --> 01:01:25,670 we'll see we actually specify a table of tiles 1275 01:01:25,670 --> 01:01:27,290 that we can collide with explicitly. 1276 01:01:27,290 --> 01:01:30,320 Because we need to start getting into pipes and the ground and stuff 1277 01:01:30,320 --> 01:01:31,606 like that so. 1278 01:01:31,606 --> 01:01:35,420 But yeah, theoretically if my generation algorithm were messed up, 1279 01:01:35,420 --> 01:01:37,130 we could be hitting clouds right now. 1280 01:01:37,130 --> 01:01:42,324 But step by step, we build up towards our ideal implementation. 1281 01:01:42,324 --> 01:01:43,240 Any further questions? 1282 01:01:43,240 --> 01:01:43,890 Yeah. 1283 01:01:43,890 --> 01:01:53,829 AUDIENCE: [INAUDIBLE] 1284 01:01:53,829 --> 01:01:55,120 COLTON OGDEN: What do you mean? 1285 01:01:55,120 --> 01:02:05,300 AUDIENCE: Like when he's jumping [INAUDIBLE] is there [INAUDIBLE] 1286 01:02:05,300 --> 01:02:08,710 COLTON OGDEN: I believe I have it set so that it's one pixel because in the game 1287 01:02:08,710 --> 01:02:14,080 I was noticing when running it that you can see his fist go above the tiles. 1288 01:02:14,080 --> 01:02:16,090 Notice that you can see his fist hit it. 1289 01:02:16,090 --> 01:02:22,240 So I have it set so like I think it's one or two pixels 1290 01:02:22,240 --> 01:02:24,820 the max of his bounding box. 1291 01:02:24,820 --> 01:02:26,797 But you do it to taste. 1292 01:02:26,797 --> 01:02:27,880 Do it right at the number. 1293 01:02:27,880 --> 01:02:29,904 Do it at one or two pixels, it's up to you. 1294 01:02:29,904 --> 01:02:33,070 But that's just the way that they ended up designing it in the original game 1295 01:02:33,070 --> 01:02:35,340 so I did it like that too. 1296 01:02:35,340 --> 01:02:39,380 But conceptually, you do it at the limits of whatever you want, 1297 01:02:39,380 --> 01:02:41,450 and then you tweak it as you want. 1298 01:02:41,450 --> 01:02:45,880 So any more questions? 1299 01:02:45,880 --> 01:02:47,590 All right, goal 10. 1300 01:02:47,590 --> 01:02:50,980 Now, we need to implement as I alluded to just a moment ago collision 1301 01:02:50,980 --> 01:02:52,480 two other tiles so now. 1302 01:02:52,480 --> 01:02:54,550 We can hit blocks but when we're walking, 1303 01:02:54,550 --> 01:02:56,466 we're not actually testing the ground below us 1304 01:02:56,466 --> 01:02:58,900 we're just walking and setting the x-axis 1305 01:02:58,900 --> 01:03:01,390 and not performing any collision below us. 1306 01:03:01,390 --> 01:03:04,150 We want to do that so we can fall through gaps, 1307 01:03:04,150 --> 01:03:06,670 but we also want to prevent him from walking into pipes. 1308 01:03:06,670 --> 01:03:10,310 And so that he can also walk on top of blocks and other things like that. 1309 01:03:10,310 --> 01:03:12,800 So we're going to go ahead and look at mario-10. 1310 01:03:12,800 --> 01:03:14,091 We'll see what this looks like. 1311 01:03:14,091 --> 01:03:17,484 1312 01:03:17,484 --> 01:03:20,150 So he happened to spawn in the middle of a pipe, so bad example. 1313 01:03:20,150 --> 01:03:21,290 [LAUGHTER] 1314 01:03:21,290 --> 01:03:23,354 All right. 1315 01:03:23,354 --> 01:03:25,770 Whoops, and my jumping is not ideal. 1316 01:03:25,770 --> 01:03:28,490 But see now he can go on top of blocks like that. 1317 01:03:28,490 --> 01:03:29,939 I can fall as you saw earlier. 1318 01:03:29,939 --> 01:03:30,980 I'll do it one more time. 1319 01:03:30,980 --> 01:03:32,389 You can fall down. 1320 01:03:32,389 --> 01:03:34,180 And I don't have it set so you can restart. 1321 01:03:34,180 --> 01:03:36,500 And you can also not walk through pipes anymore, 1322 01:03:36,500 --> 01:03:37,833 and you can jump on top of them. 1323 01:03:37,833 --> 01:03:41,840 1324 01:03:41,840 --> 01:03:46,780 So now we sort of have the bare bone requirements 1325 01:03:46,780 --> 01:03:50,890 of what it means like walk through Super Mario Bros. world. 1326 01:03:50,890 --> 01:03:54,130 And we're actually almost done for what we have time for today. 1327 01:03:54,130 --> 01:03:56,110 I have a couple of one more example after this, 1328 01:03:56,110 --> 01:03:59,420 but we'll look at mario-10 just to see how we implemented this. 1329 01:03:59,420 --> 01:04:04,290 And as I alluded to earlier, I believe it is in player here. 1330 01:04:04,290 --> 01:04:14,687 1331 01:04:14,687 --> 01:04:16,520 Oh no, it's in the map coll-- right, there's 1332 01:04:16,520 --> 01:04:18,010 a collides function that I added. 1333 01:04:18,010 --> 01:04:20,560 So this map collides, it takes in the tile, 1334 01:04:20,560 --> 01:04:23,710 and we're going to be running this function for-- when we move left 1335 01:04:23,710 --> 01:04:26,900 and right we're going to be looking at the two tiles at Mario's height, 1336 01:04:26,900 --> 01:04:28,060 left and right. 1337 01:04:28,060 --> 01:04:29,290 It takes in a tile. 1338 01:04:29,290 --> 01:04:31,360 We have this table here called collidables. 1339 01:04:31,360 --> 01:04:34,840 And this just contains all the tiles that we want to check for collision on. 1340 01:04:34,840 --> 01:04:40,000 So any bricks, question mark blocks, pipes, that's fundamentally all we 1341 01:04:40,000 --> 01:04:40,784 need to look for. 1342 01:04:40,784 --> 01:04:42,700 There's no clouds in there and then because of 1343 01:04:42,700 --> 01:04:45,701 that if we happen to intersect with a cloud, he's not going to stop. 1344 01:04:45,701 --> 01:04:47,950 He's just going to go right through it because collide 1345 01:04:47,950 --> 01:04:49,210 is going to return false. 1346 01:04:49,210 --> 01:04:50,560 Because using this function here, we're going 1347 01:04:50,560 --> 01:04:52,360 to iterate through that collidables table. 1348 01:04:52,360 --> 01:04:54,026 It's going to look through all of those. 1349 01:04:54,026 --> 01:04:57,590 If it happens to match with that tile, return true. 1350 01:04:57,590 --> 01:04:59,900 Otherwise, we return false. 1351 01:04:59,900 --> 01:05:04,690 And we'll see this if we go to our code over here. 1352 01:05:04,690 --> 01:05:07,210 1353 01:05:07,210 --> 01:05:10,090 This is part of our previous jump code, but we 1354 01:05:10,090 --> 01:05:16,150 see if map collides with whatever tile is [INAUDIBLE] above us, 1355 01:05:16,150 --> 01:05:19,820 we've replaced our previous code with this collides function. 1356 01:05:19,820 --> 01:05:25,690 And then in our actual state machine code up here, 1357 01:05:25,690 --> 01:05:27,700 when we're walking for example. 1358 01:05:27,700 --> 01:05:31,300 We need to do all of our regular processing as before. 1359 01:05:31,300 --> 01:05:32,920 If we press space, start jumping. 1360 01:05:32,920 --> 01:05:35,878 And if we move left and right, set our direction and our walking speed. 1361 01:05:35,878 --> 01:05:41,040 But we also need to check right collision and check left collision. 1362 01:05:41,040 --> 01:05:43,420 And we can probably get rid of check right collision. 1363 01:05:43,420 --> 01:05:46,630 If we're moving to the right or to the left, 1364 01:05:46,630 --> 01:05:48,790 we can probably just put it up above here. 1365 01:05:48,790 --> 01:05:52,720 But the functions work as such; if we go down 1366 01:05:52,720 --> 01:05:57,910 to check left collision, if our delta x is less than zero-- 1367 01:05:57,910 --> 01:06:00,550 if we're testing that map collides function, 1368 01:06:00,550 --> 01:06:04,110 we're basically looking at the tile to our left and our y values. 1369 01:06:04,110 --> 01:06:08,134 So at our head level and at our bottom level so we're Mario's torso is. 1370 01:06:08,134 --> 01:06:11,050 Because he's two tiles tall, he needs to check both tiles to his left. 1371 01:06:11,050 --> 01:06:13,716 There could be, theoretically, a block that's here but not here. 1372 01:06:13,716 --> 01:06:15,575 Even though that doesn't normally occur. 1373 01:06:15,575 --> 01:06:17,950 But we're going to check to see if there is a tile there. 1374 01:06:17,950 --> 01:06:20,770 And if so, delta x needs to get set to zero. 1375 01:06:20,770 --> 01:06:23,980 And then we're going to basically look to see how many tiles he 1376 01:06:23,980 --> 01:06:28,895 would have moved to the left and then push him back that number of pixels. 1377 01:06:28,895 --> 01:06:32,620 However many pixels he would have moved in that direction, move him back. 1378 01:06:32,620 --> 01:06:34,900 And same thing with check right-- 1379 01:06:34,900 --> 01:06:40,050 if his delta x is greater than 0, then he needs to instantly get that set-- 1380 01:06:40,050 --> 01:06:42,490 Or if his delta x is greater than 0, and he's 1381 01:06:42,490 --> 01:06:47,270 got two tiles, or one or two tiles, or one or the other, 1382 01:06:47,270 --> 01:06:49,590 do the same thing but to the right. 1383 01:06:49,590 --> 01:06:52,090 You're going to move him to the left instead of to the right 1384 01:06:52,090 --> 01:06:52,870 if that makes sense. 1385 01:06:52,870 --> 01:06:55,240 So you're shifting him based on the number of pixels that he 1386 01:06:55,240 --> 01:06:56,770 would have moved in that direction. 1387 01:06:56,770 --> 01:07:02,140 And that has the effect of allowing him to not collide with the map. 1388 01:07:02,140 --> 01:07:04,660 And we'll look at it one more time. 1389 01:07:04,660 --> 01:07:05,870 Oh, this is perfect. 1390 01:07:05,870 --> 01:07:08,060 This is what I was talking about earlier. 1391 01:07:08,060 --> 01:07:11,264 If there are two blocks, recall we need to trigger both of them. 1392 01:07:11,264 --> 01:07:13,180 So if we hit spacebar underneath both of them, 1393 01:07:13,180 --> 01:07:16,310 they both get activated instead of just one. 1394 01:07:16,310 --> 01:07:19,560 And we can fall to the ground. 1395 01:07:19,560 --> 01:07:23,440 It's effectively pushing him however many pixels 1396 01:07:23,440 --> 01:07:27,510 he would have moved in that direction to the right. 1397 01:07:27,510 --> 01:07:31,510 And then we can jump on top of blocks as well. 1398 01:07:31,510 --> 01:07:36,370 So that's effectively it when he's moving left and right. 1399 01:07:36,370 --> 01:07:39,970 And if he's falling that's another important consideration. 1400 01:07:39,970 --> 01:07:45,820 This bit of code here gets applied after our velocity gets changed. 1401 01:07:45,820 --> 01:07:49,024 If both tiles below-- 1402 01:07:49,024 --> 01:07:51,190 because he could be theoretically between two tiles, 1403 01:07:51,190 --> 01:07:56,020 so we need to check the tile below him and also the tile to the right. 1404 01:07:56,020 --> 01:07:59,950 If they collide with him then set the dy to zero, 1405 01:07:59,950 --> 01:08:03,130 so that his velocity now is at zero. 1406 01:08:03,130 --> 01:08:07,450 State to idle, animation to idle, and then basically tweak 1407 01:08:07,450 --> 01:08:12,850 his value so that he's not-- 1408 01:08:12,850 --> 01:08:15,760 set his y value back the number of pixels 1409 01:08:15,760 --> 01:08:18,236 he would have gone past whatever tile he collided with. 1410 01:08:18,236 --> 01:08:20,319 Because based on his velocity, we could update him 1411 01:08:20,319 --> 01:08:23,920 he could be like seven pixels below a tile in the map, 1412 01:08:23,920 --> 01:08:28,180 we want him back up 7 pixels that he's even with the tile grid 1413 01:08:28,180 --> 01:08:29,540 if that makes sense. 1414 01:08:29,540 --> 01:08:32,979 So any questions on how that works? 1415 01:08:32,979 --> 01:08:35,380 Sorry, that was loud. 1416 01:08:35,380 --> 01:08:39,430 Last goal and this was probably the easiest step of the presentation. 1417 01:08:39,430 --> 01:08:44,170 We're going to add just some basic sounds just to make our application 1418 01:08:44,170 --> 01:08:45,520 look all the more polished. 1419 01:08:45,520 --> 01:08:48,100 So with this, we're going to use a couple of functions. 1420 01:08:48,100 --> 01:08:51,399 We're going to use the function called love.audio.newSource, which just takes 1421 01:08:51,399 --> 01:08:53,950 in a path to a particular audio source. 1422 01:08:53,950 --> 01:08:59,350 In this case sounds/samplse.mp3, and we can pass the key static. 1423 01:08:59,350 --> 01:09:03,279 And that just means if we pass in static, store that sound in memory 1424 01:09:03,279 --> 01:09:05,210 constantly, don't stream it from disk. 1425 01:09:05,210 --> 01:09:07,479 And this is perfect for small sound files 1426 01:09:07,479 --> 01:09:11,710 because depending on your computer that may or may not trigger a lag spike. 1427 01:09:11,710 --> 01:09:15,220 And then we'll use the function play on a given sound object 1428 01:09:15,220 --> 01:09:17,080 and that will just start playing the audio. 1429 01:09:17,080 --> 01:09:20,830 And we'll see in a second, we can set a call function 1430 01:09:20,830 --> 01:09:22,279 called set looping to true. 1431 01:09:22,279 --> 01:09:24,040 So, for example, for music, we don't want the music 1432 01:09:24,040 --> 01:09:25,080 to stop after it plays once. 1433 01:09:25,080 --> 01:09:26,740 We want it to loop over and over again. 1434 01:09:26,740 --> 01:09:30,580 So we'll go ahead and take a look now at mario-11 11. 1435 01:09:30,580 --> 01:09:34,645 This may be the most gratifying example so far. 1436 01:09:34,645 --> 01:09:37,027 [MUSIC PLAYING - "SUPER MARIO BROS. 1437 01:09:37,027 --> 01:09:39,641 THEME"] 1438 01:09:39,641 --> 01:09:44,949 Notice that also we're triggering a jump sound whenever he jump. 1439 01:09:44,949 --> 01:09:46,990 And when we hit the block, it plays a coin sound. 1440 01:09:46,990 --> 01:09:49,720 But if we hit it again, it doesn't play it. 1441 01:09:49,720 --> 01:09:51,529 It plays like a little thud sound. 1442 01:09:51,529 --> 01:09:53,529 And that's effectively the gist of the game. 1443 01:09:53,529 --> 01:09:58,450 There's no death sound unfortunately, but that's 1444 01:09:58,450 --> 01:10:00,700 how far we'll go with Mario today. 1445 01:10:00,700 --> 01:10:03,340 But we'll go ahead and look at how we implemented this. 1446 01:10:03,340 --> 01:10:07,090 We store the music in our map with music gets love.audio.newSource 1447 01:10:07,090 --> 01:10:09,760 music/overworld.mp3. 1448 01:10:09,760 --> 01:10:14,230 And all we do is start playing this here at the bottom of our create function, 1449 01:10:14,230 --> 01:10:18,160 this .music set looping to true as well, and this .musicplay. 1450 01:10:18,160 --> 01:10:21,320 Because recall we do want the music to constantly go over and over again, 1451 01:10:21,320 --> 01:10:25,510 even if they surpass the duration of the sound file. 1452 01:10:25,510 --> 01:10:29,510 And then player.lua has a new sounds table here, 1453 01:10:29,510 --> 01:10:31,940 which just has jump, hit, and coin, and then there 1454 01:10:31,940 --> 01:10:33,464 are just new sources as shown there. 1455 01:10:33,464 --> 01:10:36,630 And they're all passed in the static flag because these are all small files, 1456 01:10:36,630 --> 01:10:39,820 they can be stored in memory without too many repercussions-- jump, hit, 1457 01:10:39,820 --> 01:10:41,170 and coin. 1458 01:10:41,170 --> 01:10:44,500 And these are called here in the idle state, for example, 1459 01:10:44,500 --> 01:10:47,170 and in all states when we transition to jumping, 1460 01:10:47,170 --> 01:10:51,730 we should play this .soundsJump Soundz play which is simply just plays his 1461 01:10:51,730 --> 01:10:52,730 jump sound. 1462 01:10:52,730 --> 01:10:55,210 Same thing for in the walking statement. 1463 01:10:55,210 --> 01:11:02,230 If we go to the part where we collide with tiles here, the question blocks, 1464 01:11:02,230 --> 01:11:06,160 we can see that we're keeping two variables -- play coin and play hit-- 1465 01:11:06,160 --> 01:11:08,530 because depending on which kind of tile block we've hit 1466 01:11:08,530 --> 01:11:12,520 we may want to play the coin sound or the thud sound. 1467 01:11:12,520 --> 01:11:15,610 If we hit a tile question block, the yellow one, 1468 01:11:15,610 --> 01:11:19,690 and we make a transition to the dark one, we should hit do play coin. 1469 01:11:19,690 --> 01:11:20,740 Otherwise, play hit. 1470 01:11:20,740 --> 01:11:24,020 We've collided with something, but it's not a yellow block so just play a hit. 1471 01:11:24,020 --> 01:11:26,440 And then here down below, we see if play coin is 1472 01:11:26,440 --> 01:11:29,170 set to true, then self.soundsCoin play. 1473 01:11:29,170 --> 01:11:30,972 Otherwise, self.soundsHit play. 1474 01:11:30,972 --> 01:11:32,680 And that's all there really is the audio, 1475 01:11:32,680 --> 01:11:36,220 it's probably the easiest part of using LOVE 2D. 1476 01:11:36,220 --> 01:11:39,462 So other things to do that we didn't have time for unfortunately 1477 01:11:39,462 --> 01:11:41,170 would be like adding enemies to the game. 1478 01:11:41,170 --> 01:11:43,870 So something similar to Mario in spirit, but that 1479 01:11:43,870 --> 01:11:47,530 has its own logic being applied in the update function for example. 1480 01:11:47,530 --> 01:11:52,900 And you can give enemies their own states, idle, walking, et cetera. 1481 01:11:52,900 --> 01:11:55,972 Maybe they're going after Mario, maybe they're running away. 1482 01:11:55,972 --> 01:11:58,180 An end flagpole, something that you collide with that 1483 01:11:58,180 --> 01:12:00,760 triggers a transition to another level. 1484 01:12:00,760 --> 01:12:04,090 A timer so that you can add a sense of urgency to the game. 1485 01:12:04,090 --> 01:12:07,174 Power ups, text transitions, lives, worlds, even 1486 01:12:07,174 --> 01:12:10,090 if you want to be grandiose, and then warp pipes and a whole lot more. 1487 01:12:10,090 --> 01:12:12,290 So that's the nice thing is you guys have a foundation now. 1488 01:12:12,290 --> 01:12:14,860 You can pretty much implement anything with the tools that you show today. 1489 01:12:14,860 --> 01:12:17,530 So I'll stick around for questions, but thanks a lot 1490 01:12:17,530 --> 01:12:19,760 for coming and best of luck. 1491 01:12:19,760 --> 01:12:20,740 Thank you. 1492 01:12:20,740 --> 01:12:23,490 [APPLAUSE] 1493 01:12:23,490 --> 01:12:26,472