WEBVTT X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:900000 00:00:00.000 --> 00:00:03.479 [MUSIC PLAYING] 00:00:16.999 --> 00:00:18.290 COLTON OGDEN: Hello, everybody. 00:00:18.290 --> 00:00:20.530 Welcome to GD50 Lecture 2. 00:00:20.530 --> 00:00:22.300 This is Breakout. 00:00:22.300 --> 00:00:25.225 And interestingly, CS50 has a history with Breakout, 00:00:25.225 --> 00:00:26.680 so I pulled this up today. 00:00:26.680 --> 00:00:30.700 This is Pset3 in 2015, 2014. 00:00:30.700 --> 00:00:34.960 It was an implementation of Breakout using the Stanford Portable 00:00:34.960 --> 00:00:37.150 Library, which was a sort of Java library 00:00:37.150 --> 00:00:40.450 that we were able to get C bindings for. 00:00:40.450 --> 00:00:42.430 And so students were able to actually implement 00:00:42.430 --> 00:00:48.040 a game what was at the time the CS50 appliance, which is a Linux distro. 00:00:48.040 --> 00:00:49.870 But suffice to say that was-- 00:00:49.870 --> 00:00:51.280 oh, a funny story also. 00:00:51.280 --> 00:00:56.570 I happened to also write the lasers for this implementation back in the day. 00:00:56.570 --> 00:00:59.260 And I think that was one of the first bits of code 00:00:59.260 --> 00:01:02.380 I got my hands dirty with when working with CS50. 00:01:02.380 --> 00:01:04.919 So today in the context of Breakout, we'll 00:01:04.919 --> 00:01:07.960 be talking about a few different things that we haven't talked about yet. 00:01:07.960 --> 00:01:10.600 Sprite sheets being chief among them most likely. 00:01:10.600 --> 00:01:12.130 At least the most visibly so. 00:01:12.130 --> 00:01:15.370 So sprite sheets are simply a way of taking an image, a large image, 00:01:15.370 --> 00:01:19.210 and rather than splitting it, rather than loading individual images 00:01:19.210 --> 00:01:21.260 for all of your different things in the game, 00:01:21.260 --> 00:01:25.990 whether it's your aliens or your paddles or whatnot, you can put everything 00:01:25.990 --> 00:01:30.080 into one sheet and then just sort of index into that sheet using rectangles, 00:01:30.080 --> 00:01:30.580 quads. 00:01:30.580 --> 00:01:31.535 We'll talk about soon. 00:01:31.535 --> 00:01:33.910 Which will allow you to just draw a subset of that image, 00:01:33.910 --> 00:01:37.930 and therefore condense all of your artwork just into one piece, one file. 00:01:37.930 --> 00:01:41.020 We'll be talking a little bit more about procedural generation 00:01:41.020 --> 00:01:42.580 in the context of Breakout. 00:01:42.580 --> 00:01:45.680 And in this case, we'll be laying out all the bricks in the game world 00:01:45.680 --> 00:01:46.240 procedurally. 00:01:46.240 --> 00:01:50.480 So having instead of the same set of colors, in this case, 00:01:50.480 --> 00:01:55.510 the standard layout is to have a bunch of the same colored bricks row by row. 00:01:55.510 --> 00:01:58.702 We'll actually implement a dynamic generation approach 00:01:58.702 --> 00:02:00.910 and have a bunch of different cool layouts we'll see. 00:02:00.910 --> 00:02:04.810 And it's actually quite simple to achieve pretty believable results. 00:02:04.810 --> 00:02:07.100 We'll manage state a little bit better in this game. 00:02:07.100 --> 00:02:10.479 So before we sort of had a couple of global variables 00:02:10.479 --> 00:02:14.320 and we didn't really have the concept of a per state or a global state 00:02:14.320 --> 00:02:18.310 that we were cleanly sort of sharing between all of our states 00:02:18.310 --> 00:02:19.510 for our state machine. 00:02:19.510 --> 00:02:22.552 But to avoid having sort of like a polluted global name space 00:02:22.552 --> 00:02:24.760 and to just sort of keep things a little bit cleaner, 00:02:24.760 --> 00:02:28.051 we'll end up taking all of the important variables for our code like, you know, 00:02:28.051 --> 00:02:29.770 the player and any other entities. 00:02:29.770 --> 00:02:30.940 The bricks, the ball. 00:02:30.940 --> 00:02:33.064 And rather than keep them in our main [INAUDIBLE],, 00:02:33.064 --> 00:02:34.410 we'll end up shifting them. 00:02:34.410 --> 00:02:37.990 We'll sort of transfer them to and from the different states 00:02:37.990 --> 00:02:41.366 via the state machine's enter method. 00:02:41.366 --> 00:02:42.490 We'll actually have levels. 00:02:42.490 --> 00:02:43.780 So a progression system. 00:02:43.780 --> 00:02:45.650 So start at level one, go up. 00:02:45.650 --> 00:02:48.730 And then with each level, we'll implement a scale 00:02:48.730 --> 00:02:51.730 in terms of the generation of the bricks. 00:02:51.730 --> 00:02:54.545 So we'll get higher tiered bricks and more points as a result. 00:02:54.545 --> 00:02:55.670 We'll have a health system. 00:02:55.670 --> 00:02:58.780 So hearts, in a similar fashion to Legend of Zelda. 00:02:58.780 --> 00:03:02.380 Particle systems, which are a very important aesthetic component 00:03:02.380 --> 00:03:04.720 to 2D games and 3D games. 00:03:04.720 --> 00:03:07.720 Particle systems basically being a bunch of spawned images 00:03:07.720 --> 00:03:10.720 that you sort of cluster, you put into a little spawner, 00:03:10.720 --> 00:03:15.190 emit them in a certain way, and color them, perform math on them, 00:03:15.190 --> 00:03:17.462 and get sort of believable effects like fire and smoke 00:03:17.462 --> 00:03:19.420 and all these other things that would otherwise 00:03:19.420 --> 00:03:24.040 be not easy to do using simple animation, 00:03:24.040 --> 00:03:26.980 but trivial with a particle system. 00:03:26.980 --> 00:03:31.060 We'll do a little bit more complicated collision detection with our paddle 00:03:31.060 --> 00:03:33.132 and with our bricks than we did with Pong. 00:03:33.132 --> 00:03:34.840 And then we'll also talk lastly about how 00:03:34.840 --> 00:03:38.950 we can save data locally to our computer so that when we close the application 00:03:38.950 --> 00:03:42.460 and run it again, we end up having a persistent high score rather 00:03:42.460 --> 00:03:44.980 than just something that's volatile. 00:03:44.980 --> 00:03:49.300 So first though, I would like to demo today's finished game. 00:03:49.300 --> 00:03:55.260 So if anybody would like to demo from the audience, that would be nice. 00:03:55.260 --> 00:03:57.311 Go ahead and come up. 00:03:57.311 --> 00:03:58.810 I'll go ahead and cue it up for you. 00:04:02.652 --> 00:04:03.360 What's your name? 00:04:03.360 --> 00:04:03.980 JEREMY: Jeremy. 00:04:03.980 --> 00:04:04.330 COLTON OGDEN: Jeremy. 00:04:04.330 --> 00:04:04.660 Colton. 00:04:04.660 --> 00:04:05.290 JEREMY: Nice to meet you, Colton. 00:04:05.290 --> 00:04:06.280 COLTON OGDEN: Nice to meet you. 00:04:06.280 --> 00:04:08.410 So we're going to go ahead and run Breakout here. 00:04:13.220 --> 00:04:15.462 And so it uses the arrow keys. 00:04:15.462 --> 00:04:17.170 So if you go ahead and press up and down, 00:04:17.170 --> 00:04:19.430 you'll see you can move between the start and the high score screen. 00:04:19.430 --> 00:04:21.040 So they're two separate screens. 00:04:21.040 --> 00:04:22.392 So go ahead and-- 00:04:22.392 --> 00:04:24.600 here we have when you start, you can choose a paddle. 00:04:24.600 --> 00:04:27.740 So rather than just the same old paddle every time, you get to select. 00:04:27.740 --> 00:04:29.650 And as you can see here, he chose green. 00:04:29.650 --> 00:04:31.000 So he gets the green paddle. 00:04:31.000 --> 00:04:32.650 These bricks all procedure generated. 00:04:32.650 --> 00:04:36.520 So if he runs the application, they'll be completely different. 00:04:36.520 --> 00:04:39.610 And as is the classic formula, the ball moves 00:04:39.610 --> 00:04:41.230 between the bricks and the paddle. 00:04:41.230 --> 00:04:44.060 When it hits a brick, if it's of a certain color, 00:04:44.060 --> 00:04:46.840 it'll either get destroyed-- in this case, if it's blue, 00:04:46.840 --> 00:04:48.280 it's the base color brick. 00:04:48.280 --> 00:04:50.590 So it's the lowest value. 00:04:50.590 --> 00:04:52.480 And if it's higher than blue, it'll end up 00:04:52.480 --> 00:04:55.840 going down a color depending on which color it is. 00:04:55.840 --> 00:04:58.210 I believe it goes blue, green, red, purple, yellow. 00:04:58.210 --> 00:05:01.600 So anything higher will get shifted down. 00:05:01.600 --> 00:05:04.240 And then the player amasses points, as you can see top right. 00:05:04.240 --> 00:05:04.930 Score. 00:05:04.930 --> 00:05:06.370 And notice also the three hearts. 00:05:06.370 --> 00:05:07.820 That will be the player's health. 00:05:07.820 --> 00:05:13.830 So if he were to lose on purpose possibly, 00:05:13.830 --> 00:05:18.870 we can see he gets another message that's saying press Enter to serve. 00:05:18.870 --> 00:05:20.730 His hearts have gone down by one. 00:05:20.730 --> 00:05:23.370 So now he's got two out of three health. 00:05:23.370 --> 00:05:28.020 And so eventually if he were to by chance lose completely-- 00:05:32.900 --> 00:05:35.290 oh. 00:05:35.290 --> 00:05:37.290 That's honestly the most fun part about Breakout 00:05:37.290 --> 00:05:39.630 is just getting it caught in a bunch of stuff. 00:05:39.630 --> 00:05:41.620 But you can see we go to a Game Over screen. 00:05:41.620 --> 00:05:42.850 It shows your final score. 00:05:42.850 --> 00:05:46.130 And then you can press Enter and it will-- 00:05:46.130 --> 00:05:46.840 oh. 00:05:46.840 --> 00:05:48.040 I must have had a bug. 00:05:48.040 --> 00:05:51.500 But that should take you back to the-- 00:05:51.500 --> 00:05:53.440 if in the event that you have a high score, 00:05:53.440 --> 00:05:55.360 it'll take you to enter a high score. 00:05:55.360 --> 00:06:00.270 And if you don't have a high score, it'll take you back to the Start menu. 00:06:00.270 --> 00:06:02.020 So I made a couple of last minute changes. 00:06:02.020 --> 00:06:04.390 Unfortunately I must have left something in there. 00:06:04.390 --> 00:06:07.606 But that's Breakout in a nutshell. 00:06:07.606 --> 00:06:10.730 Our goal today will be to implement basically all the functionality we saw. 00:06:10.730 --> 00:06:12.550 Oh, we didn't take a look at the high score screen. 00:06:12.550 --> 00:06:14.633 So let's take a look at that really quick as well. 00:06:14.633 --> 00:06:17.820 So here at the title, you can see we have Start and High Scores. 00:06:17.820 --> 00:06:18.970 Oh, man. 00:06:18.970 --> 00:06:19.510 OK. 00:06:19.510 --> 00:06:20.884 I must have screwed something up. 00:06:20.884 --> 00:06:24.050 So I'm going want to go [INAUDIBLE] Breakout 12. 00:06:24.050 --> 00:06:24.550 OK. 00:06:24.550 --> 00:06:25.049 Sorry. 00:06:25.049 --> 00:06:25.660 I apologize. 00:06:25.660 --> 00:06:26.900 I'm going to fix that. 00:06:26.900 --> 00:06:31.420 But it should show this menu here where you will have a list of all your names 00:06:31.420 --> 00:06:35.830 that get loaded from a file and will output your score accordingly. 00:06:35.830 --> 00:06:41.920 And in the event that you get a new high score, 00:06:41.920 --> 00:06:44.230 you'll get to enter your name after that, 00:06:44.230 --> 00:06:46.360 and then it will end up saving it to another file. 00:06:46.360 --> 00:06:48.401 And when we get to that point I'll try and fix it 00:06:48.401 --> 00:06:51.140 so that we can actually see what it looks like. 00:06:51.140 --> 00:06:53.240 So let's go back to these slides here. 00:06:53.240 --> 00:06:55.790 So this is the overall state, flow of our game. 00:06:55.790 --> 00:06:59.080 So as you can see by me marking it out in a highlighted color, 00:06:59.080 --> 00:07:00.550 we start off in the StartState. 00:07:00.550 --> 00:07:01.990 And this is all stuff we've covered before. 00:07:01.990 --> 00:07:03.100 Just the state machine. 00:07:03.100 --> 00:07:05.266 It's a little bit more complicated than Flappy Bird. 00:07:05.266 --> 00:07:07.930 We have eight states as opposed to I think it 00:07:07.930 --> 00:07:10.420 was four or five in the last lecture. 00:07:10.420 --> 00:07:13.640 And the arrows illustrate which states can move in between other states. 00:07:13.640 --> 00:07:17.110 So as we saw, the StartState can move via the up and down arrows 00:07:17.110 --> 00:07:18.700 in the HighScoreState. 00:07:18.700 --> 00:07:20.710 It can move between the HighScoreState and back. 00:07:20.710 --> 00:07:23.020 So when you go into the HighScoreState, press Escape, 00:07:23.020 --> 00:07:25.180 go back to the StartState. 00:07:25.180 --> 00:07:27.280 The StartState also has an arrow branching off 00:07:27.280 --> 00:07:29.710 to the left going down to the PaddleSelectState 00:07:29.710 --> 00:07:32.952 where we saw the user is able to select a paddle to use. 00:07:32.952 --> 00:07:35.410 Once they've selected a paddle, we'll go to the ServeState. 00:07:35.410 --> 00:07:37.780 They'll be able to serve the ball at their leisure. 00:07:37.780 --> 00:07:40.380 And then it will go back and forth between the PlayState. 00:07:40.380 --> 00:07:43.900 So if they end up taking damage, the ball 00:07:43.900 --> 00:07:47.230 goes below the surface of the screen, they'll go back to the ServeState 00:07:47.230 --> 00:07:49.930 again so they can reorient themselves. 00:07:49.930 --> 00:07:53.170 If they're in the PlayState and they end up scoring, 00:07:53.170 --> 00:07:55.476 clearing the whole entire set of bricks, they'll 00:07:55.476 --> 00:07:57.100 actually get taken to the VictoryState. 00:07:57.100 --> 00:07:59.266 And the VictoryState is where we increment the level 00:07:59.266 --> 00:08:00.760 and we also regenerate the level. 00:08:00.760 --> 00:08:02.801 And the VictoryState goes back to the ServeState, 00:08:02.801 --> 00:08:04.990 and then we repeat that whole loop again. 00:08:04.990 --> 00:08:08.200 In the PlayState if they are to get a Game Over, 00:08:08.200 --> 00:08:10.960 they'll go to the GameOverState, it'll tell them their score, 00:08:10.960 --> 00:08:13.090 and then they'll go to the EnterHighScoreState 00:08:13.090 --> 00:08:14.950 depending on whether they have a high score. 00:08:14.950 --> 00:08:18.010 And if not, as seen by the arrow that goes up and to the left, 00:08:18.010 --> 00:08:19.990 they'll actually go back to the StartState. 00:08:19.990 --> 00:08:22.730 And then the EnterHighScoreState will also go back to the HighScoreState 00:08:22.730 --> 00:08:24.438 so that they can see once they've entered 00:08:24.438 --> 00:08:27.687 their high score, their score relative to the other scores in the list. 00:08:27.687 --> 00:08:29.770 So in Breakout0, which we're going to look at now, 00:08:29.770 --> 00:08:31.436 we're going to do some very basic stuff. 00:08:31.436 --> 00:08:34.059 So this is the Day 0 update as always. 00:08:34.059 --> 00:08:37.900 I'm in Breakout0 right now. 00:08:37.900 --> 00:08:39.049 Yes, I am. 00:08:39.049 --> 00:08:42.940 So what we're going to do is we're going to look at first thing here, line 27. 00:08:42.940 --> 00:08:47.110 So before what we were doing in our application 00:08:47.110 --> 00:08:51.850 is having basically a lot of files at the top level and sort of losing track 00:08:51.850 --> 00:08:53.697 of what we were doing potentially. 00:08:53.697 --> 00:08:55.780 Especially as you start adding more and more files 00:08:55.780 --> 00:08:58.330 and you've got like 50, 100 more files, that's 00:08:58.330 --> 00:09:00.190 something that's obviously not maintainable. 00:09:00.190 --> 00:09:02.884 So the solution there, just put them in folders 00:09:02.884 --> 00:09:04.300 and then keep track of everything. 00:09:04.300 --> 00:09:05.370 Keep them organized. 00:09:05.370 --> 00:09:07.745 And that's a major thing that we're going to start doing. 00:09:07.745 --> 00:09:10.120 And on top of that, we're also going to, in our code, 00:09:10.120 --> 00:09:11.800 keep things a little bit more modular. 00:09:11.800 --> 00:09:15.790 And that's why we have this file source slash dependencies, 00:09:15.790 --> 00:09:18.520 which we'll take a look at in a second. 00:09:18.520 --> 00:09:20.530 We've allocated a bunch of global tables here. 00:09:20.530 --> 00:09:23.410 So we're taking the design decision of even 00:09:23.410 --> 00:09:26.740 though I mentioned that we will be sort of taking a lot of the global variables 00:09:26.740 --> 00:09:29.740 out of our application assets, we're going to keep 00:09:29.740 --> 00:09:31.450 all of those in some global variables. 00:09:31.450 --> 00:09:34.491 And we'll see in the future how we can maybe implement a resource manager 00:09:34.491 --> 00:09:36.430 class that takes care of this for us. 00:09:36.430 --> 00:09:38.890 But for now, for simplicity's sake, in love.load, 00:09:38.890 --> 00:09:41.320 we're just going to have a few global tables that contain, 00:09:41.320 --> 00:09:43.300 in this case, global fonts. 00:09:43.300 --> 00:09:46.237 So by key, we can index small, medium, and large fonts, 00:09:46.237 --> 00:09:48.070 which are just new fonts at different sizes. 00:09:48.070 --> 00:09:49.379 8, 16, 32. 00:09:49.379 --> 00:09:50.170 And we're using it. 00:09:50.170 --> 00:09:53.649 We have a fonts folder now instead of just keeping it at the parent level. 00:09:53.649 --> 00:09:54.940 We're going to set it to small. 00:09:54.940 --> 00:09:55.960 We have global textures. 00:09:55.960 --> 00:09:59.641 So background, main, arrows, hearts, particle. 00:09:59.641 --> 00:10:02.390 So we have the background, which was the background of our screen. 00:10:02.390 --> 00:10:05.230 Main has all of our bricks, paddles, the balls, et cetera. 00:10:05.230 --> 00:10:07.770 Arrows are going to be for the paddle select screen. 00:10:07.770 --> 00:10:09.220 The two left and right arrows. 00:10:09.220 --> 00:10:10.990 Hearts are going to be for our health. 00:10:10.990 --> 00:10:13.570 And then particle is a single, small, tiny little texture 00:10:13.570 --> 00:10:16.330 that we'll use to spawn all the particles in our particle systems 00:10:16.330 --> 00:10:20.584 later on as we get towards the end of the demonstration. 00:10:20.584 --> 00:10:21.250 So this is push. 00:10:21.250 --> 00:10:22.450 We're setting it up just like normal. 00:10:22.450 --> 00:10:23.440 Nothing new there. 00:10:23.440 --> 00:10:26.200 Except the virtual width, virtual height, and all that stuff, 00:10:26.200 --> 00:10:29.930 those have been moved out, if we look into source in a constants file. 00:10:29.930 --> 00:10:32.770 So this file here, instead of having all the constants in main, 00:10:32.770 --> 00:10:35.020 it kind of makes sense just to take them out, put them 00:10:35.020 --> 00:10:39.520 in a file called constants.lua, and we can sort manage all that. 00:10:39.520 --> 00:10:42.340 We can know immediately when we're looking at capital window 00:10:42.340 --> 00:10:43.240 width, window height, et cetera. 00:10:43.240 --> 00:10:44.410 And these are all constants. 00:10:44.410 --> 00:10:46.150 If you have a constants file, we just can more easily 00:10:46.150 --> 00:10:48.460 track it rather than having to grab through all of our files 00:10:48.460 --> 00:10:50.410 to try and figure out what we were looking at. 00:10:53.830 --> 00:10:59.410 And the constants are used here in our set up screen as before. 00:10:59.410 --> 00:11:03.289 And then another sounds global table, just as before. 00:11:03.289 --> 00:11:05.080 We have a bunch of different sound effects. 00:11:05.080 --> 00:11:07.038 I've separated the music from the sound effects 00:11:07.038 --> 00:11:10.020 just so that we can see at a glance, oh, this is the music, 00:11:10.020 --> 00:11:11.560 these are the sound effects. 00:11:11.560 --> 00:11:12.970 Pretty straight forward. 00:11:12.970 --> 00:11:14.500 We have a state machine, as always. 00:11:14.500 --> 00:11:18.250 And we're just going to use a StartState for this demonstration. 00:11:18.250 --> 00:11:20.770 Setting it to Start. 00:11:20.770 --> 00:11:22.180 Love.resize, love.update. 00:11:22.180 --> 00:11:23.929 These are all functions we've seen before. 00:11:23.929 --> 00:11:25.000 Nothing too new. 00:11:25.000 --> 00:11:25.772 Love.keypress. 00:11:25.772 --> 00:11:26.980 We have a global input table. 00:11:26.980 --> 00:11:30.880 So as in the case of Flappy Bird, we can index into that input table anywhere 00:11:30.880 --> 00:11:34.930 in our application and call love.keyboard.wasPressed[key], 00:11:34.930 --> 00:11:38.020 which allows us to take input exclusively from main and use it 00:11:38.020 --> 00:11:40.680 in other modules. 00:11:40.680 --> 00:11:42.850 Here we're drawing the-- 00:11:42.850 --> 00:11:45.160 so this is the actual rendering code. 00:11:45.160 --> 00:11:49.690 And we're doing this in our love.draw as opposed to a specific state 00:11:49.690 --> 00:11:51.940 because this is actually going to apply to all states. 00:11:51.940 --> 00:11:53.731 We're always going to have this background. 00:11:53.731 --> 00:11:55.900 So rather than duplicate it over and over again, 00:11:55.900 --> 00:11:58.240 in this instance, this minor bit of code, 00:11:58.240 --> 00:12:02.214 we're going to display the background behind all the states. 00:12:02.214 --> 00:12:04.630 So all the states are going to render over this background 00:12:04.630 --> 00:12:07.000 and make it seem a little more cohesive. 00:12:07.000 --> 00:12:09.039 We're going to draw at 0, 0 without rotation. 00:12:09.039 --> 00:12:11.080 And then this bit of math here, the virtual width 00:12:11.080 --> 00:12:15.929 divided by, and then background width minus one, end up being a scale factor 00:12:15.929 --> 00:12:18.220 so that we can always scale it to be our virtual width. 00:12:18.220 --> 00:12:22.930 Because the texture by default is some amount smaller than our actual window 00:12:22.930 --> 00:12:26.140 or our actual virtual width and height, but by dividing virtual width 00:12:26.140 --> 00:12:29.560 by whatever the background width of that image is by one, 00:12:29.560 --> 00:12:32.950 we'll get a scale factor because virtual width is larger than the image. 00:12:32.950 --> 00:12:37.270 We'll get a scale factor on X and Y that equates to it completely stretching 00:12:37.270 --> 00:12:41.050 to fill our virtual width and height. 00:12:41.050 --> 00:12:44.136 And recall that these two parameters are the scale on the X and the Y. 00:12:44.136 --> 00:12:47.260 So it's going to be some, like, one point something or two point something. 00:12:47.260 --> 00:12:50.230 Whatever it takes to end up filling the screen. 00:12:50.230 --> 00:12:52.567 And then lastly here, the new bit I implemented 00:12:52.567 --> 00:12:55.150 is just a display of frames per second function, which I think 00:12:55.150 --> 00:12:58.357 is kind of important generally, and it's very easy to do. 00:12:58.357 --> 00:13:00.940 I don't recall, I don't think we talked about it yet, but just 00:13:00.940 --> 00:13:02.710 love.timer.getFPS. 00:13:02.710 --> 00:13:04.811 And then I just draw in the top left in green 00:13:04.811 --> 00:13:07.810 so that we can see it throughout all of our iterations of the game, what 00:13:07.810 --> 00:13:08.770 are frames per second are. 00:13:08.770 --> 00:13:12.070 If you want to monitor without having to look through your terminal or anything 00:13:12.070 --> 00:13:13.861 like that, just displaying at the top, it's 00:13:13.861 --> 00:13:16.090 standard practice in a lot of games. 00:13:16.090 --> 00:13:18.140 If you've gone to the debug console or whatnot 00:13:18.140 --> 00:13:20.920 or sort of looked into some of the hacks, 00:13:20.920 --> 00:13:23.030 you'll see that in a lot of places. 00:13:23.030 --> 00:13:25.750 So I talked earlier about dependencies.lua. 00:13:25.750 --> 00:13:30.190 So this ties in as well to our effort to sort of modularize everything, 00:13:30.190 --> 00:13:31.750 keep everything organized. 00:13:31.750 --> 00:13:34.010 Instead of requiring everything at the top of main, 00:13:34.010 --> 00:13:36.550 let's just put it all in a file and then we'll 00:13:36.550 --> 00:13:40.630 know at a glance what we're requiring and we don't have to look through main 00:13:40.630 --> 00:13:44.620 and make main 100 lines, potentially a lot more than it needs to be. 00:13:44.620 --> 00:13:47.500 So requiring push, requiring class. 00:13:47.500 --> 00:13:49.690 Same as we've done before. 00:13:49.690 --> 00:13:50.791 Require source.constants. 00:13:50.791 --> 00:13:51.790 We have access to those. 00:13:51.790 --> 00:13:55.550 Require StateMachine, and then BaseState and StartState. 00:13:55.550 --> 00:13:58.750 So let's go ahead and take a look at our StartState. 00:13:58.750 --> 00:14:01.330 So I put states in a subfolder of source. 00:14:01.330 --> 00:14:03.580 This is another effort to sort of keep things modular. 00:14:03.580 --> 00:14:08.410 In this particular project, we won't have a lot of nested folders of code, 00:14:08.410 --> 00:14:10.720 but I decided to put the states in their own folder 00:14:10.720 --> 00:14:14.130 just so easily you can get access to all your states. 00:14:14.130 --> 00:14:18.610 So we'll look at StartState here on line 21. 00:14:18.610 --> 00:14:21.940 So recall in the StartState, we just had Breakout in the center of the screen 00:14:21.940 --> 00:14:24.950 and then we had Start Game and High Scores. 00:14:24.950 --> 00:14:27.772 So the user was able to highlight which state he wanted to look at. 00:14:27.772 --> 00:14:29.980 So we need to keep track of which one is highlighted. 00:14:29.980 --> 00:14:32.220 So all this variable's purpose is just to keep track. 00:14:32.220 --> 00:14:33.250 So one or two. 00:14:33.250 --> 00:14:36.115 One being Play Game, and two being High Scores. 00:14:39.580 --> 00:14:43.930 And then here if we press up and down, then we-- 00:14:43.930 --> 00:14:45.850 because there's only two options effectively, 00:14:45.850 --> 00:14:48.400 you can just flip whatever highlight is with one or two. 00:14:48.400 --> 00:14:50.590 If you have a list of options that's more than two, 00:14:50.590 --> 00:14:52.840 you'll need to increment one until it gets to whatever 00:14:52.840 --> 00:14:55.332 X is, your number of list options. 00:14:55.332 --> 00:14:57.040 And then if you press down at that point, 00:14:57.040 --> 00:14:58.840 you should flip back up to the top. 00:14:58.840 --> 00:15:02.110 And the same holds true for whether you're at option one. 00:15:02.110 --> 00:15:05.290 You should go flip, rotate to the bottom of your list 00:15:05.290 --> 00:15:08.712 so that it looks as if you've gone all the way around. 00:15:08.712 --> 00:15:11.170 And-- then we're just playing a sound here when we do that. 00:15:11.170 --> 00:15:14.390 We have a love.keyboard.wasPressed[escape] call 00:15:14.390 --> 00:15:14.890 here. 00:15:14.890 --> 00:15:18.109 It's not global anymore because there are some states in our application 00:15:18.109 --> 00:15:20.650 where we might want to press Escape to actually go backwards, 00:15:20.650 --> 00:15:22.464 and we'll see that. 00:15:22.464 --> 00:15:23.380 And so rendering here. 00:15:23.380 --> 00:15:25.450 We render Breakout with a large font. 00:15:25.450 --> 00:15:29.500 Now that we can access G fonts at large key in the center of the screen, 00:15:29.500 --> 00:15:30.370 set medium font. 00:15:30.370 --> 00:15:36.060 And then we're going to render our two text fields one after the other. 00:15:36.060 --> 00:15:38.132 But if highlighted is equal to one, then we're 00:15:38.132 --> 00:15:40.590 going to set it to some blue color, which is one of three-- 00:15:40.590 --> 00:15:43.590 255, 255, 255. 00:15:43.590 --> 00:15:44.996 And then render it. 00:15:44.996 --> 00:15:47.620 And then make sure to reset the color after that because recall 00:15:47.620 --> 00:15:50.270 love 2D is sort of like a state machine in its own right, 00:15:50.270 --> 00:15:53.340 where if you set the color to something, whatever you draw and render 00:15:53.340 --> 00:15:56.430 after that, be it images or text, will adopt that color. 00:15:56.430 --> 00:16:00.480 So having everything be 255, 255, 255, 255, 00:16:00.480 --> 00:16:02.670 which is pure white, completely opaque, has 00:16:02.670 --> 00:16:04.920 the effect of drawing everything completely opaque. 00:16:04.920 --> 00:16:07.980 But if you don't do that, your images or whatnot that you draw afterwards 00:16:07.980 --> 00:16:10.962 will be tinted or transparent, which you most of the time don't want. 00:16:10.962 --> 00:16:13.170 But you might sometimes want that, and we'll actually 00:16:13.170 --> 00:16:14.589 see that in the PaddleSelectState. 00:16:14.589 --> 00:16:15.880 And same thing holds true here. 00:16:15.880 --> 00:16:19.090 If highlighted is two, do the exact same thing. 00:16:19.090 --> 00:16:22.830 And so if we run this application, which is mainly 00:16:22.830 --> 00:16:26.771 just a subset of what we saw before, we can move up and down 00:16:26.771 --> 00:16:28.020 between Start and High Scores. 00:16:28.020 --> 00:16:30.270 But if we press Enter on any of them, nothing 00:16:30.270 --> 00:16:33.450 happens because we have no event handlers actually taking care of that. 00:16:33.450 --> 00:16:37.300 But we have the image scaled to the screen, we have Breakout in the middle, 00:16:37.300 --> 00:16:40.750 and we have our two menu options there. 00:16:40.750 --> 00:16:41.680 So Breakout1. 00:16:41.680 --> 00:16:46.710 So this is where we start to dive a little bit into sprite sheets, which 00:16:46.710 --> 00:16:49.530 is a major component of game development, 2D game 00:16:49.530 --> 00:16:53.340 development that we'll be looking at in the future and in this application. 00:16:53.340 --> 00:16:57.300 But a sprite sheet is just, ultimately, rather than have-- 00:16:57.300 --> 00:17:00.150 I don't know how many images there are on this sprite sheet here. 00:17:00.150 --> 00:17:03.030 But however many of these files, just have 00:17:03.030 --> 00:17:07.200 one file put them all together, and then using rectangles, define 00:17:07.200 --> 00:17:08.849 where all the different sprites are. 00:17:08.849 --> 00:17:11.400 And then when we want to draw, use those rectangles 00:17:11.400 --> 00:17:14.230 and just tell love.graphics.draw, I want you 00:17:14.230 --> 00:17:16.589 to draw this texture, this sprite sheet, but I want 00:17:16.589 --> 00:17:19.500 you to draw just this section of it. 00:17:19.500 --> 00:17:23.490 You'd pass it in a quad, which is just simply rectangle with height, X and Y. 00:17:23.490 --> 00:17:27.900 And love 2D will know, OK, I'm going to draw the image, but only this bit. 00:17:27.900 --> 00:17:31.290 And it has the effect of looking as if you're only drawing tiny little images 00:17:31.290 --> 00:17:34.092 as opposed to one monstrous image. 00:17:34.092 --> 00:17:36.300 And the functions that are relevant for us to look at 00:17:36.300 --> 00:17:41.250 are love.graphics.newquad, which takes an X, Y, width, and a height. 00:17:41.250 --> 00:17:45.570 And also a dimensions object, which you get from an image. 00:17:45.570 --> 00:17:46.380 We'll see that. 00:17:46.380 --> 00:17:50.870 And all that basically is, I believe, is just an X, Y, width and height as well. 00:17:50.870 --> 00:17:54.540 Or just a width and a height, rather, from whatever image 00:17:54.540 --> 00:17:57.180 you want to create quads for. 00:17:57.180 --> 00:18:00.174 And then love.graphics.draw, we've already seen it, 00:18:00.174 --> 00:18:01.590 but this is a different signature. 00:18:01.590 --> 00:18:06.480 This has texture, quad, X, Y. Quad being the second argument. 00:18:06.480 --> 00:18:09.030 And when it takes in this quad, it knows to only draw 00:18:09.030 --> 00:18:13.290 that defined rectangle of image to the screen. 00:18:13.290 --> 00:18:17.436 And so we'll go ahead and take a look now at Breakout1. 00:18:17.436 --> 00:18:22.746 AUDIENCE: [INAUDIBLE] 00:18:22.746 --> 00:18:24.870 COLTON OGDEN: The question was, are there any tools 00:18:24.870 --> 00:18:28.980 so that we don't have to guess where the quad is when we're doing the sheet? 00:18:28.980 --> 00:18:31.320 Yes, there are a lot of the time. 00:18:31.320 --> 00:18:35.637 I looked and saw a couple, but I haven't tested them thoroughly myself. 00:18:35.637 --> 00:18:37.470 For simpler examples like this, it's usually 00:18:37.470 --> 00:18:39.053 easy enough to programmatically do it. 00:18:39.053 --> 00:18:42.840 But yeah, when you get into having giant sprite atlases where 00:18:42.840 --> 00:18:46.890 you have especially things that are not necessarily symmetrical 00:18:46.890 --> 00:18:50.970 or rectangular looking even though they still need to be defined rectangularly, 00:18:50.970 --> 00:18:54.600 it's often best to use a tool like that. 00:18:54.600 --> 00:18:57.600 There are, I do believe, I just haven't used them. 00:18:57.600 --> 00:19:00.450 I can bring it up in a future lecture so we can discuss. 00:19:03.450 --> 00:19:08.430 Any other questions before we carry into Breakout1? 00:19:08.430 --> 00:19:09.840 All right. 00:19:09.840 --> 00:19:12.450 So I'm going to go ahead and open up the very first thing 00:19:12.450 --> 00:19:14.430 we should look at on Breakout1. 00:19:14.430 --> 00:19:18.000 In the source directory, we have a new file. 00:19:18.000 --> 00:19:20.970 And from here on out, I'm going to assume, 00:19:20.970 --> 00:19:24.990 we're going to always assume that when we introduce a new file, 00:19:24.990 --> 00:19:28.620 we're going to include it in dependencies.lua. 00:19:28.620 --> 00:19:32.160 And so in this case, all we need to do is just say require source/util. 00:19:32.160 --> 00:19:38.700 And as you can see, we're also adding a PlayState to this demonstration. 00:19:38.700 --> 00:19:41.520 But from here on out, I won't make mention of us actually 00:19:41.520 --> 00:19:42.840 adding it to our project. 00:19:42.840 --> 00:19:47.210 So util.lua is the module that contains the code 00:19:47.210 --> 00:19:51.150 we're going to use to actually generate quads for a given sprite sheet. 00:19:51.150 --> 00:19:54.850 And this function, all it does, is it takes an atlas or sprite sheet-- 00:19:54.850 --> 00:19:56.110 the names are synonymous. 00:19:56.110 --> 00:19:57.370 You'll hear them both. 00:19:57.370 --> 00:20:02.040 Or we pass it an Atlas, we pass it the width of the tile that we want 00:20:02.040 --> 00:20:04.800 and the height of the tile that we want. 00:20:04.800 --> 00:20:07.440 It's going to get the width and the height of the sheet here. 00:20:07.440 --> 00:20:11.050 So every image has a function called get width and get height, 00:20:11.050 --> 00:20:13.150 so we're just going to do that. 00:20:13.150 --> 00:20:16.290 And specifically the sheet width and sheet height 00:20:16.290 --> 00:20:19.920 are the width of the image divided by tile width and tile height. 00:20:19.920 --> 00:20:23.480 So we know how many times we need to iterate over the sprite sheet 00:20:23.480 --> 00:20:24.480 to generate a rectangle. 00:20:24.480 --> 00:20:29.700 We're dividing it up based on the size of our tiles. 00:20:29.700 --> 00:20:32.350 And then we just basically do a simple nested four loop here. 00:20:32.350 --> 00:20:34.250 We start a counter and a sprite sheet. 00:20:34.250 --> 00:20:37.470 This sprite sheet is going to be a table that holds all of our quads. 00:20:37.470 --> 00:20:39.220 We just say for Y, get zero. 00:20:39.220 --> 00:20:40.400 Sheet height minus one. 00:20:40.400 --> 00:20:44.200 So starting at the top left, going down. 00:20:44.200 --> 00:20:46.720 And starting at the top going down, and then x equals zero, 00:20:46.720 --> 00:20:50.320 starting at the left going right. 00:20:50.320 --> 00:20:55.540 At sprite sheet, sheet counter, which is one here because in lua, 00:20:55.540 --> 00:20:57.280 tables are one indexed. 00:20:57.280 --> 00:21:02.650 We're going to create a new quad at X times tile width, Y times tile width. 00:21:02.650 --> 00:21:05.110 Give it the width and the height of our tile. 00:21:05.110 --> 00:21:07.930 So just whatever we passed into our function signature. 00:21:07.930 --> 00:21:10.420 Here it will often be in this case be 16 by 32 00:21:10.420 --> 00:21:14.590 because that's the size of the bricks. 00:21:14.590 --> 00:21:17.380 And then we pass in the last parameter that we saw in the slide, 00:21:17.380 --> 00:21:19.640 which is atlas:getdimensions. 00:21:19.640 --> 00:21:21.760 And then we just increment our sheet counter here. 00:21:21.760 --> 00:21:23.990 And then at the end of this, when we're all done, we'll return this. 00:21:23.990 --> 00:21:25.930 We'll have a table of quads that we can then 00:21:25.930 --> 00:21:31.410 use that are in a sort of one, two, three, four, five, six, seven, eight. 00:21:31.410 --> 00:21:34.390 Well, I should say, one, two, three, four, five, six, seven, eight top 00:21:34.390 --> 00:21:38.230 left to bottom right of all the sprites in our sheet to make it 00:21:38.230 --> 00:21:40.990 super easy to look at. 00:21:40.990 --> 00:21:42.480 We have another function here. 00:21:42.480 --> 00:21:45.940 Lua doesn't by default, have a slice function, but we are just adding to it. 00:21:45.940 --> 00:21:47.200 Table.slice. 00:21:47.200 --> 00:21:52.390 It takes the table a first, the first entry in the table that we want, 00:21:52.390 --> 00:21:54.940 the last entry, and then the step between them. 00:21:54.940 --> 00:22:00.160 Just like Pythons slice function, it just iterates over the 00:22:00.160 --> 00:22:02.500 for loop, which is first till one. 00:22:02.500 --> 00:22:10.270 So one by default. Until the last or until whatever this sort of number sign 00:22:10.270 --> 00:22:13.450 is the size of a table, which I don't think we've introduced yet. 00:22:13.450 --> 00:22:18.430 But basically, if we pass in last, it'll stop there, otherwise just 00:22:18.430 --> 00:22:20.920 assume we want the whole entire table. 00:22:20.920 --> 00:22:25.030 And then this comma here at the end, which has step or one, 00:22:25.030 --> 00:22:28.600 you can pass in a step at the end of a for loop as a third argument, 00:22:28.600 --> 00:22:33.710 and that will be however much increments or decrements the loop that you're in. 00:22:33.710 --> 00:22:35.000 So by default, just one. 00:22:35.000 --> 00:22:37.540 We go one, then we go to two, then we go to three. 00:22:37.540 --> 00:22:39.260 But you can set it to negative one. 00:22:39.260 --> 00:22:42.370 And so if you say four i gets three to one minus one, 00:22:42.370 --> 00:22:44.260 you'll go three, two, one. 00:22:44.260 --> 00:22:50.170 And you can't do normally a step, which is what we do here. 00:22:50.170 --> 00:22:52.100 No, you can do a step, but you can't slice, 00:22:52.100 --> 00:22:58.250 which is why we have here sliced at number of slice plus one gets table i, 00:22:58.250 --> 00:22:59.750 and then eventually we return slice. 00:22:59.750 --> 00:23:04.690 So it just returns just a segment of whatever table we're in. 00:23:04.690 --> 00:23:07.270 And then the important function here that we're actually 00:23:07.270 --> 00:23:11.440 going to use in our application, we're going to generate quads paddles. 00:23:11.440 --> 00:23:15.070 And so this takes X and Y, 0 and 64. 00:23:15.070 --> 00:23:19.630 And if we look back at our paddles here, we 00:23:19.630 --> 00:23:22.100 can see that we have various different sizes. 00:23:22.100 --> 00:23:25.690 So we have a small one, a medium one, a large one, and then a really large one. 00:23:25.690 --> 00:23:30.130 So if we want to get every single paddle in our sprite sheet, 00:23:30.130 --> 00:23:33.809 small, medium, large, giant, notice that we have four blocks 00:23:33.809 --> 00:23:36.350 and within each of those blocks we have four different sizes. 00:23:36.350 --> 00:23:38.200 So we can just iterate over this four times 00:23:38.200 --> 00:23:41.005 and then just define whatever the size of this rect 00:23:41.005 --> 00:23:43.270 is, that rect, that rect, and that rect. 00:23:43.270 --> 00:23:45.850 And we'll see the math for it here. 00:23:45.850 --> 00:23:50.920 If I go zero to three, for i, get zero till three. 00:23:50.920 --> 00:23:53.890 We're going to go ahead because that will give us four. 00:23:53.890 --> 00:23:57.400 So that's how many times we want to iterate over the sprite sheet 00:23:57.400 --> 00:24:00.010 to get the separate quads. 00:24:00.010 --> 00:24:01.990 We'll get the smallest one. 00:24:01.990 --> 00:24:03.160 So quads counter. 00:24:03.160 --> 00:24:05.080 We initialize counter to one. 00:24:05.080 --> 00:24:11.980 Gets love.graphics, that new quad at X, Y, with the 32 and 16. 00:24:11.980 --> 00:24:16.960 Oh, and X and Y default at 0 and 64 here because the note-- 00:24:16.960 --> 00:24:21.880 recall that these are all 16 tall here. 00:24:21.880 --> 00:24:24.520 So we're starting Y at 64 so that we start right here. 00:24:24.520 --> 00:24:28.792 And we're starting X at zero because it's on the left side. 00:24:28.792 --> 00:24:29.500 So we'll do that. 00:24:29.500 --> 00:24:31.780 We'll increment counter. 00:24:31.780 --> 00:24:34.150 Get it at 32 wide by 16 tall. 00:24:34.150 --> 00:24:36.430 Those are the actual dimensions of the smallest one. 00:24:36.430 --> 00:24:40.240 The same exact logic applies for medium and for large. 00:24:40.240 --> 00:24:44.560 Only that we're adding 32 and then we're making it size 64, 00:24:44.560 --> 00:24:49.420 and then we're adding 96 to X at size 96 because they're getting wider, 00:24:49.420 --> 00:24:52.510 but they're also offsetting more to the right. 00:24:52.510 --> 00:24:56.830 And then the last bit is pretty much the same thing as before, except now we're 00:24:56.830 --> 00:25:01.960 going Y plus 16 back to X because we've gone down a row in our sprite sheet 00:25:01.960 --> 00:25:06.580 The paddle width at that point is 128, but still 16 pixels. 00:25:06.580 --> 00:25:13.280 And then here at the bottom because we want to do this four times, 00:25:13.280 --> 00:25:16.030 we want to go through the chunks are effectively 32 pixels 00:25:16.030 --> 00:25:19.350 because we're going 16, 16, 16, 16. 00:25:19.350 --> 00:25:23.080 We're going to add 32 to Y and then go to the next set of four paddles. 00:25:23.080 --> 00:25:26.350 So this is how we're effectively getting all of the paddle sprites, 00:25:26.350 --> 00:25:30.900 and they;re going to be stored one through X where I believe X is 16. 00:25:30.900 --> 00:25:34.180 So we'll have 16 quads defined in our sprite sheet 00:25:34.180 --> 00:25:36.560 thereafter that we can then return. 00:25:36.560 --> 00:25:43.640 So I'm going to go back to main.lua now on line 64. 00:25:43.640 --> 00:25:47.321 Here we have a new global table called gframes. 00:25:47.321 --> 00:25:49.820 We'll be able to access this anywhere we want to draw stuff. 00:25:49.820 --> 00:25:52.010 And it's just the same thing that we just saw. 00:25:52.010 --> 00:25:55.430 Generate quads paddles, and we just pass it in our main texture. 00:25:55.430 --> 00:25:57.350 And our main texture is this. 00:25:57.350 --> 00:25:59.695 This is what our main texture looks like. 00:25:59.695 --> 00:26:01.070 And then we're going to index it. 00:26:01.070 --> 00:26:04.610 We're going to say it gets the key paddles, 00:26:04.610 --> 00:26:10.400 because in that particular table was just the quads for our paddles. 00:26:10.400 --> 00:26:14.330 So in the future, we just need to call love.graphics.drawtexture and then 00:26:14.330 --> 00:26:19.640 index into gframes paddles at whatever paddle we want. 00:26:19.640 --> 00:26:24.440 And that's how we can keep track of what we want to draw paddle wise. 00:26:24.440 --> 00:26:28.010 And in this particular demo we have a new paddle class 00:26:28.010 --> 00:26:30.380 because paddle is a thing in our game. 00:26:30.380 --> 00:26:33.390 We can represent it as sort of a class or an object. 00:26:33.390 --> 00:26:36.599 So we'll define a class for it. 00:26:36.599 --> 00:26:38.140 Everything is pretty simple thus far. 00:26:38.140 --> 00:26:41.680 Gets an X and a Y. Dx is zero with height. 00:26:41.680 --> 00:26:42.470 Skin. 00:26:42.470 --> 00:26:45.800 The skin is going to be what color it is. 00:26:45.800 --> 00:26:47.300 We need to keep track of that. 00:26:47.300 --> 00:26:51.440 And then the size, because size will be how we sort of offset 00:26:51.440 --> 00:26:56.540 into our paddles, our quads, because the sizes are small, medium, large, giant. 00:26:56.540 --> 00:26:58.306 One, two, three, four times four. 00:26:58.306 --> 00:27:00.680 So one, two, three, four for the first set and then five, 00:27:00.680 --> 00:27:03.079 six, seven, eight for the second set. 00:27:03.079 --> 00:27:04.370 Those are all sort of by color. 00:27:04.370 --> 00:27:07.140 So we can just multiply skin times-- 00:27:07.140 --> 00:27:12.050 or we can multiply whatever our size is by skin 00:27:12.050 --> 00:27:15.710 and that will give us the current frame, the current quad that we want in order 00:27:15.710 --> 00:27:18.770 to draw to the screen. 00:27:18.770 --> 00:27:21.040 And then on line seven-- 00:27:21.040 --> 00:27:23.000 so this is keyboard input here. 00:27:23.000 --> 00:27:24.340 Stuff that we've seen before. 00:27:24.340 --> 00:27:28.570 If we're pressing left or right, then the paddles should move. 00:27:28.570 --> 00:27:31.167 Dx should be set left or right. 00:27:31.167 --> 00:27:32.000 We want to clamp it. 00:27:32.000 --> 00:27:33.770 We saw this, we've seen this as well. 00:27:33.770 --> 00:27:37.820 Clamp the input to the left and the right side of the screen. 00:27:37.820 --> 00:27:42.050 If the dx is less than zero, do math.max and math.min otherwise 00:27:42.050 --> 00:27:43.841 if we're moving to the right. 00:27:43.841 --> 00:27:46.340 And then here, this is actually where we tie it all together 00:27:46.340 --> 00:27:49.680 and we actually use the quads to draw something onto the screen. 00:27:49.680 --> 00:27:54.860 So we're calling love.graphics.draw just our texture, our main texture. 00:27:54.860 --> 00:28:01.280 And then gframes at paddles at our current size, which is two. 00:28:01.280 --> 00:28:07.760 We want to by default have the medium size plus four times whatever 00:28:07.760 --> 00:28:10.080 our skin is, minus one. 00:28:10.080 --> 00:28:18.720 So if our skin is one, which is the blue skin, we won't add anything to it. 00:28:18.720 --> 00:28:20.370 It'll just be four times zero. 00:28:20.370 --> 00:28:22.790 But if we have the next one, it'll be two minus one, 00:28:22.790 --> 00:28:24.710 so we'll end up adding four to that. 00:28:24.710 --> 00:28:28.920 And because we're adding four to it times whatever that skin is, 00:28:28.920 --> 00:28:32.120 it will just basically put us four quads in, 00:28:32.120 --> 00:28:36.860 which is the next, the exact same paddle, but the next color. 00:28:36.860 --> 00:28:41.360 And then lastly what we'll look at here is the PlayState. 00:28:41.360 --> 00:28:44.777 So we had just the StartState before, but now we 00:28:44.777 --> 00:28:47.110 want to actually test to make sure we can draw a paddle, 00:28:47.110 --> 00:28:48.450 move it around the screen. 00:28:48.450 --> 00:28:50.630 So we're going to implement a simple PlayState here. 00:28:50.630 --> 00:28:53.840 So on line 20, we're just calling self.paddle gets paddle. 00:28:53.840 --> 00:28:56.220 We're initializing a new paddle object. 00:28:56.220 --> 00:28:58.970 And then we're keeping track of also this 00:28:58.970 --> 00:29:01.610 is a simple, like, pause demonstration. 00:29:01.610 --> 00:29:06.010 If self.paused, then-- actually yeah. 00:29:06.010 --> 00:29:06.950 Did I say self.paused? 00:29:06.950 --> 00:29:07.240 I did. 00:29:07.240 --> 00:29:07.490 OK. 00:29:07.490 --> 00:29:09.200 I just don't initialize it to anything. 00:29:09.200 --> 00:29:15.440 I should have set self.paused to false here. 00:29:15.440 --> 00:29:19.790 If self.paused, we're going to test to see whether we're pressing space, 00:29:19.790 --> 00:29:22.910 and if we are, unpause it. 00:29:22.910 --> 00:29:26.360 Otherwise, basically just do the same exact thing in reverse. 00:29:26.360 --> 00:29:31.580 If we press space, pause the game, play a sound, et cetera. 00:29:31.580 --> 00:29:34.250 Here on line 39, we're just going to call update on the paddle. 00:29:34.250 --> 00:29:37.049 Which, just remember, test for left or right input. 00:29:37.049 --> 00:29:38.840 Here we want to be able to escape the game, 00:29:38.840 --> 00:29:41.060 so we're going to have a handler for escape. 00:29:41.060 --> 00:29:45.350 Render the paddle on lines 47, which will do the love.graphics.draw 00:29:45.350 --> 00:29:47.480 with a quad as we saw before, but it'll use 00:29:47.480 --> 00:29:52.160 the skin and the size of that paddle to index into the quads tile sheet 00:29:52.160 --> 00:29:54.170 appropriately. 00:29:54.170 --> 00:29:57.170 And then here if we're paused, let's just draw 00:29:57.170 --> 00:30:00.110 some text in the middle of the screen that just says Pause. 00:30:00.110 --> 00:30:01.400 And we use the large font. 00:30:01.400 --> 00:30:04.316 So we can go ahead and demo this now and see everything come together. 00:30:08.190 --> 00:30:11.260 We have as before our StartState. 00:30:11.260 --> 00:30:13.711 But if we press Enter, now we go to our PlayState 00:30:13.711 --> 00:30:15.960 and we just have a paddle at the bottom of the screen. 00:30:15.960 --> 00:30:18.720 It's size two, skin one. 00:30:18.720 --> 00:30:19.740 Just the blue skin. 00:30:19.740 --> 00:30:21.895 And we can move it left or right like that. 00:30:21.895 --> 00:30:24.270 And if it hits the left side of the screen, it will stop. 00:30:24.270 --> 00:30:27.130 And if it hits the right side of the screen, it will stop as well. 00:30:27.130 --> 00:30:30.180 So we've made progress, but this is one of the fundamental things 00:30:30.180 --> 00:30:35.220 I'd like to showcase today is just, like, using quads and categorizing 00:30:35.220 --> 00:30:38.160 them, organizing them, and being able to draw 00:30:38.160 --> 00:30:43.860 your assets from a large compiled image rather than keep track 00:30:43.860 --> 00:30:45.742 of however many images it would take. 00:30:45.742 --> 00:30:47.700 And you have to name all of them and sort them. 00:30:47.700 --> 00:30:49.270 It would just be a big pain. 00:30:49.270 --> 00:30:49.770 So yeah. 00:30:49.770 --> 00:30:52.500 Definitely going forward when you have more than one sprite, 00:30:52.500 --> 00:30:55.200 you want to sort of put it together in one sheet, 00:30:55.200 --> 00:30:57.900 and that's how we can accomplish that. 00:30:57.900 --> 00:31:00.030 But we don't have bricks, and this is probably 00:31:00.030 --> 00:31:05.380 the other big main component of Breakout besides the paddle and the ball. 00:31:05.380 --> 00:31:08.710 We want to have bricks that we can actually hit and aim for on the screen. 00:31:08.710 --> 00:31:10.450 So this update will address that. 00:31:10.450 --> 00:31:16.430 So let's go ahead and take a look at Breakout2 in main.lua. 00:31:16.430 --> 00:31:17.670 I'm going to open it up here. 00:31:21.740 --> 00:31:29.600 On line 66, you can see we have a new table in our gframes. 00:31:29.600 --> 00:31:32.266 Because we had one just for paddles, we took out 00:31:32.266 --> 00:31:33.890 just the paddles from our sprite sheet. 00:31:33.890 --> 00:31:36.056 We're going to do the same thing for just the balls. 00:31:36.056 --> 00:31:38.070 So we're going to look at-- if we look here, 00:31:38.070 --> 00:31:43.100 we can see that the balls sort of come after all of the bricks here 00:31:43.100 --> 00:31:46.070 and they're just laid out in eight pixels wide by eight 00:31:46.070 --> 00:31:48.500 pixels tall increments here. 00:31:48.500 --> 00:31:52.070 So four pixels to one brick, four balls to one brick, 00:31:52.070 --> 00:31:56.610 two balls to one horizontally, and then two balls vertically. 00:31:56.610 --> 00:32:02.360 And so what we'll end up doing is just a simple function in our util 00:32:02.360 --> 00:32:04.530 that takes a look at that. 00:32:04.530 --> 00:32:08.570 So let's go ahead and take a look at our util.lua, which we've made changes to. 00:32:14.120 --> 00:32:17.810 And so what this is going to do is sort of do the same thing 00:32:17.810 --> 00:32:20.540 that we did before. 00:32:20.540 --> 00:32:21.290 It has to iterate. 00:32:21.290 --> 00:32:23.850 So notice we have two rows of balls. 00:32:23.850 --> 00:32:26.030 We have these four and we have these three. 00:32:26.030 --> 00:32:27.606 So we want to iterate four times. 00:32:27.606 --> 00:32:29.480 You want to find whatever the offset is here, 00:32:29.480 --> 00:32:35.270 the X and Y. So it looks like three times 32 and then three times 16. 00:32:35.270 --> 00:32:36.860 So 96 by-- 00:32:36.860 --> 00:32:37.550 I can't do math. 00:32:37.550 --> 00:32:39.110 Whatever 16 times three is. 00:32:39.110 --> 00:32:40.700 And then we'll end up 48. 00:32:40.700 --> 00:32:42.839 And then we'll have-- 00:32:42.839 --> 00:32:43.880 which is what we do here. 00:32:43.880 --> 00:32:46.187 So we have two iterations. 00:32:46.187 --> 00:32:48.020 So a four loop that goes from zero to three. 00:32:48.020 --> 00:32:51.030 So the top row, the four. 00:32:51.030 --> 00:32:52.990 We'll set a counter to one here. 00:32:52.990 --> 00:32:54.641 And notice also 96 and 48. 00:32:54.641 --> 00:32:56.390 That's the X and the Y that we're setting. 00:32:56.390 --> 00:33:01.510 That's where the offset is for the individual ball sprites. 00:33:01.510 --> 00:33:03.710 Quads at counter gets-- 00:33:03.710 --> 00:33:05.330 and notice also quads is a table. 00:33:05.330 --> 00:33:06.650 We're going to return this. 00:33:06.650 --> 00:33:10.630 Quads at counter gets love.graphics.newquad at X, Y. 00:33:10.630 --> 00:33:12.230 Eight pixels wide, eight pixels tall. 00:33:12.230 --> 00:33:13.580 That's how large the balls are. 00:33:13.580 --> 00:33:17.390 And then we're going to add eight to it because we're going to the right. 00:33:17.390 --> 00:33:20.960 So this iteration just goes left to right. 00:33:20.960 --> 00:33:25.460 And then here we're going to do basically X being set to 96 00:33:25.460 --> 00:33:27.900 and then Y to 56. 00:33:27.900 --> 00:33:30.350 And then because we were editing X directly in here, 00:33:30.350 --> 00:33:33.380 we want to reset X back to 96, but then also add 00:33:33.380 --> 00:33:36.320 the eight pixels so that we have the start for the next row 00:33:36.320 --> 00:33:38.840 vertically, so at Y 56. 00:33:38.840 --> 00:33:40.730 Do the exact same thing here, but only do it 00:33:40.730 --> 00:33:43.010 three times because recall there is four balls on top 00:33:43.010 --> 00:33:44.720 and then three balls on bottom. 00:33:44.720 --> 00:33:46.280 And then return it at the very end. 00:33:46.280 --> 00:33:49.250 And so now we have just an individual table. 00:33:49.250 --> 00:33:51.620 We don't need to keep like one monstrous table of quads, 00:33:51.620 --> 00:33:53.360 which I find sort of disorganized. 00:33:53.360 --> 00:33:57.950 We can just have a table of frames for the paddles, 00:33:57.950 --> 00:34:00.110 and the balls, and the bricks as we'll see. 00:34:02.930 --> 00:34:05.311 Actually, I have it up here I think. 00:34:05.311 --> 00:34:05.810 Maybe not. 00:34:08.900 --> 00:34:12.480 So in ball-- oh, actually, hold on. 00:34:12.480 --> 00:34:12.980 Sorry. 00:34:12.980 --> 00:34:14.030 So we were looking at-- 00:34:17.540 --> 00:34:19.080 I skipped over this one on accident. 00:34:19.080 --> 00:34:19.955 So the bounce update. 00:34:19.955 --> 00:34:22.370 So everything I just said is relevant, but I accidentally 00:34:22.370 --> 00:34:23.840 hit that right two times. 00:34:23.840 --> 00:34:27.620 We want to go to the bounce update because this is slightly simpler. 00:34:27.620 --> 00:34:30.699 So we were just talking about the ball, which is perfect. 00:34:30.699 --> 00:34:33.949 So we're going to take the ball and then we're going to add that to the scene, 00:34:33.949 --> 00:34:36.980 and we're just going to implement bouncing off the walls. 00:34:36.980 --> 00:34:39.380 So actually, pretty identical to the code 00:34:39.380 --> 00:34:43.040 we saw for Pong where you just detect whether the ball has 00:34:43.040 --> 00:34:45.985 gone past the left, right, or top edge of the screen. 00:34:45.985 --> 00:34:48.860 In this case, it will also allow us to go to the bottom of the screen 00:34:48.860 --> 00:34:51.290 and we'll also implement colliding with the paddle 00:34:51.290 --> 00:34:54.690 so then get a sense of the actual game play and what that feels like. 00:34:54.690 --> 00:34:57.920 So everything is currently current. 00:34:57.920 --> 00:34:59.300 So we're going to go-- 00:34:59.300 --> 00:35:03.712 after talking about the function to actually get the individual ball 00:35:03.712 --> 00:35:05.420 quads out of the spreadsheet, we're going 00:35:05.420 --> 00:35:10.140 to look at the ball class which is going to allow us to spawn them in our scene. 00:35:10.140 --> 00:35:12.310 So a ball takes a width and height of eight. 00:35:12.310 --> 00:35:13.490 No velocity. 00:35:13.490 --> 00:35:17.600 But we're going to allow ourselves to initialize the ball with the skin, 00:35:17.600 --> 00:35:21.010 and we'll see this later just as a cutesy little thing to you 00:35:21.010 --> 00:35:24.550 use the actual individual sprites rather than just one constant sprite. 00:35:24.550 --> 00:35:27.740 We're just going to give it a random number between one and seven 00:35:27.740 --> 00:35:30.110 because there are seven quads. 00:35:30.110 --> 00:35:35.300 And then we'll just use gframes balls and math dot random number 00:35:35.300 --> 00:35:38.657 to get the actual ball spread that we want. 00:35:38.657 --> 00:35:40.865 And so we have a simple collides function within ball 00:35:40.865 --> 00:35:45.290 that would allow us to check to see whether we've collided with something 00:35:45.290 --> 00:35:47.180 that has a X, Y width and a height. 00:35:47.180 --> 00:35:54.620 So it's a simple A, B collision detection. 00:35:54.620 --> 00:35:56.609 And then here we have reset. 00:35:56.609 --> 00:35:58.400 Just resets it to the middle of the screen. 00:35:58.400 --> 00:35:59.840 Update applies velocity. 00:35:59.840 --> 00:36:02.420 Stuff we've already seen. 00:36:02.420 --> 00:36:05.010 This is where we actually implement bouncing off the walls. 00:36:05.010 --> 00:36:07.370 So if X is less than or equal to zero, greater than 00:36:07.370 --> 00:36:11.180 or equal to virtual width minus eight, or less than or equal to zero, 00:36:11.180 --> 00:36:14.660 this should be where we reverse the velocity. 00:36:14.660 --> 00:36:18.380 In the case of it bouncing off the left side, we want to reverse the X velocity 00:36:18.380 --> 00:36:19.370 but keep it going up. 00:36:19.370 --> 00:36:21.200 If it hits the top, then we want to reverse 00:36:21.200 --> 00:36:24.200 the Y velocity to keep it moving in whatever direction it was moving. 00:36:24.200 --> 00:36:27.620 And same thing with the right hand wall. 00:36:27.620 --> 00:36:29.150 And then play a wall hit sound. 00:36:29.150 --> 00:36:31.790 And we're incorporating the sounds sort of as we go today 00:36:31.790 --> 00:36:33.420 just because they're so simple. 00:36:33.420 --> 00:36:35.360 And it's also kind of nice just to have a little bit of feedback 00:36:35.360 --> 00:36:37.130 when you're actually endpoint of the game. 00:36:37.130 --> 00:36:39.290 And the exact same code is here for drawing. 00:36:39.290 --> 00:36:43.040 So we have main texture, but now we're using gframes balls, 00:36:43.040 --> 00:36:46.100 and then we're indexing that at self.skin. 00:36:46.100 --> 00:36:48.179 And recall that we just set self.skin in here. 00:36:48.179 --> 00:36:49.970 So all we need to do to just make it random 00:36:49.970 --> 00:36:53.660 is just wherever we create a new ball, just give it a math.random7, 00:36:53.660 --> 00:36:57.170 and then that will index into that quads table 00:36:57.170 --> 00:37:01.380 so we can draw a different ball texture each time. 00:37:01.380 --> 00:37:03.170 And so let's go ahead and see-- 00:37:03.170 --> 00:37:03.890 oh, actually, no. 00:37:03.890 --> 00:37:06.320 And one last thing we need to look at is the PlayState 00:37:06.320 --> 00:37:09.471 has a little bit of new code as well. 00:37:09.471 --> 00:37:11.970 We're going to spawn a ball, so this is where we do it here. 00:37:11.970 --> 00:37:14.886 I'm not doing it random, but I could do it random here if I wanted to. 00:37:14.886 --> 00:37:17.297 I could math.random7, and every time we boot up 00:37:17.297 --> 00:37:19.630 the game it's going to be a different color because it's 00:37:19.630 --> 00:37:22.646 going to be a different skin. 00:37:22.646 --> 00:37:23.770 We need to update the ball. 00:37:23.770 --> 00:37:26.500 So on line 50 we just update it like we do the paddle. 00:37:26.500 --> 00:37:29.560 And then on line 52, we're just testing to see 00:37:29.560 --> 00:37:32.661 whether it collides with the paddle because we're using just simple A, 00:37:32.661 --> 00:37:36.790 A, B, B. If it collides with the paddle, we can assume it was coming down. 00:37:36.790 --> 00:37:38.540 We can just reverse as delta Y. 00:37:38.540 --> 00:37:43.030 Now, does anybody know what might be a current issue 00:37:43.030 --> 00:37:46.760 with the current implementation of this function? 00:37:46.760 --> 00:37:48.880 Particularly with this line. 00:37:51.766 --> 00:38:01.284 AUDIENCE: [INAUDIBLE] 00:38:01.284 --> 00:38:02.200 COLTON OGDEN: It will. 00:38:02.200 --> 00:38:03.283 You're on the right track. 00:38:03.283 --> 00:38:06.880 The answer was, if the ball is coming from the side, 00:38:06.880 --> 00:38:11.082 it won't necessarily be bounced back up in the right Y direction. 00:38:11.082 --> 00:38:13.540 If it's coming from the side, it will always, in this case, 00:38:13.540 --> 00:38:15.170 be coming from up above. 00:38:15.170 --> 00:38:17.890 So it always still be reversing in the right delta Y. 00:38:17.890 --> 00:38:22.090 But what's going to happen if it comes in at an angle 00:38:22.090 --> 00:38:28.840 and then isn't basically reset? 00:38:28.840 --> 00:38:31.990 Like right now if it comes at an angle and it 00:38:31.990 --> 00:38:36.640 gets caught-- let's say it's like below the top edge of the paddle. 00:38:36.640 --> 00:38:37.840 AUDIENCE: [INAUDIBLE] 00:38:37.840 --> 00:38:40.352 COLTON OGDEN: You're going to get an infinite collision 00:38:40.352 --> 00:38:42.310 loop because we're not resetting it's position, 00:38:42.310 --> 00:38:44.230 we're only updating its velocity. 00:38:44.230 --> 00:38:46.630 If it comes in at the right angle from the side, 00:38:46.630 --> 00:38:50.101 it's going to get stuck inside the paddle 00:38:50.101 --> 00:38:52.600 and then it's going to cause a little bit of funky behavior. 00:38:52.600 --> 00:38:55.690 I'll try and see if I can make that happen in my demonstration here. 00:38:55.690 --> 00:38:58.300 But that's the gist of all of these updates. 00:38:58.300 --> 00:39:02.620 So if we go to Start, we can see immediately we have a ball. 00:39:02.620 --> 00:39:05.999 And when it hits the sides or the top, it bounces accordingly. 00:39:05.999 --> 00:39:06.790 It hits the paddle. 00:39:06.790 --> 00:39:11.420 So when it comes in from the top flush on the top, it flips the Y velocity. 00:39:11.420 --> 00:39:14.620 Let's see if I can get it at an angle here. 00:39:14.620 --> 00:39:15.520 There it is. 00:39:15.520 --> 00:39:17.150 It'll get stuck. 00:39:17.150 --> 00:39:22.120 And so whenever you sort of do A, A, B, B collision detection, 00:39:22.120 --> 00:39:25.810 just remember to always reset the position of whatever it 00:39:25.810 --> 00:39:29.020 is that collided that's moving so that it doesn't clip 00:39:29.020 --> 00:39:31.611 and get stuck inside of something else over and over again. 00:39:31.611 --> 00:39:32.110 Yes. 00:39:32.110 --> 00:39:42.190 AUDIENCE: [INAUDIBLE] 00:39:42.190 --> 00:39:45.610 COLTON OGDEN: The question is I'm always doing love space dot, 00:39:45.610 --> 00:39:50.050 and as opposed to just running things from using 00:39:50.050 --> 00:39:55.030 the complete path of whatever the file is, in order to do that-- so are you 00:39:55.030 --> 00:39:56.320 on a Mac or a Windows machine? 00:39:56.320 --> 00:39:57.450 AUDIENCE: [INAUDIBLE] 00:39:57.450 --> 00:40:01.780 COLTON OGDEN: So on a Windows machine it is a little trickier, 00:40:01.780 --> 00:40:07.390 but I've found a really nice sort of plug-in for VS Code. 00:40:07.390 --> 00:40:11.650 So if you're VS Code, which is the editor that I use, it has plug-ins 00:40:11.650 --> 00:40:14.830 and one of the plug-ins that you can download is for Love2D 00:40:14.830 --> 00:40:19.210 and it has a config where if you just press Alt L, 00:40:19.210 --> 00:40:22.240 it will run whatever directory you're currently in, 00:40:22.240 --> 00:40:24.260 whatever project you're currently in. 00:40:24.260 --> 00:40:25.780 It will call Love. 00:40:25.780 --> 00:40:27.470 It adds it to your path for you. 00:40:27.470 --> 00:40:31.810 So download the Love2D plug-in on VS code if you want that to work. 00:40:31.810 --> 00:40:34.990 I'm on a Mac, so I can edit what's called my batch profile, 00:40:34.990 --> 00:40:40.970 and alias Love to its complete path in my file system. 00:40:40.970 --> 00:40:43.880 And you can do the same thing with-- 00:40:43.880 --> 00:40:48.280 I don't know how it would work with Windows in terms of aliasing, 00:40:48.280 --> 00:40:52.540 but it's essentially the same thing as typing out the entire path to Love, 00:40:52.540 --> 00:40:55.120 but only I'm changing it to another word. 00:40:55.120 --> 00:40:56.790 I'm changing it to Love. 00:40:56.790 --> 00:40:59.680 So I'm setting Love equals to application slash 00:40:59.680 --> 00:41:02.870 love.app/content/resources et cetera. 00:41:02.870 --> 00:41:04.150 So good question. 00:41:04.150 --> 00:41:05.560 I would download on Windows. 00:41:05.560 --> 00:41:07.720 I'm a big fan of VS Code and the Love2D plug-in. 00:41:07.720 --> 00:41:09.427 I would recommend looking into that. 00:41:09.427 --> 00:41:12.010 And I'm sure there are other plug-ins, and there's a page also 00:41:12.010 --> 00:41:13.611 on the website-- 00:41:13.611 --> 00:41:15.360 I don't have a browser open at the moment. 00:41:15.360 --> 00:41:19.330 But on the wiki, you can look at the Getting Started page. 00:41:19.330 --> 00:41:21.606 I believe it's like love2d.com/wiki/gettingstarted. 00:41:21.606 --> 00:41:23.980 They have a bunch of instructions for different operating 00:41:23.980 --> 00:41:26.521 systems and different text editors that allow you to get sort 00:41:26.521 --> 00:41:29.750 of a more efficient workflow going. 00:41:29.750 --> 00:41:34.590 So any other questions? 00:41:34.590 --> 00:41:35.580 All right. 00:41:35.580 --> 00:41:38.130 So we did the bounce update. 00:41:38.130 --> 00:41:40.780 Now we can finally edit the bricks. 00:41:40.780 --> 00:41:42.420 Add in the bricks, I should say. 00:41:42.420 --> 00:41:44.880 So these are pretty simple. 00:41:44.880 --> 00:41:47.264 So we're going to take a look at it. 00:41:47.264 --> 00:41:50.430 And right now we're not going to do any sort of fancy procedural generation, 00:41:50.430 --> 00:41:52.980 we're just going to get some bricks on the screen. 00:41:52.980 --> 00:41:55.509 Just some easy bricks. 00:41:55.509 --> 00:41:58.050 Or rather, we will get some very basic procedural generation, 00:41:58.050 --> 00:41:59.758 but not to the level that we'll see soon. 00:41:59.758 --> 00:42:01.660 We'll see that very soon. 00:42:01.660 --> 00:42:02.160 OK. 00:42:02.160 --> 00:42:05.940 So I'm going to go into my main.lua here. 00:42:05.940 --> 00:42:08.400 I'm going to go into the Breakout3. 00:42:12.250 --> 00:42:15.310 And same thing that we did before on line 67, 00:42:15.310 --> 00:42:19.920 we just have a new bricks table in our gframes. 00:42:19.920 --> 00:42:21.640 And it just generate quads bricks. 00:42:21.640 --> 00:42:25.840 We call from util.lua, so we can look at that really quick as well. 00:42:25.840 --> 00:42:28.270 This one's actually really easy. 00:42:28.270 --> 00:42:30.310 Sourceutil.lua. 00:42:30.310 --> 00:42:33.790 Because they start at the very top of the screen, 00:42:33.790 --> 00:42:37.720 we can assume that-- we could effectively 00:42:37.720 --> 00:42:40.270 treat this whole thing as if it were just these 00:42:40.270 --> 00:42:43.720 and just generate quads at a constant width and height 00:42:43.720 --> 00:42:47.530 because, effectively, we only need a subset of the frames that's generating. 00:42:47.530 --> 00:42:52.130 Because it's generating them this way, top to bottom, left to right, 00:42:52.130 --> 00:42:55.500 we can just grab all the way up to here using 00:42:55.500 --> 00:42:59.020 table.slice, which we saw before, and not 00:42:59.020 --> 00:43:01.720 worry about indexing into any weird, like, 00:43:01.720 --> 00:43:04.450 having any constants X and Y that we need to index with in order 00:43:04.450 --> 00:43:05.620 to get an offset. 00:43:05.620 --> 00:43:07.730 We can just do a very simple-- 00:43:07.730 --> 00:43:15.460 if we go down to line 57, generate quads bricks, it just does a table.slice. 00:43:15.460 --> 00:43:19.400 And so within that, we're going to generate quads atlas 32,16. 00:43:19.400 --> 00:43:23.440 So this is going to have the effect of dividing up our sprite sheet by 32 00:43:23.440 --> 00:43:24.730 by 16 pieces. 00:43:24.730 --> 00:43:27.010 It's going to generate all of these just fine, 00:43:27.010 --> 00:43:30.076 but then it's going to have quads here, here, here, here, here 00:43:30.076 --> 00:43:32.200 that don't line up with the quads that you see here 00:43:32.200 --> 00:43:36.889 because it's just blindly assuming that all of the sprites in that sheet 00:43:36.889 --> 00:43:38.930 are the same size because that's all we're doing. 00:43:38.930 --> 00:43:41.410 We're just calling generate quads, which if you recall, 00:43:41.410 --> 00:43:46.300 just generates a fixed size width and height throughout our entire atlas, 00:43:46.300 --> 00:43:49.290 which is great for a lot of sheets that are symmetrical, 00:43:49.290 --> 00:43:52.270 but there are cases where we have, like, for example here, where 00:43:52.270 --> 00:43:53.929 our spreadsheet is asymmetrical. 00:43:53.929 --> 00:43:55.720 We have paddles of differing sizes, we have 00:43:55.720 --> 00:43:58.011 the balls which are eight by eight, we have the bricks, 00:43:58.011 --> 00:44:01.840 we have the other power ups at the bottom. 00:44:01.840 --> 00:44:05.920 But the generate quads bricks takes in that table 00:44:05.920 --> 00:44:08.769 that we're generating, which is going to be a bunch of frames 00:44:08.769 --> 00:44:09.560 that we don't want. 00:44:09.560 --> 00:44:11.650 Many of them clipped, half clipped. 00:44:11.650 --> 00:44:14.120 And then we're just going to take it from one to 21. 00:44:14.120 --> 00:44:18.500 And when we do that, one to 21 is effectively-- that's how many of these 00:44:18.500 --> 00:44:19.000 there are. 00:44:19.000 --> 00:44:22.270 So 18 and then one, two, three. 00:44:22.270 --> 00:44:24.434 So from one to 21, all of those. 00:44:24.434 --> 00:44:25.600 That will be all the bricks. 00:44:25.600 --> 00:44:28.270 We can throw away all the rest of the quads 00:44:28.270 --> 00:44:33.310 and just blindly assume that they're all the same size. 00:44:36.450 --> 00:44:41.090 So any questions on how quads or how any of these tables are working? 00:44:41.090 --> 00:44:41.590 OK. 00:44:44.075 --> 00:44:45.200 So we're going to go ahead. 00:44:45.200 --> 00:44:47.200 We have a new class now, brick.lua. 00:44:49.750 --> 00:44:52.600 So simple building blocks. 00:44:52.600 --> 00:44:57.160 In brick.lua on line 30, we have a flag called in play. 00:44:57.160 --> 00:44:58.574 self.inplay gets true. 00:44:58.574 --> 00:45:00.490 And so we're just going to use this to render. 00:45:00.490 --> 00:45:02.800 We're just going to say, if it's in play, render it. 00:45:02.800 --> 00:45:04.160 If it's not, don't render it. 00:45:04.160 --> 00:45:04.869 It's that simple. 00:45:04.869 --> 00:45:08.035 That way we don't have to worry about object deallocation or anything fancy. 00:45:08.035 --> 00:45:10.270 We have all of our bricks and whether it's in play 00:45:10.270 --> 00:45:12.310 or not, render it or perform update logic. 00:45:12.310 --> 00:45:14.980 And if it's not in play, just pretend it doesn't exist. 00:45:14.980 --> 00:45:17.560 Just ignore it. 00:45:17.560 --> 00:45:21.220 We're only going to have like 30 or I don't know how many, 00:45:21.220 --> 00:45:26.860 13 max by four bricks in our scene at once, so worrying about freeing memory 00:45:26.860 --> 00:45:28.070 isn't really an issue. 00:45:28.070 --> 00:45:34.150 But if you have a million different things getting generated all the time, 00:45:34.150 --> 00:45:37.480 having simple in play is false might not always 00:45:37.480 --> 00:45:40.730 be viable because you need to store all that memory for all those objects. 00:45:40.730 --> 00:45:44.920 So just a shortcut here, but not necessarily best 00:45:44.920 --> 00:45:47.080 practice for very large games. 00:45:47.080 --> 00:45:49.870 But certainly great and simple for small games. 00:45:53.340 --> 00:45:56.350 On line 37, we define a function called brick hit. 00:45:56.350 --> 00:46:00.520 And all this does is just play a sound effect and set in play to false. 00:46:00.520 --> 00:46:03.430 And so all we're going to do is just check 00:46:03.430 --> 00:46:06.940 to see whether there's a collision and then just call this hit function, 00:46:06.940 --> 00:46:10.990 play a sound, and then just pretend it doesn't exist anymore. 00:46:10.990 --> 00:46:15.010 And then render, all render does is if it's in play, check the in play flag, 00:46:15.010 --> 00:46:23.425 draw main at bricks or using our bricks table here that we created. 00:46:23.425 --> 00:46:25.300 And then we're going to start at one and then 00:46:25.300 --> 00:46:30.020 we're going to index it based on our color minus one times four, 00:46:30.020 --> 00:46:31.630 and then we're going to add it's tier. 00:46:31.630 --> 00:46:36.490 So there are, if you recall, one, two, three, four, 00:46:36.490 --> 00:46:41.380 five colors and four tiers. 00:46:41.380 --> 00:46:44.380 And so what we're going to do is we're going to jump between the colors. 00:46:44.380 --> 00:46:49.840 So we'll go value one, value two, value three, value four, value five. 00:46:49.840 --> 00:46:52.450 That will be our first five or I guess six. 00:46:52.450 --> 00:46:55.690 That will be our first six bricks. 00:46:55.690 --> 00:46:59.099 And then we're going to go one, two, three-- or we're going to add, 00:46:59.099 --> 00:47:00.640 we're going to have a tier basically. 00:47:00.640 --> 00:47:04.030 It'll be one, two, three, or four. 00:47:04.030 --> 00:47:07.780 And if it's at tier one, then we can just add-- 00:47:07.780 --> 00:47:10.000 basically to index into whatever tier we're on, 00:47:10.000 --> 00:47:13.870 we just need to add tier minus one to whatever our index is. 00:47:13.870 --> 00:47:17.050 So here if our tier is one, then we just want to render this block. 00:47:17.050 --> 00:47:18.550 We don't want to go to the next one. 00:47:18.550 --> 00:47:20.299 So we're just going to say tier minus one. 00:47:20.299 --> 00:47:22.250 We're going to add-- 00:47:22.250 --> 00:47:23.922 so one minus one is zero. 00:47:23.922 --> 00:47:25.630 So we're going at zero to this, get this. 00:47:25.630 --> 00:47:29.860 But if tiers two, we'll add one, and two, and three. 00:47:29.860 --> 00:47:34.750 And then we just multiply whatever brick we want by our color. 00:47:34.750 --> 00:47:40.540 Multiply it by four to get an offset for whatever our actual color is. 00:47:40.540 --> 00:47:43.210 So we take our color, figure out where on the sheet it is, 00:47:43.210 --> 00:47:46.180 and then just add our tier to it in order to index 00:47:46.180 --> 00:47:49.580 into our spreadsheet accordingly. 00:47:49.580 --> 00:47:52.900 And so that's what the math here is doing. 00:47:52.900 --> 00:47:56.285 And if we go back to our PlayState-- 00:48:02.050 --> 00:48:04.880 and I'm going to start moving a little bit faster 00:48:04.880 --> 00:48:06.590 just so we can keep caught up. 00:48:06.590 --> 00:48:10.700 But in our PlayState, one thing that we notice here, 00:48:10.700 --> 00:48:13.820 we have a new class called level maker that we're seeing, 00:48:13.820 --> 00:48:15.530 which was a function called createmap. 00:48:15.530 --> 00:48:17.930 We're going to take out all the logic for generating our levels 00:48:17.930 --> 00:48:19.370 and we're just going to put it in one place. 00:48:19.370 --> 00:48:20.911 We're going to call that level maker. 00:48:20.911 --> 00:48:24.170 Rather than in our different states that maybe 00:48:24.170 --> 00:48:26.630 generate the bricks like the PlayState or I 00:48:26.630 --> 00:48:30.680 guess it would be the ServeState, VictoryState, 00:48:30.680 --> 00:48:33.470 I guess, rather than generating all the bricks 00:48:33.470 --> 00:48:37.610 in that state within it's innate code, let's just make a level maker 00:48:37.610 --> 00:48:41.750 and we can just say, OK, set bricks to levelmaker.createmap, 00:48:41.750 --> 00:48:46.840 which will return a table of bricks. 00:48:46.840 --> 00:48:51.110 Same-- excuse me-- logic as we saw before. 00:48:51.110 --> 00:48:53.510 In this case, we're just going to iterate for k brick 00:48:53.510 --> 00:48:57.050 in pairs of self.bricks. 00:48:57.050 --> 00:49:00.710 If the brick's in play and it collides, if the ball collides with it, 00:49:00.710 --> 00:49:02.730 then hit it, which will set it not into play. 00:49:02.730 --> 00:49:08.030 So simple A, A, B, B. 00:49:08.030 --> 00:49:10.550 And then lastly, we have our render logic here, 00:49:10.550 --> 00:49:13.805 which is going to take that bricks table and just iterate over it. 00:49:13.805 --> 00:49:15.680 And the last thing we should probably look at 00:49:15.680 --> 00:49:18.921 is the actual level maker itself, which in this case is very simple, 00:49:18.921 --> 00:49:21.920 but we'll see it gets a little bit more complicated later when we do it. 00:49:21.920 --> 00:49:26.516 When we have a more elaborate procedural generation approach to our levels. 00:49:26.516 --> 00:49:29.390 But right now, we're just going to say set two random variables here. 00:49:29.390 --> 00:49:31.040 Number of rows and columns. 00:49:31.040 --> 00:49:37.670 And then for every row or for basically every row and every column, 00:49:37.670 --> 00:49:40.280 create a new brick. 00:49:40.280 --> 00:49:41.810 And then there's some math here. 00:49:41.810 --> 00:49:43.934 I'm going to kind of skim over it, but basically it 00:49:43.934 --> 00:49:46.970 calculates where the brick is and then gives us 00:49:46.970 --> 00:49:50.510 eight pixels of padding on either side. 00:49:50.510 --> 00:49:53.145 And then based on how many it is, it needs to center all 00:49:53.145 --> 00:49:55.520 the bricks and shift them by a certain amount to the left 00:49:55.520 --> 00:49:57.080 and then start drawing all of them. 00:49:57.080 --> 00:49:59.270 And that's essentially what this code does here. 00:49:59.270 --> 00:50:01.580 So calculate the center. 00:50:01.580 --> 00:50:03.680 I wrote it out in comments here, but I'm going 00:50:03.680 --> 00:50:05.600 just kind of glaze over it for now. 00:50:05.600 --> 00:50:08.330 But effectively, center all the bricks. 00:50:08.330 --> 00:50:12.380 Basically calculate what offset on the X-axis you need to put all of them 00:50:12.380 --> 00:50:16.850 so that they appear centered, and then you're going to draw them all out. 00:50:16.850 --> 00:50:20.630 And then that's it for the level maker class. 00:50:20.630 --> 00:50:24.800 So simply number of rows and columns, and then fill a table with bricks 00:50:24.800 --> 00:50:28.392 but set their X equal to however much we need to center all 00:50:28.392 --> 00:50:29.850 of them when they're all drawn out. 00:50:29.850 --> 00:50:32.850 So we need to figure, we need to basically take in our number of columns 00:50:32.850 --> 00:50:34.430 into account when we do that. 00:50:34.430 --> 00:50:43.560 And then if we go into Breakout3 and run that, we have bricks. 00:50:43.560 --> 00:50:47.130 They're getting collided with, and as soon as they get hit, 00:50:47.130 --> 00:50:51.390 collided are in play on each of those bricks gets set to false 00:50:51.390 --> 00:50:52.770 and they no longer get rendered. 00:50:52.770 --> 00:50:55.830 And they no longer get updated in terms of collision. 00:50:55.830 --> 00:50:58.470 Now, we still have the issue with the ball not getting reset. 00:50:58.470 --> 00:50:59.140 We'll fix that. 00:50:59.140 --> 00:51:01.034 That's an easy fix. 00:51:01.034 --> 00:51:02.200 But we're coming a long way. 00:51:02.200 --> 00:51:04.880 We have things moving at quite a pace. 00:51:04.880 --> 00:51:08.039 I'm going to go ahead and move to the next bit of code here. 00:51:08.039 --> 00:51:09.330 So this is another bit of code. 00:51:09.330 --> 00:51:12.450 I'm going to sort of glaze over a little bit of the details here. 00:51:12.450 --> 00:51:16.380 But at a high level what we need to do is it's one thing 00:51:16.380 --> 00:51:18.840 to detect that we've collided with a brick, 00:51:18.840 --> 00:51:21.990 but in Breakout, the ball bounces off of the brick 00:51:21.990 --> 00:51:23.580 depending on which side it hits. 00:51:23.580 --> 00:51:27.780 And we don't know this necessarily just based off of the collision. 00:51:27.780 --> 00:51:29.920 We just know whether the collision is true or not. 00:51:29.920 --> 00:51:34.260 We don't know where it came from and how much it collided with. 00:51:34.260 --> 00:51:37.860 And then we're also going to fix our paddles so that rather than-- because 00:51:37.860 --> 00:51:40.790 currently all it does is just negate whatever the Y velocity is, 00:51:40.790 --> 00:51:43.230 but we want to add a little bit more variety 00:51:43.230 --> 00:51:47.895 to how we end up sort of ricocheting the ball off the paddle when we play 00:51:47.895 --> 00:51:50.190 so that we can sort of strategize a little bit, 00:51:50.190 --> 00:51:51.550 give ourselves a little bit of game play. 00:51:51.550 --> 00:51:54.300 So if we are moving to the right and we hit the right edge of the puddle 00:51:54.300 --> 00:51:56.800 with the ball, it should probably go in a sharper direction. 00:51:56.800 --> 00:51:58.330 Same thing with the left side. 00:51:58.330 --> 00:52:01.320 And we can effectively do that by taking the middle, 00:52:01.320 --> 00:52:03.630 figuring out how far away from the center it is, 00:52:03.630 --> 00:52:08.430 and then just amplifying our delta X in the negative or positive direction 00:52:08.430 --> 00:52:09.510 based off of that. 00:52:09.510 --> 00:52:13.390 And that has the effect of causing that to happen. 00:52:13.390 --> 00:52:18.090 So here we can see we have the ball sort of coming at the paddle, 00:52:18.090 --> 00:52:21.270 and let's pretend that the paddle is moving to the left. 00:52:21.270 --> 00:52:24.930 In this case, however far away the ball is from the center, 00:52:24.930 --> 00:52:27.390 we want to scale that by some amount and then 00:52:27.390 --> 00:52:32.970 end up making that our negative delta X, because that's effectively 00:52:32.970 --> 00:52:34.650 how the game normally works. 00:52:34.650 --> 00:52:36.692 If you move the paddle to the left or the right, 00:52:36.692 --> 00:52:39.150 hit it on a corner or something, gives it that sharp angle. 00:52:39.150 --> 00:52:41.108 And that's effectively what the sharp angle is. 00:52:41.108 --> 00:52:44.350 It's just a strong delta X, and it gets amplified the larger this is. 00:52:44.350 --> 00:52:47.040 So just basically take this, multiply it by some amount, 00:52:47.040 --> 00:52:50.220 and then make it negative or positive on your dx. 00:52:50.220 --> 00:52:56.730 That's your sort of paddle collision V2. 00:52:56.730 --> 00:53:00.630 Brick collision is a little bit-- 00:53:00.630 --> 00:53:03.340 it's pretty simple, but it's a little bit more complicated. 00:53:03.340 --> 00:53:06.900 Basically what we need to do is just check and see which edge of the ball 00:53:06.900 --> 00:53:09.630 isn't inside the brick. 00:53:09.630 --> 00:53:12.000 And so if the left edge of the-- and we can also sort of 00:53:12.000 --> 00:53:13.290 simplify this a little bit. 00:53:13.290 --> 00:53:15.500 If the left-- as you see here by the pseudocode-- 00:53:15.500 --> 00:53:20.140 if the left edge of the ball is outside the brick and the dx is positive, 00:53:20.140 --> 00:53:22.440 then we can say, oh, we can basically assume 00:53:22.440 --> 00:53:25.590 we've come in from the left side, so we should probably 00:53:25.590 --> 00:53:30.120 go in the opposite Y direction on the left side. 00:53:30.120 --> 00:53:35.310 Or sorry, we should go in the same Y direction, but negate our delta 00:53:35.310 --> 00:53:38.730 X. Because we're coming in from the left, 00:53:38.730 --> 00:53:42.907 the left side is outside the brick, so bounce it back. 00:53:42.907 --> 00:53:44.490 And the same thing for the right edge. 00:53:44.490 --> 00:53:48.300 And we only do this test, the left edge of the ball, if dx is positive. 00:53:48.300 --> 00:53:50.130 Because if dx is negative, there's no way 00:53:50.130 --> 00:53:52.590 the ball's colliding with the left side of our brick. 00:53:52.590 --> 00:53:54.379 So we can shortcut that effectively. 00:53:54.379 --> 00:53:56.670 We do the same exact logic here, just on the right edge 00:53:56.670 --> 00:53:59.460 of the brick instead of the left edge. 00:53:59.460 --> 00:54:01.230 And then if none of those hold true, we're 00:54:01.230 --> 00:54:06.570 going to see if the top edge of the ball is above the top edge of the brick. 00:54:06.570 --> 00:54:09.290 And if that's the case, we know that we've hit from the top. 00:54:09.290 --> 00:54:10.740 We can trigger a top collision. 00:54:10.740 --> 00:54:12.600 And if none of those have held true, we know 00:54:12.600 --> 00:54:14.970 that we have had a collision of some kind, 00:54:14.970 --> 00:54:18.440 we can just register a bottom collision. 00:54:18.440 --> 00:54:23.130 And so this is a simple version of this sort 00:54:23.130 --> 00:54:25.290 of way of doing Breakout collision. 00:54:25.290 --> 00:54:27.604 It has a few faults when it comes to corners, 00:54:27.604 --> 00:54:29.520 sometimes corners can be a little bit finicky, 00:54:29.520 --> 00:54:31.680 but I would say it works 99% of the time. 00:54:31.680 --> 00:54:37.170 For a much more robust and a better example, I would look at this URL 00:54:37.170 --> 00:54:40.050 here because he also goes into a full sort of breakdown 00:54:40.050 --> 00:54:43.230 of how he would implement arkanoid, which is the same thing effectively 00:54:43.230 --> 00:54:46.050 as Breakout if you just want an alternative look at it. 00:54:46.050 --> 00:54:50.220 But basically, his solution involved taking how much the X and the Y 00:54:50.220 --> 00:54:54.570 differed on different points of the bricks relative to the ball. 00:54:54.570 --> 00:54:58.680 And I believe he also kept the ball as an actual ball with a center point, 00:54:58.680 --> 00:55:04.420 even though he rendered it as a rectangle. 00:55:04.420 --> 00:55:05.820 So it's a little bit more robust. 00:55:05.820 --> 00:55:09.090 I decided to implement it a simpler way, which I'll showcase, 00:55:09.090 --> 00:55:12.240 which is the way that I demonstrated because it worked well 00:55:12.240 --> 00:55:15.600 and it wasn't too much code to sort of look over. 00:55:15.600 --> 00:55:17.580 But I do encourage you to take a look at that. 00:55:17.580 --> 00:55:19.860 We're going to look at our PlayState now in Breakout4. 00:55:26.880 --> 00:55:28.965 And in our PlayState, we're going to see-- 00:55:32.560 --> 00:55:33.460 sorry. 00:55:33.460 --> 00:55:35.470 Line 65. 00:55:35.470 --> 00:55:43.830 So this is the actual paddle code for influencing the ball's delta X. 00:55:43.830 --> 00:55:53.700 So basically, if the ball.x is less than the paddle.x plus it's width divided 00:55:53.700 --> 00:55:58.500 by two, so basically on the left side of the paddle, 00:55:58.500 --> 00:56:02.040 and the paddle's delta X is less than zero, which means it's moving left-- 00:56:02.040 --> 00:56:04.956 because we don't really want to necessarily influence it if we're just 00:56:04.956 --> 00:56:05.889 standing still-- 00:56:05.889 --> 00:56:07.680 we're going to do what I described earlier. 00:56:07.680 --> 00:56:11.490 We're going to give it some scaler, like some start off value. 00:56:11.490 --> 00:56:15.210 In this case, negative 50 is just sort of seeding this, giving it 00:56:15.210 --> 00:56:18.090 some sort of initial value. 00:56:18.090 --> 00:56:24.470 And then we're just going to subtract the ball's X from the middle point. 00:56:24.470 --> 00:56:26.892 This being the middle point of the paddle 00:56:26.892 --> 00:56:28.350 And then just multiply it by eight. 00:56:28.350 --> 00:56:31.980 So whatever the difference is between the ball's X and the middle 00:56:31.980 --> 00:56:34.080 of the paddle, multiply it by eight. 00:56:34.080 --> 00:56:36.720 Add it to negative 50 and then negate that. 00:56:36.720 --> 00:56:40.560 Also negate that whole value so that the whole entire value becomes negative. 00:56:40.560 --> 00:56:44.880 And we, therefore, get a sharper delta X depending on which angle 00:56:44.880 --> 00:56:46.980 it's coming at, and also how fast-- 00:56:46.980 --> 00:56:50.250 or not how fast, but whether or not we are moving left. 00:56:50.250 --> 00:56:53.970 And it's the exact same thing on the right side. 00:56:53.970 --> 00:56:58.050 Only because we're taking this math, this self.paddle.x 00:56:58.050 --> 00:57:02.655 plus self.paddle.width divided by two minus the ball.x, 00:57:02.655 --> 00:57:05.320 the ball.x isn't going to be greater than that point. 00:57:05.320 --> 00:57:07.360 So this value is actually going to be negative. 00:57:07.360 --> 00:57:09.660 So we're going to just make it positive with math.abs. 00:57:09.660 --> 00:57:10.730 So absolute value. 00:57:10.730 --> 00:57:12.330 Just a lua function. 00:57:12.330 --> 00:57:14.820 So the absolute value of the difference between the ball's 00:57:14.820 --> 00:57:19.530 X and the middle point times eight, add it to 50, 00:57:19.530 --> 00:57:23.580 and that'll give us a positive value that scales depending on whether or not 00:57:23.580 --> 00:57:27.030 we've hit the middle of the, we've hit the right edge of the paddle 00:57:27.030 --> 00:57:28.700 and are moving to the right. 00:57:28.700 --> 00:57:33.450 And so that's, in a nutshell, how we get that collision to work with the paddle 00:57:33.450 --> 00:57:36.630 and how we can tweak delta X to be scaled a little bit more 00:57:36.630 --> 00:57:40.920 than just a constant, you know, negative or whatever 00:57:40.920 --> 00:57:43.020 it's current X was, but negative dy. 00:57:43.020 --> 00:57:45.900 A little bit more complicated. 00:57:45.900 --> 00:57:50.610 And then the actual collision code for the bricks 00:57:50.610 --> 00:57:53.110 themselves is going to take place in a for loop here. 00:57:53.110 --> 00:57:58.270 So if it's in play, if the ball collides with it, hit it. 00:57:58.270 --> 00:57:59.640 So I added plus two. 00:57:59.640 --> 00:58:05.520 So the gist of the math is if ball.x is less than brick.x 00:58:05.520 --> 00:58:09.300 and the ball is moving to the right, self.ball.dx is greater than zero, 00:58:09.300 --> 00:58:10.590 then flip it's X velocity. 00:58:10.590 --> 00:58:11.850 So bounce it to the left. 00:58:11.850 --> 00:58:13.560 That's what this check is. 00:58:13.560 --> 00:58:15.785 But it plays a little bit rough with corners 00:58:15.785 --> 00:58:17.910 because you could theoretically get into a position 00:58:17.910 --> 00:58:21.900 where you come in at an angle and it's intersecting 00:58:21.900 --> 00:58:29.190 with the paddle in two positions, both on top and the left 00:58:29.190 --> 00:58:30.930 or on bottom and the left. 00:58:30.930 --> 00:58:36.780 So in that case, adding two sort of prioritizes the Y being hit. 00:58:36.780 --> 00:58:42.690 So it basically takes the check from the exposition of the ball to the X plus 2. 00:58:42.690 --> 00:58:48.210 And so it ends up fixing the corners a little bit, but the gist of it 00:58:48.210 --> 00:58:50.810 is just check to see if the ball.x is less than the brick.x. 00:58:50.810 --> 00:58:55.680 And if it is and we've detected a collision, we can bounce it. 00:58:55.680 --> 00:58:59.670 There are some subtle corner case bugs without adding this plus two, 00:58:59.670 --> 00:59:01.500 so we add that. 00:59:01.500 --> 00:59:04.190 And then flip the velocity here. 00:59:04.190 --> 00:59:05.156 Oh, this shift here. 00:59:05.156 --> 00:59:07.530 This is what we were talking about earlier with make sure 00:59:07.530 --> 00:59:09.570 when you do a collision, shift whatever is 00:59:09.570 --> 00:59:12.870 moving outside the boundaries of whatever you're colliding with. 00:59:12.870 --> 00:59:19.010 So self.ball.x gets brick.x minus eight because the ball is eight pixels wide. 00:59:19.010 --> 00:59:23.070 It should actually be self.ball.width for a better style, 00:59:23.070 --> 00:59:26.651 but that's essentially what it translates out to. 00:59:26.651 --> 00:59:27.900 Same thing for the right edge. 00:59:27.900 --> 00:59:29.850 The plus six because it's on the right side. 00:59:29.850 --> 00:59:34.080 So it's effectively the same thing as minus two if we're on the left side. 00:59:34.080 --> 00:59:39.540 Just a sort of fixes corners, weird issues with corners. 00:59:39.540 --> 00:59:44.970 But check in to see if basically the ball plus its height minus two 00:59:44.970 --> 00:59:51.630 is greater than the brick plus X plus brick.width, which it means, 00:59:51.630 --> 00:59:55.560 oh, we've collided with the right edge of the screen, of the brick. 00:59:55.560 --> 00:59:58.530 And then if the Y is less than the brick.y, 00:59:58.530 --> 01:00:00.980 then we've collided with the top of the brick, 01:00:00.980 --> 01:00:03.420 and otherwise, we've collided with the bottom. 01:00:03.420 --> 01:00:06.780 And with the top and the bottom, just do the same thing we did with delta X, 01:00:06.780 --> 01:00:09.030 but do it with delta Y, but you're still resetting it. 01:00:09.030 --> 01:00:11.580 So ball.y gets brick.y minus eight. 01:00:11.580 --> 01:00:17.130 Ball.y gets brick.y plus 16 because the paddle or the individual bricks 01:00:17.130 --> 01:00:21.120 are 16 pixels tall. 01:00:21.120 --> 01:00:24.810 That's the gist of the collision detection. 01:00:24.810 --> 01:00:27.840 And then if we actually-- oh, and one other thing that I ended up 01:00:27.840 --> 01:00:30.600 putting here just to make it a little bit more interesting, 01:00:30.600 --> 01:00:33.960 and this also ties into more complicated collision detection. 01:00:33.960 --> 01:00:38.015 If your velocity is too fast, a lot of the time it'll skip through objects, 01:00:38.015 --> 01:00:40.890 and then that causes a lot of problems with these collision detection 01:00:40.890 --> 01:00:44.520 functions that normally are very sort of mathematically correct 01:00:44.520 --> 01:00:45.480 and they work well. 01:00:45.480 --> 01:00:47.370 They don't work well when it skips over what 01:00:47.370 --> 01:00:49.260 you're trying to actually collide with. 01:00:49.260 --> 01:00:53.970 So a solution to that, which was beyond the scope of this example but something 01:00:53.970 --> 01:00:57.600 we're thinking about, is perhaps stepping backwards a certain amount 01:00:57.600 --> 01:00:59.790 of time, a certain amount of pixels. 01:00:59.790 --> 01:01:04.380 Perhaps maybe start at where you where your ball was on one particular, 01:01:04.380 --> 01:01:08.100 on the last frame, and then just add its width and height to itself 01:01:08.100 --> 01:01:12.480 until it collides with something, until it reaches whatever its current delta X 01:01:12.480 --> 01:01:15.380 or delta Y plus its position is. 01:01:15.380 --> 01:01:16.380 That's one way to do it. 01:01:16.380 --> 01:01:18.817 Sort of just adding a bunch of invisible-- 01:01:18.817 --> 01:01:21.650 whatever you're colliding with or whatever you're using to collide-- 01:01:21.650 --> 01:01:26.250 add a bunch of invisible those to bridge the gap and check into if any of those 01:01:26.250 --> 01:01:27.900 hold true for a collision. 01:01:27.900 --> 01:01:32.010 A little bit more computationally expensive, but a lot more accurate 01:01:32.010 --> 01:01:35.107 in terms of the physics. 01:01:35.107 --> 01:01:36.940 And aside from that, everything is the same. 01:01:36.940 --> 01:01:40.326 So if you look at the code in Breakout4-- 01:01:40.326 --> 01:01:42.450 and I'm going to go a little bit faster henceforth. 01:01:42.450 --> 01:01:44.491 That's probably the meatiest part of the program. 01:01:47.050 --> 01:01:49.080 We get collisions. 01:01:49.080 --> 01:01:54.102 And then I'll try and get a strong angle so I can demo the-- 01:01:54.102 --> 01:01:54.810 that didn't work. 01:01:54.810 --> 01:01:55.890 That actually gave a weaker angle. 01:01:55.890 --> 01:01:59.250 So if you do this and you do it close to the center, it has the opposite effect. 01:01:59.250 --> 01:01:59.958 But there you go. 01:01:59.958 --> 01:02:01.200 That's a sharper angle. 01:02:01.200 --> 01:02:05.550 So now you can actually influence the ball in a little bit more 01:02:05.550 --> 01:02:07.770 of a personable way. 01:02:07.770 --> 01:02:11.070 You know, not just have it be a flat delta Y gets negative-- 01:02:11.070 --> 01:02:14.220 or get negative delta Y effectively. 01:02:14.220 --> 01:02:19.920 So any questions on sort of how the gist of all of that works? 01:02:23.730 --> 01:02:24.230 OK. 01:02:24.230 --> 01:02:25.040 Perfect. 01:02:25.040 --> 01:02:28.395 So now we're going to get into a little bit more of some fun stuff. 01:02:28.395 --> 01:02:30.770 We'll do a couple more examples, then we'll take a break. 01:02:30.770 --> 01:02:32.360 So this is the hearts update. 01:02:32.360 --> 01:02:35.690 So notice that the very top of the screen, as I've demonstrated in these 01:02:35.690 --> 01:02:38.480 slides, we have just a few hearts. 01:02:38.480 --> 01:02:39.382 One of them is empty. 01:02:39.382 --> 01:02:40.340 We showed this earlier. 01:02:40.340 --> 01:02:43.660 And then we have a game over screen, which is our final score. 01:02:43.660 --> 01:02:46.909 So I'm going to go ahead and we're just going to look at the code a little bit 01:02:46.909 --> 01:02:50.656 faster now since a lot of the stuff is fairly straightforward. 01:02:50.656 --> 01:02:52.280 I'm going to go ahead and open up the-- 01:02:52.280 --> 01:02:54.780 I'm going to make sure I'm in the right folder first of all. 01:02:54.780 --> 01:02:55.760 Breakout5. 01:02:55.760 --> 01:02:57.320 And then in the-- 01:03:00.860 --> 01:03:07.690 so one other thing we're going to start doing is-- 01:03:07.690 --> 01:03:08.917 I mentioned this earlier. 01:03:08.917 --> 01:03:10.750 And it's going to be it's going to hold true 01:03:10.750 --> 01:03:13.570 for any of the sort of state transformations 01:03:13.570 --> 01:03:15.460 that take place going forward. 01:03:15.460 --> 01:03:17.440 Rather than keep global variables, we're going 01:03:17.440 --> 01:03:20.500 to sort of do away with that idea outside of the asset tables 01:03:20.500 --> 01:03:23.210 that we have just because those are kind of an exception 01:03:23.210 --> 01:03:26.560 and they could reasonably be put into a separate class called the resource 01:03:26.560 --> 01:03:27.550 manager. 01:03:27.550 --> 01:03:35.570 We're going to start passing in what is basically our current app state, 01:03:35.570 --> 01:03:37.750 or at least the variables that make sense. 01:03:37.750 --> 01:03:40.900 And this is a common paradigm in web development with React as well. 01:03:40.900 --> 01:03:44.560 But basically, everything that we need to be preserve state to state, 01:03:44.560 --> 01:03:46.310 rather than just keeping global variables, 01:03:46.310 --> 01:03:49.210 let's pass them between the states because the state machine allows 01:03:49.210 --> 01:03:52.120 us to do that in the change function. 01:03:52.120 --> 01:03:55.240 And then whatever that state is in it's enter function, 01:03:55.240 --> 01:03:56.950 it'll have access to that and it can just 01:03:56.950 --> 01:04:01.090 set those values to self dot whatever and use them. 01:04:01.090 --> 01:04:02.770 But we no longer have global variables. 01:04:02.770 --> 01:04:03.770 We're just saying, here. 01:04:03.770 --> 01:04:06.962 Here's the values that are important for you to continue on. 01:04:06.962 --> 01:04:09.670 And then that state will take its values and go to the next state 01:04:09.670 --> 01:04:14.170 and say, oh, OK, here are the values that you need to function. 01:04:14.170 --> 01:04:16.240 Like the serve, play, and all those states 01:04:16.240 --> 01:04:17.890 that have the core game play involved will probably 01:04:17.890 --> 01:04:19.540 need to maintain a reference to like the paddle, 01:04:19.540 --> 01:04:21.676 and to the score, the amount of health we have. 01:04:21.676 --> 01:04:24.550 But when we get to the end, for example, and then we no longer really 01:04:24.550 --> 01:04:27.849 need a paddle, we no longer really need bricks or anything like that, 01:04:27.849 --> 01:04:30.640 we just need to know what our high score is so that we can enter it 01:04:30.640 --> 01:04:33.430 into our high score list, all we really need 01:04:33.430 --> 01:04:37.030 to do is just pass in the high score state entry or just our high score, 01:04:37.030 --> 01:04:39.340 and that's it. 01:04:39.340 --> 01:04:41.170 So it encapsulates all of our data. 01:04:41.170 --> 01:04:45.730 And at a glance, we can sort of see what we need to pass between the states 01:04:45.730 --> 01:04:48.730 and what's going to be relevant at a glance as well. 01:04:48.730 --> 01:04:52.340 It just clean things up quite a bit. 01:04:52.340 --> 01:04:55.120 So that's what we're doing now on line 35. 01:04:55.120 --> 01:04:58.600 And henceforth, we will do this in every state as we see, 01:04:58.600 --> 01:05:02.500 but I'm going to sort of glaze over it in the future. 01:05:02.500 --> 01:05:03.610 We have a ServeState now. 01:05:03.610 --> 01:05:08.257 So a ServeState, this is very identical to what we did in Pong. 01:05:08.257 --> 01:05:10.090 So we just wait for the user to press Space. 01:05:10.090 --> 01:05:16.810 They can move around and then when they do press Enter, 01:05:16.810 --> 01:05:20.380 basically the ball starts moving. 01:05:20.380 --> 01:05:23.110 And then we change the PlayState here using 01:05:23.110 --> 01:05:25.644 the current values that are necessary. 01:05:25.644 --> 01:05:27.310 Paddle, bricks, health, score, and ball. 01:05:27.310 --> 01:05:30.220 Those are basically the fundamental variables 01:05:30.220 --> 01:05:35.980 that we need in order to keep track of our GameState. 01:05:35.980 --> 01:05:38.890 So we have a ServeState, it will wait for us to press Enter. 01:05:38.890 --> 01:05:50.820 And then our main.lua, we have a new hearts table. 01:05:50.820 --> 01:05:55.350 And then on line 208, because we're going 01:05:55.350 --> 01:05:59.280 to need the ability to render health and render our score across several states, 01:05:59.280 --> 01:06:02.100 Play, Serve, Victory, Game Over-- 01:06:02.100 --> 01:06:04.290 actually not Game Over, but the three before that. 01:06:04.290 --> 01:06:06.081 We don't want to duplicate those behaviors, 01:06:06.081 --> 01:06:08.730 so I'm just calling a function called Render Health, which 01:06:08.730 --> 01:06:10.830 just takes in whatever health is and then 01:06:10.830 --> 01:06:13.800 we just set an X to virtual width minus 100. 01:06:13.800 --> 01:06:17.070 And then for however many health we have, draw a heart 01:06:17.070 --> 01:06:20.430 from the hearts sprite sheet, which I separated the hearts out 01:06:20.430 --> 01:06:23.670 into a smaller image so you can just split them on like eight by eight 01:06:23.670 --> 01:06:25.590 or whatever it is. 01:06:25.590 --> 01:06:27.774 But just draw those and then add 11 to X, 01:06:27.774 --> 01:06:30.690 and just keep going until we've drawn out however many hearts we have. 01:06:30.690 --> 01:06:32.220 That will draw full hearts. 01:06:32.220 --> 01:06:35.350 And then three minus health will give us however many health we're missing. 01:06:35.350 --> 01:06:38.099 So if we took a point of damage, this is going to be equal to one. 01:06:38.099 --> 01:06:41.736 So then it'll draw one empty heart after that or it'll draw two empty hearts. 01:06:41.736 --> 01:06:44.610 So draw however many full hearts we have, then draw the empty hearts. 01:06:44.610 --> 01:06:48.540 And those are two separate sprites that we get from the image. 01:06:48.540 --> 01:06:51.150 And that will have the effect of drawing our health. 01:06:51.150 --> 01:06:56.530 And then our score is simply, it takes a score variable that we pass into here. 01:06:56.530 --> 01:06:58.530 And also note that the render health [INAUDIBLE] 01:06:58.530 --> 01:07:00.450 and health variable and pass into it here. 01:07:00.450 --> 01:07:17.670 And so in our PlayState, we are calling both of these functions on line 135. 01:07:17.670 --> 01:07:20.702 Well, on line 135, we are calculating whether we 01:07:20.702 --> 01:07:23.910 go below the edge of the screen, which is another important part of the game. 01:07:23.910 --> 01:07:26.520 Obviously, we need to detect when we've lost health. 01:07:26.520 --> 01:07:28.080 So it's as simple as this. 01:07:28.080 --> 01:07:31.140 If it's greater than the virtual height, decrement health by one. 01:07:31.140 --> 01:07:34.200 If it's equal to zero, change to Game Over. 01:07:34.200 --> 01:07:35.467 Else change to the ServeState. 01:07:35.467 --> 01:07:38.550 And note that we're passing in all these variables to and from our states. 01:07:38.550 --> 01:07:39.716 The ones that are important. 01:07:39.716 --> 01:07:41.970 Game Over just needs score, but Serve needs whatever 01:07:41.970 --> 01:07:43.740 variables we were already using. 01:07:46.620 --> 01:07:50.100 And then down here we're calling render score and render health, 01:07:50.100 --> 01:07:54.900 and then the GameOverState is simply-- 01:07:54.900 --> 01:07:58.890 because it takes in score from the parameters list, 01:07:58.890 --> 01:08:03.330 just wait for keyboard input to go back to the start and then render game over, 01:08:03.330 --> 01:08:04.530 here's your score. 01:08:04.530 --> 01:08:06.930 It's self.score, and then that's it. 01:08:06.930 --> 01:08:07.620 Very simple. 01:08:07.620 --> 01:08:08.636 Very simple state. 01:08:08.636 --> 01:08:09.510 AUDIENCE: [INAUDIBLE] 01:08:09.510 --> 01:08:10.301 COLTON OGDEN: Sure. 01:08:10.301 --> 01:08:15.899 AUDIENCE: [INAUDIBLE] 01:08:15.899 --> 01:08:18.149 COLTON OGDEN: The question was, do any of these states 01:08:18.149 --> 01:08:19.740 have access to their parent file? 01:08:19.740 --> 01:08:27.180 AUDIENCE: [INAUDIBLE] 01:08:27.180 --> 01:08:30.120 COLTON OGDEN: Is everything in main.lua global functions? 01:08:30.120 --> 01:08:30.810 Yes. 01:08:30.810 --> 01:08:31.935 Functions that you declare. 01:08:31.935 --> 01:08:35.220 Anything that's basically not specified as local that you define in main.lua 01:08:35.220 --> 01:08:39.163 will be accessible anywhere in your application, including functions. 01:08:39.163 --> 01:08:40.715 AUDIENCE: [INAUDIBLE] 01:08:40.715 --> 01:08:42.840 COLTON OGDEN: You don't have to-- the question was, 01:08:42.840 --> 01:08:44.215 do you have to declare as public? 01:08:44.215 --> 01:08:45.725 No, there is no notion of public. 01:08:45.725 --> 01:08:48.720 In lua, anything that does not have a local specifier 01:08:48.720 --> 01:08:51.330 is assumed global, even if it's in a nested scope. 01:08:51.330 --> 01:08:54.930 So you could have a for loop, you could have several nested for loops 01:08:54.930 --> 01:08:59.310 and declare some variable without local, that variable can be accessed anywhere 01:08:59.310 --> 01:09:01.050 above it or outside of it. 01:09:01.050 --> 01:09:03.060 So it's pretty important to use local variables 01:09:03.060 --> 01:09:05.970 when you're not explicitly allocating something as global 01:09:05.970 --> 01:09:09.600 just to avoid the bug of for nested loops 01:09:09.600 --> 01:09:14.729 and you have some variable name like hello and you use it somewhere else. 01:09:14.729 --> 01:09:16.960 Good questions though. 01:09:16.960 --> 01:09:17.460 So yeah. 01:09:17.460 --> 01:09:18.569 We have a bunch of states now. 01:09:18.569 --> 01:09:20.360 We have a GameOverState, a PlayState, we're 01:09:20.360 --> 01:09:22.359 rendering our score, rendering our health. 01:09:22.359 --> 01:09:24.239 If we go and take a look at Breakout5-- 01:09:29.805 --> 01:09:33.630 is it a different window? 01:09:33.630 --> 01:09:35.660 There we go. 01:09:35.660 --> 01:09:37.500 We can see hearts at the top. 01:09:37.500 --> 01:09:38.279 Score zero. 01:09:41.600 --> 01:09:44.960 Oh, and I forgot to mention the part where we actually add score now. 01:09:44.960 --> 01:09:54.440 So the bricks themselves in their on hit, or I should say in the PlayState, 01:09:54.440 --> 01:10:00.221 on line 81 when we detect a hit, we're just adding 10 to the score for now. 01:10:00.221 --> 01:10:01.970 But later on, we'll do a calculation where 01:10:01.970 --> 01:10:04.200 we take tier and color into consideration 01:10:04.200 --> 01:10:09.500 and then perform arithmetic on that to get our total score for each ball hit. 01:10:09.500 --> 01:10:14.810 But yeah, we have our health, we have our score. 01:10:14.810 --> 01:10:16.569 And then once we take enough damage, we'll 01:10:16.569 --> 01:10:18.110 end up going to the Game Over screen. 01:10:18.110 --> 01:10:20.420 The Game Over screen will go back to our Start screen. 01:10:20.420 --> 01:10:21.780 So making progress. 01:10:21.780 --> 01:10:24.440 And then probably my favorite of the updates 01:10:24.440 --> 01:10:27.650 before we take a short break is the pretty colors update. 01:10:27.650 --> 01:10:31.200 So what this does is clearly we can have-- 01:10:31.200 --> 01:10:32.480 we've updated our level maker. 01:10:32.480 --> 01:10:37.160 So rather than just having a bunch of very static bricks, 01:10:37.160 --> 01:10:41.270 we end up doing a little bit more complicated procedural generation. 01:10:41.270 --> 01:10:42.740 It's not complicated though. 01:10:42.740 --> 01:10:48.290 Just in levelmaker.lua in Breakout6, we have a few different constants here. 01:10:48.290 --> 01:10:51.499 So solid, alternate, skip, or none. 01:10:51.499 --> 01:10:53.290 Actually, I don't think I use skip or none. 01:10:53.290 --> 01:10:54.860 Just solid or alternate basically. 01:10:54.860 --> 01:10:56.180 We have flags now. 01:10:56.180 --> 01:10:58.100 So number of columns. 01:10:58.100 --> 01:11:02.480 And we ensure that it's odd because even columns with generating patterns 01:11:02.480 --> 01:11:04.340 leads to asymmetry. 01:11:04.340 --> 01:11:07.679 So make sure the number of columns is odd. 01:11:07.679 --> 01:11:10.470 Generate the highest tier and the highest color based on our level. 01:11:10.470 --> 01:11:12.850 So in this case, we'll go no higher of a tier 01:11:12.850 --> 01:11:16.025 than three because we have no higher tiers than three. 01:11:16.025 --> 01:11:18.080 It goes zero, one, two, three. 01:11:18.080 --> 01:11:20.564 And then whatever our level divided by five is, 01:11:20.564 --> 01:11:21.980 and it would just take math.floor. 01:11:21.980 --> 01:11:26.540 Math.floor takes in basically performing division and then truncating 01:11:26.540 --> 01:11:28.269 the decimal point. 01:11:28.269 --> 01:11:29.060 Well, not division. 01:11:29.060 --> 01:11:31.760 It just literally truncates the decimal point off of a number. 01:11:31.760 --> 01:11:33.260 So a level divided by five. 01:11:33.260 --> 01:11:36.560 Whatever that is before the decimal point. 01:11:36.560 --> 01:11:39.420 Level modular five plus three for the highest color. 01:11:39.420 --> 01:11:40.430 So we'll cycle. 01:11:40.430 --> 01:11:42.140 We'll go over and over again. 01:11:42.140 --> 01:11:46.400 Go highest color one, two, three, four, five, and then we'll go to a new tier 01:11:46.400 --> 01:11:47.810 with level divided by 5. 01:11:47.810 --> 01:11:50.520 So basically, every five levels will increment in tier, 01:11:50.520 --> 01:11:52.410 and then we'll start back at blue. 01:11:52.410 --> 01:11:56.406 And then we go on, and on, and on like that for every number of rows. 01:11:56.406 --> 01:11:57.530 So basically I have a few-- 01:11:57.530 --> 01:11:59.889 I'm going to sort of glaze over this a little bit 01:11:59.889 --> 01:12:02.180 just because we're probably going to run short on time. 01:12:02.180 --> 01:12:05.360 But we have basically two flags. 01:12:05.360 --> 01:12:10.620 Whether we're skipping bricks in this row or alternating bricks color wise. 01:12:10.620 --> 01:12:14.810 And if we do, we need to set a color for it and a tier. 01:12:14.810 --> 01:12:16.681 And then we basically just say, you know, 01:12:16.681 --> 01:12:19.430 the same sort of logic that we had before we generated random rows 01:12:19.430 --> 01:12:24.350 and columns, but if we have the alternate flag on, 01:12:24.350 --> 01:12:31.400 then as we can see in some of these photos here, here we have skip is true. 01:12:31.400 --> 01:12:36.260 So the color for that row is set to the blue, but skip is true, 01:12:36.260 --> 01:12:40.700 so every other brick is just going to skip that iteration of the loop. 01:12:40.700 --> 01:12:42.980 Same thing here, only it's offset by one. 01:12:42.980 --> 01:12:43.770 Same thing here. 01:12:43.770 --> 01:12:44.490 Same thing here. 01:12:44.490 --> 01:12:46.550 So this is kind of a nice little pattern. 01:12:46.550 --> 01:12:47.966 And in each of these cases-- 01:12:47.966 --> 01:12:49.340 actually not each of these cases. 01:12:49.340 --> 01:12:52.070 Notice this third one, it also set alternate to true. 01:12:52.070 --> 01:12:55.250 So it goes green, purple, green, purple, green, purple. 01:12:55.250 --> 01:12:57.920 And so the logic there is if alternate is true, then 01:12:57.920 --> 01:13:00.560 just flip the color every iteration. 01:13:00.560 --> 01:13:04.430 If skip is true, don't generate a brick every other iteration, and so on 01:13:04.430 --> 01:13:05.010 and so forth. 01:13:05.010 --> 01:13:09.570 And then if you have solid or if you don't have alternate equals true, 01:13:09.570 --> 01:13:11.750 then you have a solid brick like these blue ones. 01:13:11.750 --> 01:13:16.730 And if you have alternate but no skip, you get this sort of pattern 01:13:16.730 --> 01:13:18.830 where you have green, purple, green, purple. 01:13:18.830 --> 01:13:20.480 You know, any random color. 01:13:20.480 --> 01:13:23.450 And then also the number of columns is random. 01:13:23.450 --> 01:13:24.740 So it can go-- 01:13:24.740 --> 01:13:30.590 here we have 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 but on this very bottom one, 01:13:30.590 --> 01:13:35.810 we have that minus two it looks like because it can only go that wide. 01:13:35.810 --> 01:13:37.490 And in those here too. 01:13:37.490 --> 01:13:38.660 Smaller size. 01:13:38.660 --> 01:13:40.100 That one there's no spacing. 01:13:40.100 --> 01:13:42.470 So these are very simple concepts. 01:13:42.470 --> 01:13:44.960 Like should we skip a block this iteration? 01:13:44.960 --> 01:13:47.300 Should we alternate the colors? 01:13:47.300 --> 01:13:49.970 And when you put them all together, it produces 01:13:49.970 --> 01:13:52.160 things that look as if they were almost handcrafted. 01:13:52.160 --> 01:13:53.900 Like this could be made by somebody. 01:13:53.900 --> 01:13:55.850 Like, that looks like it was made by somebody. 01:13:55.850 --> 01:13:57.170 Pretty much every iteration of this. 01:13:57.170 --> 01:13:59.330 I mean, even that, that looks like a shape almost. 01:13:59.330 --> 01:14:03.534 Its just very simple but the results are pretty awesome in my opinion. 01:14:03.534 --> 01:14:05.700 And so that's just the gist behind what we're doing. 01:14:05.700 --> 01:14:08.510 We're just setting flags and just saying, you know, 01:14:08.510 --> 01:14:10.789 if we're skipping this turn and just every iteration, 01:14:10.789 --> 01:14:13.580 every time we lay out a brick and we spawn a new brick on this row, 01:14:13.580 --> 01:14:14.810 just do or don't. 01:14:14.810 --> 01:14:16.660 Just make it's color-- 01:14:16.660 --> 01:14:19.940 pick two colors if we're alternating and then set 01:14:19.940 --> 01:14:23.660 its color to whatever the off color is that we're alternating. 01:14:23.660 --> 01:14:26.150 And if we're skipping and alternating, then 01:14:26.150 --> 01:14:29.090 we're just doing whenever we're on a brick that we're actually laying 01:14:29.090 --> 01:14:32.540 is when we change the color, the alternate color. 01:14:32.540 --> 01:14:35.004 And so like I said, I won't go into too much detail. 01:14:35.004 --> 01:14:36.920 Happy to talk about the generator after class. 01:14:36.920 --> 01:14:39.240 But just because we're running short on time, 01:14:39.240 --> 01:14:40.880 sort of going to wave my hands over it. 01:14:40.880 --> 01:14:42.350 But that's it in a nutshell. 01:14:42.350 --> 01:14:47.430 So any questions before we take a break for five minutes? 01:14:47.430 --> 01:14:47.930 Yes? 01:14:47.930 --> 01:15:02.800 AUDIENCE: [INAUDIBLE] 01:15:02.800 --> 01:15:06.340 COLTON OGDEN: The question is, in an instance with this programming 01:15:06.340 --> 01:15:10.360 if the ball were so fast that it we're actually inside the brick, 01:15:10.360 --> 01:15:12.633 would it what? 01:15:12.633 --> 01:15:14.174 AUDIENCE: Would it still bounce back? 01:15:14.174 --> 01:15:15.882 COLTON OGDEN: Would it still bounce back? 01:15:15.882 --> 01:15:18.250 The answer is no, it wouldn't. 01:15:18.250 --> 01:15:21.460 This implementation doesn't take into consideration velocity 01:15:21.460 --> 01:15:24.130 that goes too fast. 01:15:24.130 --> 01:15:26.150 Mainly to-- for two reasons. 01:15:26.150 --> 01:15:28.707 One, it's non-trivial to implement, and two, 01:15:28.707 --> 01:15:30.790 it's an interesting thing to look at, and observe, 01:15:30.790 --> 01:15:34.750 and be conscious of as you go forward in implementing your own games. 01:15:34.750 --> 01:15:38.862 The current code, if it gets clipped inside of the brick, 01:15:38.862 --> 01:15:41.320 it will have no edges that are peaking outside of the brick 01:15:41.320 --> 01:15:43.819 and therefore, it will default to the final condition, which 01:15:43.819 --> 01:15:47.080 is the last else clause, which puts it below the brick. 01:15:47.080 --> 01:15:48.850 So it'll just go below the brick. 01:15:48.850 --> 01:15:54.212 It'll almost be as if it came in from the underside and bounced out. 01:15:54.212 --> 01:15:56.920 But like I alluded to earlier, if you want to implement something 01:15:56.920 --> 01:16:01.960 like this yourself, you would have to slice up frame X and frame X plus one 01:16:01.960 --> 01:16:08.440 into the size of the ball if the delta is so wide 01:16:08.440 --> 01:16:11.800 that it either goes inside of a brick or it goes outside of a brick, 01:16:11.800 --> 01:16:13.559 or if it skips a brick. 01:16:13.559 --> 01:16:15.100 And this sort of solves that problem. 01:16:15.100 --> 01:16:18.220 It solves both of those problems, but it's a little more 01:16:18.220 --> 01:16:20.600 than we can cover in this example. 01:16:20.600 --> 01:16:24.260 Any other questions? 01:16:24.260 --> 01:16:24.760 All right. 01:16:24.760 --> 01:16:28.671 Let's take five and get back to it. 01:16:28.671 --> 01:16:29.170 All right. 01:16:29.170 --> 01:16:29.960 And we're back. 01:16:29.960 --> 01:16:35.980 So the next step is we have basically a layout dynamically 01:16:35.980 --> 01:16:39.310 generated of interesting bricks now, but we haven't really 01:16:39.310 --> 01:16:41.680 implemented scoring any of these. 01:16:41.680 --> 01:16:43.570 We just have score gets score plus 10, which 01:16:43.570 --> 01:16:46.330 isn't really particularly interesting. 01:16:46.330 --> 01:16:50.080 So Breakout7 is what I call the tier update, which 01:16:50.080 --> 01:16:55.160 should allow us to hit blocks that are a higher tier than just base blue. 01:16:55.160 --> 01:16:59.800 And if they are of a higher color than base blue, they should go down a color. 01:16:59.800 --> 01:17:04.150 So the hierarchy was, if we look back, blue 01:17:04.150 --> 01:17:08.890 goes to green goes to red goes to purple goes to gold. 01:17:08.890 --> 01:17:11.920 And if something is a higher tier, it goes 01:17:11.920 --> 01:17:15.640 to the next color below it but at that same tier, 01:17:15.640 --> 01:17:19.750 unless it happens to be like blue and gray, in which case 01:17:19.750 --> 01:17:22.010 it'll go back to blue. 01:17:22.010 --> 01:17:30.184 So how might we implement scoring based on this system? 01:17:30.184 --> 01:17:30.850 What do we need? 01:17:30.850 --> 01:17:33.770 What pieces do we need? 01:17:33.770 --> 01:17:38.202 What pieces do we already have that we can use to make this happen? 01:17:38.202 --> 01:17:40.460 AUDIENCE: [INAUDIBLE] 01:17:40.460 --> 01:17:41.460 COLTON OGDEN: I'm sorry? 01:17:41.460 --> 01:17:52.351 AUDIENCE: [INAUDIBLE] 01:17:52.351 --> 01:17:54.350 COLTON OGDEN: So the answer was the brick index. 01:17:54.350 --> 01:18:00.721 So yes, the brick skin and color are the pieces. 01:18:00.721 --> 01:18:01.220 Yes. 01:18:01.220 --> 01:18:03.280 So those are fields of brick. 01:18:03.280 --> 01:18:04.420 So if we open up-- 01:18:04.420 --> 01:18:06.950 I'm going to go up to Breakout7. 01:18:06.950 --> 01:18:10.070 And I'm going to start probably deferring a lot of this code 01:18:10.070 --> 01:18:12.020 to future reading. 01:18:12.020 --> 01:18:16.580 But in brick here, the tier and the color-- 01:18:16.580 --> 01:18:19.670 sorry, not skin, but skin is for the paddle. 01:18:19.670 --> 01:18:22.200 But the brick has a tier and it has a color. 01:18:22.200 --> 01:18:25.530 And so we need to perform some arithmetic on that here. 01:18:25.530 --> 01:18:28.100 And that's essentially what lines 44 through 58 is. 01:18:28.100 --> 01:18:32.420 So basically-- oh, I apologize. 01:18:32.420 --> 01:18:36.330 That's not actually where the arithmetic is. 01:18:36.330 --> 01:18:40.430 44, that does compute, but this is the bit of code 01:18:40.430 --> 01:18:45.810 that computes how we can actually go backwards if we make a collision. 01:18:45.810 --> 01:18:52.880 So if we collide with a brick and it's of a higher tier than one 01:18:52.880 --> 01:18:57.870 and it's a higher color than blue, it should be brought back one step. 01:18:57.870 --> 01:19:02.420 But if it happens to be blue, in which case self.color gets one 01:19:02.420 --> 01:19:05.420 because blue is one, then it should just be removed from play 01:19:05.420 --> 01:19:06.840 just like we've done before. 01:19:06.840 --> 01:19:10.490 Only now, we're also taking in tier and color. 01:19:10.490 --> 01:19:13.460 So we're decrementing tier based on what index we're at 01:19:13.460 --> 01:19:15.890 and we're decrementing color. 01:19:15.890 --> 01:19:20.053 And then this actually gets used in our PlayState. 01:19:24.000 --> 01:19:29.750 If we go to line 81, which previously just 01:19:29.750 --> 01:19:32.960 had self.score gets self.score plus one, there's a little bit of math here. 01:19:32.960 --> 01:19:34.220 It's very simple though. 01:19:34.220 --> 01:19:36.290 Just brick.tier times 200. 01:19:36.290 --> 01:19:38.270 So make the tiers worth 100. 01:19:38.270 --> 01:19:41.470 Plus brick.color times 25. 01:19:41.470 --> 01:19:43.520 And so if tier is zero, if it's a base then we're 01:19:43.520 --> 01:19:44.990 just not going to get that 200 bonus. 01:19:44.990 --> 01:19:47.115 But the first tier, everything is going to be worth 01:19:47.115 --> 01:19:49.490 25 times whatever its color is. 01:19:49.490 --> 01:19:52.400 So one, two, three, four, five. 01:19:52.400 --> 01:19:55.790 And then add 200 plus the brick.color for when 01:19:55.790 --> 01:19:58.760 we get to the next set of bricks. 01:19:58.760 --> 01:20:01.140 And so the result of this is-- 01:20:05.680 --> 01:20:10.120 I believe this is GUI Breakout7. 01:20:14.100 --> 01:20:15.680 And then if we hit a brick-- 01:20:15.680 --> 01:20:17.520 since this one is blue, it should disappear. 01:20:17.520 --> 01:20:19.103 And we're playing a new sound as well. 01:20:19.103 --> 01:20:21.285 New, like, death sound just to make it clear. 01:20:24.710 --> 01:20:26.337 But notice they change colors. 01:20:26.337 --> 01:20:27.420 So that's all we're doing. 01:20:27.420 --> 01:20:29.960 We're just taking their tier or their color 01:20:29.960 --> 01:20:32.330 and just performing a simple decrement on it. 01:20:32.330 --> 01:20:33.650 Looping back. 01:20:33.650 --> 01:20:36.620 In the event that we go down a tier, we should loop back up 01:20:36.620 --> 01:20:39.440 to the highest color of the lower tier. 01:20:39.440 --> 01:20:41.390 So I'll let you look at the code for that 01:20:41.390 --> 01:20:44.060 if you want to sort of get a more low level understanding of it, 01:20:44.060 --> 01:20:46.192 but that's the sort of high level understanding. 01:20:46.192 --> 01:20:48.650 The next big concept that I'd like to introduce you guys to 01:20:48.650 --> 01:20:51.080 is a particle system. 01:20:51.080 --> 01:20:57.420 And so particle systems are fairly omnipresent in video games, 01:20:57.420 --> 01:20:59.510 I would say, because they make effects that 01:20:59.510 --> 01:21:02.090 or otherwise difficult to do with simple sprite 01:21:02.090 --> 01:21:05.720 editing achievable very easily and realistically. 01:21:05.720 --> 01:21:07.200 Just like fire, for example. 01:21:07.200 --> 01:21:10.417 Things that are very organic, and flowy, and have a lot going on 01:21:10.417 --> 01:21:12.500 are often better represented with particle systems 01:21:12.500 --> 01:21:15.260 than they are with simple sprite animation. 01:21:15.260 --> 01:21:18.230 So does anybody know how we might be able to-- 01:21:18.230 --> 01:21:22.010 how a particle system might work underneath the hood? 01:21:22.010 --> 01:21:26.304 I think I alluded to it previously. 01:21:26.304 --> 01:21:26.803 Yes. 01:21:26.803 --> 01:21:33.879 AUDIENCE: [INAUDIBLE] 01:21:33.879 --> 01:21:34.670 COLTON OGDEN: Yeah. 01:21:34.670 --> 01:21:38.740 So what he said was in order to make fire, 01:21:38.740 --> 01:21:41.650 for example, just spawn a bunch of particles 01:21:41.650 --> 01:21:45.320 close to the center of wherever your fire is spawning and then outside of it 01:21:45.320 --> 01:21:46.450 spawn fewer. 01:21:46.450 --> 01:21:49.900 That is absolutely a way to get fire to work, 01:21:49.900 --> 01:21:54.190 and also taking into consideration the travel of your particles. 01:21:54.190 --> 01:21:57.580 For example, you might spawn a ton of fire particles really densely, 01:21:57.580 --> 01:22:00.580 but then maybe they have some logic that makes them go upwards. 01:22:00.580 --> 01:22:04.400 Maybe they have a negative delta Y and then some sort of acceleration 01:22:04.400 --> 01:22:06.040 so they've sort of trail off. 01:22:06.040 --> 01:22:10.270 And then maybe sort of how to get a more realistic fire 01:22:10.270 --> 01:22:13.720 look, they travel sort of upwards and then fade away. 01:22:13.720 --> 01:22:16.690 So the way fire works, sort of thinking of things 01:22:16.690 --> 01:22:20.830 in terms of particles like that, you can achieve a lot of effects. 01:22:20.830 --> 01:22:23.360 How might we implement, like, smoke, for example? 01:22:23.360 --> 01:22:24.340 Same system. 01:22:29.250 --> 01:22:33.980 So we could have maybe a timer in our particle effect, 01:22:33.980 --> 01:22:36.170 or even a transition because in particle systems, 01:22:36.170 --> 01:22:39.300 often you have the ability to transition colors between particles. 01:22:39.300 --> 01:22:41.900 Let's say you start off red, go to yellow, 01:22:41.900 --> 01:22:44.996 and then maybe your particle system transitions to gray or brown. 01:22:44.996 --> 01:22:47.870 And then over time, your particles are going up, they're dissipating. 01:22:47.870 --> 01:22:50.210 And they're also turning dark, they're turning brown, 01:22:50.210 --> 01:22:52.820 it sort of gives you the illusion of fire. 01:22:52.820 --> 01:22:54.890 And we won't be doing anything necessarily as 01:22:54.890 --> 01:22:59.030 complex as this in our code here, but in Breakout8, we 01:22:59.030 --> 01:23:04.160 will be using Love's sort of integrated particle system which is just 01:23:04.160 --> 01:23:06.770 love.graphics.newparticlesystem. 01:23:06.770 --> 01:23:09.830 And it takes in a texture because all particle systems need some sort 01:23:09.830 --> 01:23:11.630 of texture as their foundation. 01:23:11.630 --> 01:23:15.920 And then it needs the number of particles that it could maximally emit. 01:23:15.920 --> 01:23:18.740 And so each individual particle system can emit up 01:23:18.740 --> 01:23:20.780 to a certain instance of particles. 01:23:20.780 --> 01:23:23.234 And in the number, and speed, and whatnot of all 01:23:23.234 --> 01:23:25.400 those particles is ultimately the determining factor 01:23:25.400 --> 01:23:28.850 for how you can get an illusion. 01:23:28.850 --> 01:23:32.722 Back to last week's lecture, illusions, like, it's not fire, it's not smoke, 01:23:32.722 --> 01:23:34.430 it's just a bunch of particles responding 01:23:34.430 --> 01:23:36.740 with colors and acceleration and stuff. 01:23:36.740 --> 01:23:40.940 But there's a lot of functions that particle system gives you in Love2D, 01:23:40.940 --> 01:23:44.240 so I encourage you to look at that link just to explore some of them. 01:23:44.240 --> 01:23:48.530 Love2d.org/wiki/particlesystem. 01:23:48.530 --> 01:23:49.930 We'll be using a few of them. 01:23:49.930 --> 01:23:53.330 Here I'm going to just briefly show you. 01:23:53.330 --> 01:23:57.890 So each individual brick when it gets hit 01:23:57.890 --> 01:23:59.895 is going to need a particle system of its own. 01:23:59.895 --> 01:24:00.770 Because our goal is-- 01:24:00.770 --> 01:24:03.870 I'll run the code for you so you can see it. 01:24:03.870 --> 01:24:09.290 So if you go to Breakout8 and then you run it, 01:24:09.290 --> 01:24:18.390 we have a little bit of particles you saw there at the very end. 01:24:18.390 --> 01:24:22.250 The blue you were probably able to see a little bit better. 01:24:22.250 --> 01:24:25.150 And then one last time. 01:24:25.150 --> 01:24:27.340 So it spawns a bunch of little particles. 01:24:27.340 --> 01:24:30.970 So can anyone tell me how they think the particles are 01:24:30.970 --> 01:24:33.670 behaving sort of in a nutshell? 01:24:33.670 --> 01:24:38.354 What the logic is for the particles? 01:24:38.354 --> 01:24:40.819 AUDIENCE: [INAUDIBLE] slightly random. 01:24:40.819 --> 01:24:41.610 COLTON OGDEN: Yeah. 01:24:41.610 --> 01:24:42.400 Slightly random. 01:24:42.400 --> 01:24:45.940 And if you look at it, you'll also notice that they tend to go downwards. 01:24:49.030 --> 01:24:52.210 So knowing that, we can probably just assume 01:24:52.210 --> 01:24:57.080 that they have an acceleration that tends towards positive Y. 01:24:57.080 --> 01:24:59.080 And that's essentially all we really need to do. 01:24:59.080 --> 01:25:02.560 We spawn a bunch of particles outwards and then just set them-- 01:25:02.560 --> 01:25:04.000 they have all a lifetime. 01:25:04.000 --> 01:25:06.250 They last for a certain amount of time. 01:25:06.250 --> 01:25:09.490 And then they fade between two colors. 01:25:09.490 --> 01:25:16.150 In this case, we fade from red to transparent or whatever color it is. 01:25:16.150 --> 01:25:23.260 And then after the lifetimes elapsed, it has the overall effect 01:25:23.260 --> 01:25:25.810 of sort of this glimmering, gravity based effect, 01:25:25.810 --> 01:25:27.280 but it's really just a bunch of particles that are 01:25:27.280 --> 01:25:28.821 set to spawn in different directions. 01:25:28.821 --> 01:25:30.260 Apologize for that. 01:25:30.260 --> 01:25:32.110 So we'll take a look. 01:25:32.110 --> 01:25:38.130 It's going to be in our brick class here in Breakout8. 01:25:38.130 --> 01:25:39.380 So we're going to go to brick. 01:25:42.290 --> 01:25:45.260 We have a bunch of colors that we're storing here. 01:25:45.260 --> 01:25:49.820 So if you notice, the particle systems adopt the color of whatever brick 01:25:49.820 --> 01:25:52.430 they're hitting just so that it stays sort of congruent 01:25:52.430 --> 01:25:54.185 with what we're looking at. 01:25:54.185 --> 01:25:56.060 So we're just storing a bunch of colors here. 01:25:56.060 --> 01:25:57.950 And I wouldn't worry too much about this. 01:25:57.950 --> 01:25:59.824 These are just colors from the sprite palette 01:25:59.824 --> 01:26:01.760 that we used with our sprite art. 01:26:01.760 --> 01:26:04.490 There's specific colors that are only used in that sprite. 01:26:04.490 --> 01:26:06.487 And having a palette, generally speaking, 01:26:06.487 --> 01:26:08.570 allows your art to look a little bit more cohesive 01:26:08.570 --> 01:26:11.694 when you're doing sprite art as opposed to just picking colors willy nilly. 01:26:11.694 --> 01:26:15.320 If you say, oh, I'm going to only use 16 or 32 colors for this palette, 01:26:15.320 --> 01:26:17.840 you'll sort of have a more cohesive look and also 01:26:17.840 --> 01:26:20.780 a very retro look because often hardware was 01:26:20.780 --> 01:26:25.190 limited to a certain amount of colors back in the day for older systems. 01:26:25.190 --> 01:26:26.960 So it's nice to-- 01:26:26.960 --> 01:26:30.050 as an aside-- and we'll look at it next week as well. 01:26:30.050 --> 01:26:34.137 Looking at when you're doing your own sprite art, try to use fewer colors 01:26:34.137 --> 01:26:36.720 and then that will give you-- it also makes it easier for you. 01:26:36.720 --> 01:26:40.970 You don't have to spend time choosing I want to have this shade of green. 01:26:40.970 --> 01:26:42.240 I wonder if it looks good. 01:26:42.240 --> 01:26:45.634 If you only have two shades of green or semi shades of green to choose from, 01:26:45.634 --> 01:26:46.550 that's all you've got. 01:26:46.550 --> 01:26:48.302 You have to make do with it what you can. 01:26:48.302 --> 01:26:51.260 So what we're doing here is we're storing five colors from our palette. 01:26:51.260 --> 01:26:52.430 We're going to use this. 01:26:52.430 --> 01:26:54.920 And then when we trigger our-- 01:26:54.920 --> 01:26:57.980 so right here we're initializing a particle system. 01:26:57.980 --> 01:27:02.150 So psystem gets love.graphics.newparticlesystem. 01:27:02.150 --> 01:27:04.130 And then these are a few functions. 01:27:04.130 --> 01:27:07.370 So feel free to look in the wiki for how these functions actually behave. 01:27:07.370 --> 01:27:10.550 But lifetime acceleration and area spread 01:27:10.550 --> 01:27:13.190 just are sort of the properties that influence 01:27:13.190 --> 01:27:15.330 the way our particle systems behave. 01:27:15.330 --> 01:27:17.600 And so using whatever our current color is, 01:27:17.600 --> 01:27:21.230 we're going to set our psystem's colors using setcolors function. 01:27:21.230 --> 01:27:23.200 We're going to set it between two colors. 01:27:23.200 --> 01:27:28.796 Color with 55 times tier alpha and color with zero alpha. 01:27:28.796 --> 01:27:30.920 So the higher the tier, the brighter the particles, 01:27:30.920 --> 01:27:34.130 but they'll always fade to zero alpha, if that makes sense. 01:27:34.130 --> 01:27:35.760 And then we'll just emit 64. 01:27:35.760 --> 01:27:37.650 And this is all in the hit function. 01:27:37.650 --> 01:27:42.020 So all we've basically done is just add this particle system trigger in our hit 01:27:42.020 --> 01:27:46.170 function, and it has the result of the behavior that we saw earlier. 01:27:46.170 --> 01:27:49.700 So any questions on particle systems or how we use them? 01:27:53.220 --> 01:27:55.830 So level 9 is the progression update. 01:27:55.830 --> 01:27:59.880 So the purpose of this update is to allow us to go from level one 01:27:59.880 --> 01:28:04.170 to two to three to four and start get more interesting level 01:28:04.170 --> 01:28:06.510 generation that way. 01:28:06.510 --> 01:28:09.480 The gist of this is in our-- 01:28:09.480 --> 01:28:11.932 so if you look at our StartState-- 01:28:16.180 --> 01:28:22.090 so all we need to really do to store a level is just to store a number. 01:28:22.090 --> 01:28:24.850 And then where do we increment the number? 01:28:24.850 --> 01:28:28.069 Or when do we increment the number I should say? 01:28:28.069 --> 01:28:33.914 AUDIENCE: [INAUDIBLE] 01:28:33.914 --> 01:28:34.830 COLTON OGDEN: Exactly. 01:28:34.830 --> 01:28:40.590 So we increment the level. 01:28:40.590 --> 01:28:43.560 We go to the next level when all of the bricks 01:28:43.560 --> 01:28:47.310 are in play have gotten there in play flag set to false. 01:28:47.310 --> 01:28:52.720 So we have no pricks that are in play effectively. 01:28:52.720 --> 01:28:54.840 So in our StartState-- 01:28:54.840 --> 01:28:57.086 so let's go ahead and look at Breakout9. 01:29:00.990 --> 01:29:02.147 So StartState. 01:29:05.803 --> 01:29:07.711 We're passing in level gets one here. 01:29:07.711 --> 01:29:08.960 We're just going to start off. 01:29:08.960 --> 01:29:12.043 When we're going to StartState, we're just going to pass level equals one. 01:29:12.043 --> 01:29:17.450 And then henceforth, anytime we do any state changes from play to serve 01:29:17.450 --> 01:29:19.940 and to victory, as we'll see, victory being our new, 01:29:19.940 --> 01:29:21.750 oh, you cleared this level. 01:29:21.750 --> 01:29:22.690 Here's the next level. 01:29:22.690 --> 01:29:27.050 We're just going to pass the level between them. 01:29:27.050 --> 01:29:37.962 And then in PlayState, the important bit of code here is on line 204. 01:29:37.962 --> 01:29:39.920 So this is just a function called checkVictory, 01:29:39.920 --> 01:29:41.480 which is exactly as James said. 01:29:44.090 --> 01:29:48.590 We're going to iterate over the entire table and just say if it's in play, 01:29:48.590 --> 01:29:50.360 return false because we're not in victory 01:29:50.360 --> 01:29:52.070 if we have any bricks that are in play. 01:29:52.070 --> 01:29:55.850 But return true if we didn't meet that condition. 01:29:55.850 --> 01:29:59.390 And so this is just a simple way for us to check whether or not 01:29:59.390 --> 01:30:02.730 we are in a victory. 01:30:02.730 --> 01:30:05.800 And so on line 88 of the same file in our PlayState, 01:30:05.800 --> 01:30:09.560 we're just checking to say, hey, if self.checkVictory after we do any brick 01:30:09.560 --> 01:30:10.160 hit-- 01:30:10.160 --> 01:30:13.370 because that's when we've just set a brick to in play is false-- 01:30:13.370 --> 01:30:14.880 just check victory. 01:30:14.880 --> 01:30:17.900 And if so, play a new sound like a happy sound 01:30:17.900 --> 01:30:20.390 that we've done a victory, and then just pass everything 01:30:20.390 --> 01:30:22.400 into the new VictoryState that we have here. 01:30:22.400 --> 01:30:27.410 And the VictoryState is simply a sort of just a message state. 01:30:27.410 --> 01:30:30.630 So all it does is just renders everything as before, 01:30:30.630 --> 01:30:34.150 but it just says your current level complete. 01:30:34.150 --> 01:30:36.342 Self.level complete. 01:30:36.342 --> 01:30:39.050 And then press Enter to serve and it'll go back to the ServeState 01:30:39.050 --> 01:30:41.100 as soon as that happens. 01:30:41.100 --> 01:30:47.270 And then here is where the actual progression happens. 01:30:47.270 --> 01:30:51.450 When we go to the ServeState, we have our level but we want to add one to it. 01:30:51.450 --> 01:30:57.320 So all we need to do when we trigger a transition into our next state, just 01:30:57.320 --> 01:31:01.550 increment level by one here, and also create 01:31:01.550 --> 01:31:05.470 a new map because bricks needs to get restarted because we have a new level. 01:31:05.470 --> 01:31:07.680 Self.level plus one. 01:31:07.680 --> 01:31:12.820 And that'll have the effect of, oh, we've gone from level one to two 01:31:12.820 --> 01:31:17.480 to three to four et cetera when we go between PlayState to the VictoryState 01:31:17.480 --> 01:31:19.110 back to the ServeState. 01:31:19.110 --> 01:31:21.420 So any questions on how any of this works? 01:31:21.420 --> 01:31:21.920 Yes. 01:31:21.920 --> 01:31:24.545 AUDIENCE: Do you have to worry about garbage collection for any 01:31:24.545 --> 01:31:26.294 of the bricks at all? 01:31:26.294 --> 01:31:28.211 Or is that handled by the Love engine somehow? 01:31:28.211 --> 01:31:30.377 COLTON OGDEN: Garbage collection is handled by Love. 01:31:30.377 --> 01:31:31.060 Yes. 01:31:31.060 --> 01:31:31.560 Yeah. 01:31:31.560 --> 01:31:32.760 AUDIENCE: [INAUDIBLE] 01:31:32.760 --> 01:31:33.510 COLTON OGDEN: Yes. 01:31:33.510 --> 01:31:37.550 Because the question was, do you have to worry about garbage collection 01:31:37.550 --> 01:31:40.880 when we are sort of clearing away the bricks and adding new bricks? 01:31:40.880 --> 01:31:45.950 The self.bricks table, this table here, it's 01:31:45.950 --> 01:31:48.950 getting assigned to a brand new table from levelmap.createmap. 01:31:48.950 --> 01:31:51.950 When there are no references to an existing table, 01:31:51.950 --> 01:31:54.290 lua's garbage collector will trigger at whatever 01:31:54.290 --> 01:31:57.410 interval it's set to trigger and clear up all that for you dynamically. 01:31:57.410 --> 01:32:00.050 Just like the same way that Java works. 01:32:00.050 --> 01:32:02.810 Almost identical. 01:32:02.810 --> 01:32:06.170 Any other questions? 01:32:06.170 --> 01:32:07.690 All right. 01:32:07.690 --> 01:32:10.020 So we have progression. 01:32:10.020 --> 01:32:11.500 In the sake of speed, I won't demo. 01:32:11.500 --> 01:32:14.499 It also takes a while just because we have to clear an entire level then 01:32:14.499 --> 01:32:16.030 get to the next level. 01:32:16.030 --> 01:32:17.680 But that's how the behavior works. 01:32:17.680 --> 01:32:22.780 The next sort of iteration of this is high scores. 01:32:22.780 --> 01:32:30.040 And I will test to make sure whether or not this is actually working. 01:32:30.040 --> 01:32:31.730 I know I changed some stuff. 01:32:31.730 --> 01:32:32.230 Yeah. 01:32:32.230 --> 01:32:33.169 So high score. 01:32:33.169 --> 01:32:34.210 Let's debug for a second. 01:32:34.210 --> 01:32:41.863 So HighScoreState line 38 in Breakout10. 01:32:44.750 --> 01:32:46.730 So HighScoreState. 01:32:49.780 --> 01:32:54.030 And then the issue was [INAUDIBLE] to index field high scores. 01:32:54.030 --> 01:32:56.270 A nil value. 01:32:56.270 --> 01:32:57.000 OK. 01:32:57.000 --> 01:32:59.620 So that means that-- 01:32:59.620 --> 01:33:00.120 OK. 01:33:00.120 --> 01:33:03.420 I think I might know the issue, but it's because I 01:33:03.420 --> 01:33:06.570 transitioned to a new user that doesn't have a saved file active on this. 01:33:06.570 --> 01:33:12.780 The way that will transition, therefore, into love.file system, which 01:33:12.780 --> 01:33:17.910 is Breakout10's main new thing that it introduces-- so writing files 01:33:17.910 --> 01:33:22.429 to your file system is done [INAUDIBLE] with love.filesystem. 01:33:22.429 --> 01:33:23.470 And there's a few things. 01:33:23.470 --> 01:33:28.870 So Love automatically gives you a directory, a save directory 01:33:28.870 --> 01:33:30.120 that's pretty much hard coded. 01:33:30.120 --> 01:33:33.960 There are a few exceptions as to how to not use that directory, 01:33:33.960 --> 01:33:36.480 but it assumes that you're always using that directory. 01:33:36.480 --> 01:33:42.750 And with very few exceptions will you always use that folder. 01:33:42.750 --> 01:33:47.460 It's like app data local on Windows, and application support, 01:33:47.460 --> 01:33:50.850 and the name of your application on Mac. 01:33:50.850 --> 01:33:53.490 But it's a subfolder that Love has read and write access to 01:33:53.490 --> 01:33:55.650 for files on your file system. 01:33:55.650 --> 01:33:59.940 You can check whether it exists with love.filesystem.exists at some path. 01:33:59.940 --> 01:34:03.870 You can write to that path with some data, that data being a string value. 01:34:03.870 --> 01:34:08.140 And then love.filesystem.lines is an iterator, 01:34:08.140 --> 01:34:11.040 which will allow you to look over any of the data that's 01:34:11.040 --> 01:34:12.720 in a file at a given location. 01:34:12.720 --> 01:34:13.395 Yes. 01:34:13.395 --> 01:34:16.010 AUDIENCE: [INAUDIBLE] 01:34:16.010 --> 01:34:17.010 COLTON OGDEN: Yeah. 01:34:17.010 --> 01:34:20.930 AUDIENCE: Does this work if you [INAUDIBLE] 01:34:20.930 --> 01:34:21.930 COLTON OGDEN: It should. 01:34:21.930 --> 01:34:24.150 We can pull that up now actually and see. 01:34:24.150 --> 01:34:28.500 Because I know on their Love2D-- 01:34:28.500 --> 01:34:30.180 so file system. 01:34:30.180 --> 01:34:35.910 So the question was he ported his-- 01:34:35.910 --> 01:34:39.390 when you port your Love app to the iPhone, 01:34:39.390 --> 01:34:43.440 will it have the same sort of behavior if you're-- 01:34:47.867 --> 01:34:50.700 on an iPhone, will it have the same sort of save directory behavior? 01:34:50.700 --> 01:34:53.010 And it looks like it's not officially on here. 01:34:53.010 --> 01:34:57.840 I know that there is an iOS port for Love2D, 01:34:57.840 --> 01:35:02.458 or the ability to send it to Love2D. 01:35:02.458 --> 01:35:05.610 AUDIENCE: [INAUDIBLE] 01:35:05.610 --> 01:35:07.110 COLTON OGDEN: I have to imagine yes. 01:35:07.110 --> 01:35:08.359 It probably has some sort of-- 01:35:08.359 --> 01:35:14.040 I'm not entirely familiar with how iOS handles sort of local storage, 01:35:14.040 --> 01:35:18.210 but I'm assuming that just in the way that it's been abstracted for desktops 01:35:18.210 --> 01:35:20.460 and for Android, it's also abstracted for iOS. 01:35:20.460 --> 01:35:22.320 Haven't tested it myself. 01:35:22.320 --> 01:35:25.260 I would experiment and see actually maybe with this code. 01:35:25.260 --> 01:35:29.200 See if you can maybe get it working with persistent high scores. 01:35:29.200 --> 01:35:33.292 I know that iOS does typically let you store a small amount of data per app 01:35:33.292 --> 01:35:35.250 in some location, a fixed location, but I'm not 01:35:35.250 --> 01:35:37.920 entirely sure what that is offhand. 01:35:37.920 --> 01:35:40.400 I can look into it more and come up with a-- 01:35:40.400 --> 01:35:41.609 AUDIENCE: [INAUDIBLE] 01:35:41.609 --> 01:35:42.400 COLTON OGDEN: Yeah. 01:35:42.400 --> 01:35:45.180 I mean, not from firsthand because I don't have an Android, 01:35:45.180 --> 01:35:47.280 but it has official Android support. 01:35:47.280 --> 01:35:50.840 So I'm guessing it does, but I haven't tested it. 01:35:50.840 --> 01:35:53.710 I have not tested it manually on Android to verify that. 01:35:59.030 --> 01:36:00.190 But yes. 01:36:00.190 --> 01:36:03.420 I believe-- because in the prior directory we were looking at when it 01:36:03.420 --> 01:36:05.690 showed-- 01:36:05.690 --> 01:36:08.690 oh, it's actually up here. 01:36:08.690 --> 01:36:09.750 This path here. 01:36:09.750 --> 01:36:14.640 This data/user/0/love2d.android. 01:36:14.640 --> 01:36:15.300 file save. 01:36:15.300 --> 01:36:18.990 That looks to me like it's the official sort of path 01:36:18.990 --> 01:36:22.050 that data is stored on an Android device for application. 01:36:22.050 --> 01:36:24.150 So I haven't tested it myself. 01:36:24.150 --> 01:36:27.360 But if you have an Android and you're curious or maybe an emulator, 01:36:27.360 --> 01:36:30.470 give it a shot and see if it works. 01:36:30.470 --> 01:36:32.970 Oh, and it even says here, there are various save locations. 01:36:32.970 --> 01:36:35.760 And if they don't work, you can see what the actual location 01:36:35.760 --> 01:36:37.200 is with this function here. 01:36:37.200 --> 01:36:39.540 The love.filesystem.get save directory. 01:36:39.540 --> 01:36:43.440 That may work on iOS as well, so I'd be curious to hear about 01:36:43.440 --> 01:36:45.360 whether that actually works on that. 01:36:50.790 --> 01:36:51.300 Yeah. 01:36:51.300 --> 01:36:52.260 So that's the gist. 01:36:52.260 --> 01:36:55.530 Using the love.filesystem abstraction lets us read and write files. 01:36:55.530 --> 01:36:58.470 We can then just paste or we can just save whatever data 01:36:58.470 --> 01:37:02.520 we want anywhere within that directory. 01:37:02.520 --> 01:37:06.090 We can just create files in there and then use those to store our, 01:37:06.090 --> 01:37:10.440 you know, sort of game worlds, or character profiles, or whatnot. 01:37:10.440 --> 01:37:14.445 How would we maybe go about implementing sort of like a high score list? 01:37:21.290 --> 01:37:21.860 So I'll look. 01:37:21.860 --> 01:37:24.360 There's a picture here. 01:37:24.360 --> 01:37:27.320 So we have 10 scores. 01:37:27.320 --> 01:37:29.390 We'll assume that's fixed. 01:37:29.390 --> 01:37:34.650 Each of the scores has a name, and then each of the scores has an actual score. 01:37:34.650 --> 01:37:39.230 So all we really need to do is just store ultimately the names 01:37:39.230 --> 01:37:42.190 and then the scores. 01:37:42.190 --> 01:37:47.680 AUDIENCE: [INAUDIBLE] 01:37:47.680 --> 01:37:49.180 COLTON OGDEN: So we'll use an array. 01:37:49.180 --> 01:37:53.371 Their response was we'll use an array as sorted by that score. 01:37:53.371 --> 01:37:53.870 Yeah. 01:37:53.870 --> 01:37:55.120 Essentially that's exactly it. 01:37:55.120 --> 01:37:57.560 We're just going to keep a score table and each table 01:37:57.560 --> 01:37:59.069 is going to have a sub table. 01:37:59.069 --> 01:38:00.860 And each of those entries, one through ten, 01:38:00.860 --> 01:38:02.870 is going to have a name and a score. 01:38:02.870 --> 01:38:05.300 And then once we're done with our application, 01:38:05.300 --> 01:38:07.790 we'll just use love.filesystem.write. 01:38:07.790 --> 01:38:10.460 We'll have to convert all of those into a string 01:38:10.460 --> 01:38:15.200 because we can't just take a table and then spit that out into a file. 01:38:15.200 --> 01:38:18.230 We have to actually make it into some form that we can save 01:38:18.230 --> 01:38:20.880 and then reload back in somehow. 01:38:20.880 --> 01:38:24.295 What would be the most efficient way, do you think, or a way we can do this? 01:38:28.950 --> 01:38:31.995 Probably just a new line separated list. 01:38:31.995 --> 01:38:33.870 The way that I've done it in this application 01:38:33.870 --> 01:38:39.570 is just names, and then new line, score, new line, name, new line, score. 01:38:39.570 --> 01:38:41.280 10, so 20 rows. 01:38:41.280 --> 01:38:43.130 And that gets the job done. 01:38:43.130 --> 01:38:46.772 Assuming that you don't tamper with the file, then everything should work. 01:38:46.772 --> 01:38:48.480 And you can write additional code as well 01:38:48.480 --> 01:38:51.845 to say, oh, if there is a score that's all 01:38:51.845 --> 01:38:53.970 garbled, we don't have enough scores, then probably 01:38:53.970 --> 01:38:55.500 should render it accordingly. 01:38:55.500 --> 01:38:59.272 My code does something similar to this, but not entirely. 01:39:02.370 --> 01:39:06.240 The relevant code-- and I'm going to sort of just glaze over it. 01:39:06.240 --> 01:39:08.640 If we're looking at-- this is Breakout11, right? 01:39:08.640 --> 01:39:09.140 Yeah. 01:39:09.140 --> 01:39:11.550 Oh, no, this is Breakout10. 01:39:11.550 --> 01:39:14.850 So in Breakout10, we have to load all the high scores 01:39:14.850 --> 01:39:16.810 in main.lua, which is here. 01:39:16.810 --> 01:39:19.680 So set identity to Breakout or create a folder called Breakout 01:39:19.680 --> 01:39:22.170 that we can save and read files to and from. 01:39:22.170 --> 01:39:24.450 If it doesn't exist, then just create them. 01:39:24.450 --> 01:39:26.910 In this case, I'm just seeding CTO my initials. 01:39:26.910 --> 01:39:28.870 And then I times 1,000. 01:39:28.870 --> 01:39:30.990 So 10,000 down to 1,000. 01:39:30.990 --> 01:39:33.314 Just very simple data. 01:39:33.314 --> 01:39:34.980 Writing into a file called breakout.lst. 01:39:34.980 --> 01:39:36.300 It can be whatever you want. 01:39:36.300 --> 01:39:40.620 All we're doing is reading lines from the data, or from the file. 01:39:40.620 --> 01:39:42.540 And then this is if it doesn't exist. 01:39:42.540 --> 01:39:50.100 And then if it does exist, then we're going to iterate over it 01:39:50.100 --> 01:39:53.730 with love.filesystem.line, which will take a file 01:39:53.730 --> 01:39:56.460 and then just split it on new lines basically and give you 01:39:56.460 --> 01:39:58.200 an iterator over all those lines. 01:39:58.200 --> 01:40:00.540 So it can just say, OK, if it's a name, which 01:40:00.540 --> 01:40:06.120 means that if it's one or three or five or seven in the list, 01:40:06.120 --> 01:40:08.990 then set the name to-- 01:40:08.990 --> 01:40:11.460 and we're using string.sub just in case they 01:40:11.460 --> 01:40:14.270 write some long name or some long name gets-- they can't do it 01:40:14.270 --> 01:40:17.640 through our game, but if it gets written to the file as some long name, 01:40:17.640 --> 01:40:19.770 it should get truncated to three characters 01:40:19.770 --> 01:40:22.360 so we can display it appropriately. 01:40:22.360 --> 01:40:24.850 And then otherwise if we're not on a name line, 01:40:24.850 --> 01:40:28.140 if we're on, like, an odd line or even line, 01:40:28.140 --> 01:40:32.140 we should consider that a score and just use to number. 01:40:32.140 --> 01:40:37.154 Because we're using string data and if we try to assign, 01:40:37.154 --> 01:40:39.570 do any sort of comparisons numerically on the string data, 01:40:39.570 --> 01:40:41.790 which we will have to do to compare high scores, 01:40:41.790 --> 01:40:44.160 it's not going to work because it's going to see that there's strings. 01:40:44.160 --> 01:40:45.330 So we use to number here. 01:40:45.330 --> 01:40:46.819 Just a simple Lua function. 01:40:46.819 --> 01:40:47.610 And then that's it. 01:40:47.610 --> 01:40:50.430 And then we just return scores. 01:40:50.430 --> 01:40:53.490 And then I'll sort out what's causing the issue, 01:40:53.490 --> 01:40:56.880 and then push that to the repo ASAP. 01:40:56.880 --> 01:41:00.690 But that has the effect of us being able to actually load all of our high scores 01:41:00.690 --> 01:41:03.790 and display them at the start of the game. 01:41:03.790 --> 01:41:08.680 It doesn't take care of being able to actually input our score. 01:41:08.680 --> 01:41:12.870 And so we can do this with Breakout11, which you can see if you run the repo. 01:41:12.870 --> 01:41:17.580 And you can test just to assign your initial score to some value like 10,000 01:41:17.580 --> 01:41:19.890 or 20,000, and then just lose on purpose and you 01:41:19.890 --> 01:41:21.990 get a sense of how it actually works. 01:41:21.990 --> 01:41:24.010 But essentially, it's just arcade style. 01:41:24.010 --> 01:41:26.760 You know, you had only three characters you could input your name. 01:41:26.760 --> 01:41:31.530 So does anybody have any idea as to how we are sort of storing this, 01:41:31.530 --> 01:41:32.670 or can pitch an idea? 01:41:36.720 --> 01:41:41.100 So we have three characters and we want to-- 01:41:41.100 --> 01:41:45.330 ideally if we're, let's say I want to go to C on the first one. 01:41:45.330 --> 01:41:52.526 Let's say I pressed up twice so I get to C. How is it going from A to C? 01:41:52.526 --> 01:41:54.610 You could just say, you could just render 01:41:54.610 --> 01:41:58.900 I want to render the character A, the character A, the character A, 01:41:58.900 --> 01:42:01.700 but how is it going to know when you want to go to B, or C, or D. 01:42:01.700 --> 01:42:15.116 AUDIENCE: [INAUDIBLE] 01:42:15.116 --> 01:42:16.990 COLTON OGDEN: The pitch was, you could create 01:42:16.990 --> 01:42:19.406 a table with all of the characters and iterate through it. 01:42:19.406 --> 01:42:22.450 You absolutely could do that. 01:42:22.450 --> 01:42:23.740 It's a little bit bulky. 01:42:23.740 --> 01:42:24.880 That might be what-- 01:42:24.880 --> 01:42:28.720 actually, that's probably not how arcade systems did it back in the day. 01:42:28.720 --> 01:42:38.680 Because the way that we're going to do it here in Breakout11 is I 01:42:38.680 --> 01:42:43.780 added a new state called EnterHighScoreState. 01:42:43.780 --> 01:42:46.390 And if you recall, CS50 teaches this. 01:42:46.390 --> 01:42:51.260 But all sort of characters at the end of the day are just numbers. 01:42:51.260 --> 01:42:53.740 ASCI. 01:42:53.740 --> 01:42:58.030 In this case, 65, if you recall, is capital A. 01:42:58.030 --> 01:43:03.640 So all we need to do is just draw out whatever that character 01:43:03.640 --> 01:43:06.590 cast to a string is, or character. 01:43:06.590 --> 01:43:11.740 And we do that simply down here in the draw function. 01:43:11.740 --> 01:43:19.962 If we do string.char, at char is three. 01:43:19.962 --> 01:43:22.420 All that has the effect of doing is just taking that number 01:43:22.420 --> 01:43:25.660 and then converting it to a character. 01:43:25.660 --> 01:43:27.520 So all we need to do then is what? 01:43:27.520 --> 01:43:33.006 When we want to go from A to B, B to C, C to D. 01:43:33.006 --> 01:43:35.654 AUDIENCE: [INAUDIBLE] 01:43:35.654 --> 01:43:36.570 COLTON OGDEN: Exactly. 01:43:36.570 --> 01:43:39.165 But then what happens if we're at A and we want to go down? 01:43:39.165 --> 01:43:41.102 AUDIENCE: [INAUDIBLE] 01:43:41.102 --> 01:43:42.060 COLTON OGDEN: We would. 01:43:42.060 --> 01:43:49.650 So if we're at A, then if we press downward and we want to go to Z, 01:43:49.650 --> 01:43:50.670 the logic is in here. 01:43:53.580 --> 01:43:59.280 But one we've incremented our code, if it's greater than 90, which is Z, 01:43:59.280 --> 01:44:00.670 then we should set it back to 65. 01:44:00.670 --> 01:44:01.560 We'll loop back to A. 01:44:01.560 --> 01:44:02.820 And same thing here. 01:44:02.820 --> 01:44:06.630 If we press down and we're at A, we've got to go back up to Z, 01:44:06.630 --> 01:44:08.130 so we just set it to 90. 01:44:08.130 --> 01:44:09.690 So simple loop back logic. 01:44:09.690 --> 01:44:11.940 And we just draw it, we highlight. 01:44:11.940 --> 01:44:14.940 And then once we've done that, the user presses Enter. 01:44:14.940 --> 01:44:19.380 We transition to the HighScoreState, actually, 01:44:19.380 --> 01:44:22.410 because this state should only trigger if they entered a new high score. 01:44:22.410 --> 01:44:26.740 Which means that we need to check in the VictoryState, or not the VictoryState, 01:44:26.740 --> 01:44:29.070 but rather in the GameOverState whether or not 01:44:29.070 --> 01:44:33.540 their score is higher than any of the stores in some sort of, 01:44:33.540 --> 01:44:36.000 quote unquote, global scores table. 01:44:36.000 --> 01:44:39.558 And then how do we think we're passing the scores back and forth now? 01:44:42.510 --> 01:44:45.388 Does anybody recall how we're keeping track of app state? 01:44:48.280 --> 01:44:55.190 AUDIENCE: [INAUDIBLE] 01:44:55.190 --> 01:44:55.940 COLTON OGDEN: Yep. 01:44:55.940 --> 01:44:57.270 In the change function. 01:44:57.270 --> 01:44:59.540 So all we need to do is keep track of-- load 01:44:59.540 --> 01:45:01.490 our high scores at the beginning of the game, 01:45:01.490 --> 01:45:03.360 pass them all the way down the line. 01:45:03.360 --> 01:45:07.560 And then finally-- and we can also load them in our EnterHighScoreState, 01:45:07.560 --> 01:45:11.390 but we need to keep track of what our high scores are in the GameOverState 01:45:11.390 --> 01:45:13.562 so that we know, oh, I've got a high score. 01:45:13.562 --> 01:45:16.520 Let's instead of transitioning back to the StartState, let's transition 01:45:16.520 --> 01:45:22.130 to the EnterHighScoreState so the user can add their high score to the list. 01:45:22.130 --> 01:45:30.770 And then once they've entered their high score, which is here, 01:45:30.770 --> 01:45:35.940 we'll just write it to this file again. 01:45:35.940 --> 01:45:39.760 Compile a score string, which takes name and score of our scores. 01:45:39.760 --> 01:45:43.000 We take whatever score that we were at that's-- 01:45:43.000 --> 01:45:45.220 we look through our scores table backwards 01:45:45.220 --> 01:45:47.982 and when we find a score that's lower than ours, 01:45:47.982 --> 01:45:51.190 we just keep track of that index until we get to one that's higher than ours. 01:45:51.190 --> 01:45:54.160 In which case the one plus one, that index plus one 01:45:54.160 --> 01:45:55.600 is what we should then overwrite. 01:45:55.600 --> 01:45:59.050 And so we shift all the other ones below accordingly. 01:45:59.050 --> 01:46:01.907 And we do that in this class if curious. 01:46:01.907 --> 01:46:04.240 And so I'm just going to breeze through the last couple. 01:46:04.240 --> 01:46:07.000 The paddle select update is just kind of a fluffy state 01:46:07.000 --> 01:46:13.330 that lets us add a element of sort of, like, user selection to our game. 01:46:13.330 --> 01:46:18.850 In our PaddleSelectState here, we transition immediately. 01:46:18.850 --> 01:46:21.370 Instead of going to the [INAUDIBLE] PlayState now, 01:46:21.370 --> 01:46:26.140 we're going to go from Start to Paddle Select when we hit Start Game. 01:46:26.140 --> 01:46:27.310 So we're going to go to-- 01:46:27.310 --> 01:46:29.200 and then the Paddle Select class itself. 01:46:32.170 --> 01:46:35.080 CurrentPaddle gets one, and then all it essentially 01:46:35.080 --> 01:46:42.210 is is us drawing two arrows here. 01:46:42.210 --> 01:46:47.260 And so if we're at number one-- in this case, I think we're at number three-- 01:46:47.260 --> 01:46:49.990 then both of these arrows will be completely opaque. 01:46:49.990 --> 01:46:53.320 But if we're on the left or the right edge, they should darken to say to us, 01:46:53.320 --> 01:46:55.528 oh, we can't move left or right anymore because we're 01:46:55.528 --> 01:47:00.100 at either index one or four or five, and there's only that many colors. 01:47:00.100 --> 01:47:02.170 And then render whatever that color variable 01:47:02.170 --> 01:47:07.976 is using the quads table that we had before of the different tables. 01:47:07.976 --> 01:47:09.100 And then just instructions. 01:47:09.100 --> 01:47:13.360 And then from there is where we'll end up transitioning to the ServeState 01:47:13.360 --> 01:47:18.100 rather than going to the ServeState from the StartState. 01:47:18.100 --> 01:47:20.590 And all the code in that is here. 01:47:20.590 --> 01:47:22.540 We have sound effects playing. 01:47:22.540 --> 01:47:26.654 And then making sure that we also play a different sound 01:47:26.654 --> 01:47:29.320 effect based upon whether they're at the left or the right edge. 01:47:29.320 --> 01:47:30.880 If they're on the left edge and they try to go left, 01:47:30.880 --> 01:47:33.940 it should play like a sound that sort of sounds a little rougher 01:47:33.940 --> 01:47:35.680 to let them know that they can't go left, 01:47:35.680 --> 01:47:38.350 and the opposite for the right edge. 01:47:38.350 --> 01:47:42.770 And then once that's all done, once they press Enter on whatever paddle 01:47:42.770 --> 01:47:46.940 they want, they're going to get the paddle, 01:47:46.940 --> 01:47:50.770 we're going to instantiate a paddle, pass that into the ServeState, 01:47:50.770 --> 01:47:53.710 and we're going to take currentPaddle from the state, which 01:47:53.710 --> 01:47:59.920 is whatever value they got by scrolling between all the different paddles. 01:47:59.920 --> 01:48:06.280 And then the last update, which is my favorite part of most every lecture 01:48:06.280 --> 01:48:09.850 I think is the music update. 01:48:09.850 --> 01:48:15.130 And all that really is is just music set play in main.lua, 01:48:15.130 --> 01:48:19.410 and then set looping to true, and then we have a game. 01:48:19.410 --> 01:48:20.660 And this is our Paddle Select. 01:48:20.660 --> 01:48:25.960 So notice the arrows are semi-opaque on the left and the right. 01:48:25.960 --> 01:48:30.310 It's kind of hard to hear, but when I press right now 01:48:30.310 --> 01:48:33.890 it's kind of like there's a bit of a rougher sound. 01:48:33.890 --> 01:48:35.080 We choose red. 01:48:35.080 --> 01:48:37.900 We go to level one and we transition to the ServeState 01:48:37.900 --> 01:48:43.240 from the PaddleSelectState, and then we just play the game as normal. 01:48:43.240 --> 01:48:45.060 And that's basically all there is to it. 01:48:45.060 --> 01:48:48.018 And there is a couple of features we didn't have time to really go over 01:48:48.018 --> 01:48:51.374 like making sure we recover HP if a certain amount of points 01:48:51.374 --> 01:48:53.290 have been elapsed, but I encourage you to look 01:48:53.290 --> 01:48:56.290 into that when you trigger a hit. 01:48:56.290 --> 01:48:58.930 There's some logic in the PlayState to say, oh, 01:48:58.930 --> 01:49:01.467 if they've gone over a current recovery threshold, 01:49:01.467 --> 01:49:04.300 let's add one heart to the player, you know, just keep them playing. 01:49:04.300 --> 01:49:06.008 Just to reward them for their high score. 01:49:08.470 --> 01:49:10.410 Next time we'll cover a few concepts. 01:49:10.410 --> 01:49:11.300 So basic shaders. 01:49:11.300 --> 01:49:13.550 Shaders are like little programs you can run in your graphics card 01:49:13.550 --> 01:49:16.210 and do fancy effects, but we won't go into too much detail. 01:49:16.210 --> 01:49:17.140 Anonymous functions. 01:49:17.140 --> 01:49:21.119 We've seen a lot of anonymous functions in Lua in the context of Love. 01:49:21.119 --> 01:49:23.410 They're just functions without a name, and you can just 01:49:23.410 --> 01:49:26.410 use them as function arguments and do all sorts of cool stuff with them. 01:49:26.410 --> 01:49:29.140 We'll use them for callbacks next week when we do things 01:49:29.140 --> 01:49:31.480 like tweening, which is taking some value 01:49:31.480 --> 01:49:35.190 and making it interpolate over time to some other thing. 01:49:35.190 --> 01:49:38.440 Because right now we've basically just been updating things based on velocity, 01:49:38.440 --> 01:49:40.609 but we haven't really done anything based on time. 01:49:40.609 --> 01:49:42.400 So we'll take a look at that in more detail 01:49:42.400 --> 01:49:46.600 next week with a library called timer, which is really fantastic. 01:49:46.600 --> 01:49:50.050 Lets you time things and then chain things together. 01:49:50.050 --> 01:49:52.670 We'll be covering the game Match Three if familiar. 01:49:52.670 --> 01:49:54.130 It's basically Candy Crush. 01:49:54.130 --> 01:49:57.130 We'll be using a different tile set, but it's the same idea. 01:49:57.130 --> 01:49:59.500 And we'll have to calculate how to actually find out 01:49:59.500 --> 01:50:02.500 whether we've gotten a match in the grid, our tile grid, 01:50:02.500 --> 01:50:06.820 and then shift the blocks accordingly and do all the other logic, add score. 01:50:06.820 --> 01:50:10.810 And then basically since it's so fundamental to Candy Crush and games 01:50:10.810 --> 01:50:13.720 of its nature, we will have to cover how to sort of generate 01:50:13.720 --> 01:50:17.076 these maps procedurally to have tiles that are laid out in a dynamic way, 01:50:17.076 --> 01:50:19.450 and also in a way that doesn't start off with any matches 01:50:19.450 --> 01:50:22.199 because then that wouldn't make any sense because the matches have 01:50:22.199 --> 01:50:23.230 to resolve. 01:50:23.230 --> 01:50:26.740 And then we'll take a little time if we have the time next week 01:50:26.740 --> 01:50:28.810 to talk about sprite art again and palettes. 01:50:28.810 --> 01:50:32.490 And maybe I'll show you guys how to sort of convert images from one 01:50:32.490 --> 01:50:35.200 palette to another in, like, a program that I use, Aseprite, 01:50:35.200 --> 01:50:40.200 but you can do this in any sort of large photo editing software. 01:50:40.200 --> 01:50:44.990 And then assignment two is a couple of extensions to Breakout. 01:50:44.990 --> 01:50:50.100 So if you noticed in the sheet there were a few little sprites here 01:50:50.100 --> 01:50:51.240 at the bottom-- 01:50:51.240 --> 01:50:53.580 so get rid of the quad outlines. 01:50:53.580 --> 01:50:56.580 So these little things down here are, I'm assuming, 01:50:56.580 --> 01:50:57.909 they're meant to be power ups. 01:50:57.909 --> 01:50:58.950 They look like power ups. 01:50:58.950 --> 01:51:01.790 But the goal of the pset is to implement a power up. 01:51:01.790 --> 01:51:05.939 And a power up is going to be such that when you grab it, 01:51:05.939 --> 01:51:08.730 you'll get two additional balls, or however many you want actually, 01:51:08.730 --> 01:51:12.270 that will spawn in addition to your one and detect collisions on their own. 01:51:12.270 --> 01:51:14.760 So you'll have several and they'll score points for you. 01:51:14.760 --> 01:51:18.450 And, of course, only when the last ball comes below the surface of the screen 01:51:18.450 --> 01:51:20.700 should you trigger a Game Over. 01:51:20.700 --> 01:51:22.930 And then I want you to add-- and this will also 01:51:22.930 --> 01:51:24.763 be more detailed than the spec-- but I would 01:51:24.763 --> 01:51:26.970 like you to add growing and shrinking to the paddle. 01:51:26.970 --> 01:51:29.925 So currently, we have like four different sizes of paddle, 01:51:29.925 --> 01:51:31.230 but we're not using them. 01:51:31.230 --> 01:51:34.380 So it would be nice if when we gain enough points or we lose points, or not 01:51:34.380 --> 01:51:37.890 points, but lives rather, we increase or decrease the size of the paddle 01:51:37.890 --> 01:51:40.260 accordingly just to introduce another level of challenge 01:51:40.260 --> 01:51:42.630 and or lack of challenge. 01:51:42.630 --> 01:51:46.620 And then finally, one last part which is in the sprite sheet as well, 01:51:46.620 --> 01:51:54.040 there's a key block here and a key power up here. 01:51:54.040 --> 01:52:00.504 So sort of let the power up come, pick the power up with your paddle. 01:52:00.504 --> 01:52:02.670 And then only when you have that power up should you 01:52:02.670 --> 01:52:06.069 be able to break the block with a key. 01:52:06.069 --> 01:52:07.860 And you should take this into consideration 01:52:07.860 --> 01:52:09.360 when generating your levels as well. 01:52:09.360 --> 01:52:12.420 So you'll have to also get your hands dirty with the level maker. 01:52:12.420 --> 01:52:14.670 But all in all, that was Breakout. 01:52:14.670 --> 01:52:16.170 So I'll see you guys next time. 01:52:16.170 --> 01:52:18.020 Thank you.