CS50 Video Player
    • 🧁

    • 🍨

    • 🍒

    • 🍿
    • 0:00:00Introduction
    • 0:03:44Breakout Demo
    • 0:06:04Lecture Goal
    • 0:06:54Breakout State Flow
    • 0:08:27The Day-0 Update
    • 0:08:42Project Organization
    • 0:16:40The Quad Update
    • 0:16:52Sprite Sheets and Quads
    • 0:30:56The Brick Update
    • 0:34:14The Bounce Update
    • 0:49:14LevelMaker
    • 0:51:04The Collision Update
    • 0:52:12Paddle Collision
    • 0:52:55Brick Collision
    • 1:02:20The Hearts Update
    • 1:10:21The Pretty Colors Update
    • 1:16:28The Tier Update
    • 1:20:45The Particle Update
    • 1:27:50The Progression Update
    • 1:32:17The High Scores Update
    • 1:40:56The Entry Update
    • 1:46:03The Paddle Select Update
    • 1:49:07Next Time
    • 0:00:00[MUSIC PLAYING]
    • 0:00:16COLTON OGDEN: Hello, everybody.
    • 0:00:18Welcome to GD50 Lecture 2.
    • 0:00:20This is Breakout.
    • 0:00:22And interestingly, CS50 has a history with Breakout,
    • 0:00:25so I pulled this up today.
    • 0:00:26This is Pset3 in 2015, 2014.
    • 0:00:30It was an implementation of Breakout using the Stanford Portable
    • 0:00:34Library, which was a sort of Java library
    • 0:00:37that we were able to get C bindings for.
    • 0:00:40And so students were able to actually implement
    • 0:00:42a game what was at the time the CS50 appliance, which is a Linux distro.
    • 0:00:48But suffice to say that was--
    • 0:00:49oh, a funny story also.
    • 0:00:51I happened to also write the lasers for this implementation back in the day.
    • 0:00:56And I think that was one of the first bits of code
    • 0:00:59I got my hands dirty with when working with CS50.
    • 0:01:02So today in the context of Breakout, we'll
    • 0:01:04be talking about a few different things that we haven't talked about yet.
    • 0:01:07Sprite sheets being chief among them most likely.
    • 0:01:10At least the most visibly so.
    • 0:01:12So sprite sheets are simply a way of taking an image, a large image,
    • 0:01:15and rather than splitting it, rather than loading individual images
    • 0:01:19for all of your different things in the game,
    • 0:01:21whether it's your aliens or your paddles or whatnot, you can put everything
    • 0:01:25into one sheet and then just sort of index into that sheet using rectangles,
    • 0:01:30quads.
    • 0:01:30We'll talk about soon.
    • 0:01:31Which will allow you to just draw a subset of that image,
    • 0:01:33and therefore condense all of your artwork just into one piece, one file.
    • 0:01:37We'll be talking a little bit more about procedural generation
    • 0:01:41in the context of Breakout.
    • 0:01:42And in this case, we'll be laying out all the bricks in the game world
    • 0:01:45procedurally.
    • 0:01:46So having instead of the same set of colors, in this case,
    • 0:01:50the standard layout is to have a bunch of the same colored bricks row by row.
    • 0:01:55We'll actually implement a dynamic generation approach
    • 0:01:58and have a bunch of different cool layouts we'll see.
    • 0:02:00And it's actually quite simple to achieve pretty believable results.
    • 0:02:04We'll manage state a little bit better in this game.
    • 0:02:07So before we sort of had a couple of global variables
    • 0:02:10and we didn't really have the concept of a per state or a global state
    • 0:02:14that we were cleanly sort of sharing between all of our states
    • 0:02:18for our state machine.
    • 0:02:19But to avoid having sort of like a polluted global name space
    • 0:02:22and to just sort of keep things a little bit cleaner,
    • 0:02:24we'll end up taking all of the important variables for our code like, you know,
    • 0:02:28the player and any other entities.
    • 0:02:29The bricks, the ball.
    • 0:02:30And rather than keep them in our main [INAUDIBLE],,
    • 0:02:33we'll end up shifting them.
    • 0:02:34We'll sort of transfer them to and from the different states
    • 0:02:37via the state machine's enter method.
    • 0:02:41We'll actually have levels.
    • 0:02:42So a progression system.
    • 0:02:43So start at level one, go up.
    • 0:02:45And then with each level, we'll implement a scale
    • 0:02:48in terms of the generation of the bricks.
    • 0:02:51So we'll get higher tiered bricks and more points as a result.
    • 0:02:54We'll have a health system.
    • 0:02:55So hearts, in a similar fashion to Legend of Zelda.
    • 0:02:58Particle systems, which are a very important aesthetic component
    • 0:03:02to 2D games and 3D games.
    • 0:03:04Particle systems basically being a bunch of spawned images
    • 0:03:07that you sort of cluster, you put into a little spawner,
    • 0:03:10emit them in a certain way, and color them, perform math on them,
    • 0:03:15and get sort of believable effects like fire and smoke
    • 0:03:17and all these other things that would otherwise
    • 0:03:19be not easy to do using simple animation,
    • 0:03:24but trivial with a particle system.
    • 0:03:26We'll do a little bit more complicated collision detection with our paddle
    • 0:03:31and with our bricks than we did with Pong.
    • 0:03:33And then we'll also talk lastly about how
    • 0:03:34we can save data locally to our computer so that when we close the application
    • 0:03:38and run it again, we end up having a persistent high score rather
    • 0:03:42than just something that's volatile.
    • 0:03:44So first though, I would like to demo today's finished game.
    • 0:03:49So if anybody would like to demo from the audience, that would be nice.
    • 0:03:55Go ahead and come up.
    • 0:03:57I'll go ahead and cue it up for you.
    • 0:04:02What's your name?
    • 0:04:03JEREMY: Jeremy.
    • 0:04:03COLTON OGDEN: Jeremy.
    • 0:04:04Colton.
    • 0:04:04JEREMY: Nice to meet you, Colton.
    • 0:04:05COLTON OGDEN: Nice to meet you.
    • 0:04:06So we're going to go ahead and run Breakout here.
    • 0:04:13And so it uses the arrow keys.
    • 0:04:15So if you go ahead and press up and down,
    • 0:04:17you'll see you can move between the start and the high score screen.
    • 0:04:19So they're two separate screens.
    • 0:04:21So go ahead and--
    • 0:04:22here we have when you start, you can choose a paddle.
    • 0:04:24So rather than just the same old paddle every time, you get to select.
    • 0:04:27And as you can see here, he chose green.
    • 0:04:29So he gets the green paddle.
    • 0:04:31These bricks all procedure generated.
    • 0:04:32So if he runs the application, they'll be completely different.
    • 0:04:36And as is the classic formula, the ball moves
    • 0:04:39between the bricks and the paddle.
    • 0:04:41When it hits a brick, if it's of a certain color,
    • 0:04:44it'll either get destroyed-- in this case, if it's blue,
    • 0:04:46it's the base color brick.
    • 0:04:48So it's the lowest value.
    • 0:04:50And if it's higher than blue, it'll end up
    • 0:04:52going down a color depending on which color it is.
    • 0:04:55I believe it goes blue, green, red, purple, yellow.
    • 0:04:58So anything higher will get shifted down.
    • 0:05:01And then the player amasses points, as you can see top right.
    • 0:05:04Score.
    • 0:05:04And notice also the three hearts.
    • 0:05:06That will be the player's health.
    • 0:05:07So if he were to lose on purpose possibly,
    • 0:05:13we can see he gets another message that's saying press Enter to serve.
    • 0:05:18His hearts have gone down by one.
    • 0:05:20So now he's got two out of three health.
    • 0:05:23And so eventually if he were to by chance lose completely--
    • 0:05:32oh.
    • 0:05:35That's honestly the most fun part about Breakout
    • 0:05:37is just getting it caught in a bunch of stuff.
    • 0:05:39But you can see we go to a Game Over screen.
    • 0:05:41It shows your final score.
    • 0:05:42And then you can press Enter and it will--
    • 0:05:46oh.
    • 0:05:46I must have had a bug.
    • 0:05:48But that should take you back to the--
    • 0:05:51if in the event that you have a high score,
    • 0:05:53it'll take you to enter a high score.
    • 0:05:55And if you don't have a high score, it'll take you back to the Start menu.
    • 0:06:00So I made a couple of last minute changes.
    • 0:06:02Unfortunately I must have left something in there.
    • 0:06:04But that's Breakout in a nutshell.
    • 0:06:07Our goal today will be to implement basically all the functionality we saw.
    • 0:06:10Oh, we didn't take a look at the high score screen.
    • 0:06:12So let's take a look at that really quick as well.
    • 0:06:14So here at the title, you can see we have Start and High Scores.
    • 0:06:17Oh, man.
    • 0:06:18OK.
    • 0:06:19I must have screwed something up.
    • 0:06:20So I'm going want to go [INAUDIBLE] Breakout 12.
    • 0:06:24OK.
    • 0:06:24Sorry.
    • 0:06:25I apologize.
    • 0:06:25I'm going to fix that.
    • 0:06:26But it should show this menu here where you will have a list of all your names
    • 0:06:31that get loaded from a file and will output your score accordingly.
    • 0:06:35And in the event that you get a new high score,
    • 0:06:41you'll get to enter your name after that,
    • 0:06:44and then it will end up saving it to another file.
    • 0:06:46And when we get to that point I'll try and fix it
    • 0:06:48so that we can actually see what it looks like.
    • 0:06:51So let's go back to these slides here.
    • 0:06:53So this is the overall state, flow of our game.
    • 0:06:55So as you can see by me marking it out in a highlighted color,
    • 0:06:59we start off in the StartState.
    • 0:07:00And this is all stuff we've covered before.
    • 0:07:01Just the state machine.
    • 0:07:03It's a little bit more complicated than Flappy Bird.
    • 0:07:05We have eight states as opposed to I think it
    • 0:07:07was four or five in the last lecture.
    • 0:07:10And the arrows illustrate which states can move in between other states.
    • 0:07:13So as we saw, the StartState can move via the up and down arrows
    • 0:07:17in the HighScoreState.
    • 0:07:18It can move between the HighScoreState and back.
    • 0:07:20So when you go into the HighScoreState, press Escape,
    • 0:07:23go back to the StartState.
    • 0:07:25The StartState also has an arrow branching off
    • 0:07:27to the left going down to the PaddleSelectState
    • 0:07:29where we saw the user is able to select a paddle to use.
    • 0:07:32Once they've selected a paddle, we'll go to the ServeState.
    • 0:07:35They'll be able to serve the ball at their leisure.
    • 0:07:37And then it will go back and forth between the PlayState.
    • 0:07:40So if they end up taking damage, the ball
    • 0:07:43goes below the surface of the screen, they'll go back to the ServeState
    • 0:07:47again so they can reorient themselves.
    • 0:07:49If they're in the PlayState and they end up scoring,
    • 0:07:53clearing the whole entire set of bricks, they'll
    • 0:07:55actually get taken to the VictoryState.
    • 0:07:57And the VictoryState is where we increment the level
    • 0:07:59and we also regenerate the level.
    • 0:08:00And the VictoryState goes back to the ServeState,
    • 0:08:02and then we repeat that whole loop again.
    • 0:08:04In the PlayState if they are to get a Game Over,
    • 0:08:08they'll go to the GameOverState, it'll tell them their score,
    • 0:08:10and then they'll go to the EnterHighScoreState
    • 0:08:13depending on whether they have a high score.
    • 0:08:14And if not, as seen by the arrow that goes up and to the left,
    • 0:08:18they'll actually go back to the StartState.
    • 0:08:19And then the EnterHighScoreState will also go back to the HighScoreState
    • 0:08:22so that they can see once they've entered
    • 0:08:24their high score, their score relative to the other scores in the list.
    • 0:08:27So in Breakout0, which we're going to look at now,
    • 0:08:29we're going to do some very basic stuff.
    • 0:08:31So this is the Day 0 update as always.
    • 0:08:34I'm in Breakout0 right now.
    • 0:08:37Yes, I am.
    • 0:08:39So what we're going to do is we're going to look at first thing here, line 27.
    • 0:08:42So before what we were doing in our application
    • 0:08:47is having basically a lot of files at the top level and sort of losing track
    • 0:08:51of what we were doing potentially.
    • 0:08:53Especially as you start adding more and more files
    • 0:08:55and you've got like 50, 100 more files, that's
    • 0:08:58something that's obviously not maintainable.
    • 0:09:00So the solution there, just put them in folders
    • 0:09:02and then keep track of everything.
    • 0:09:04Keep them organized.
    • 0:09:05And that's a major thing that we're going to start doing.
    • 0:09:07And on top of that, we're also going to, in our code,
    • 0:09:10keep things a little bit more modular.
    • 0:09:11And that's why we have this file source slash dependencies,
    • 0:09:15which we'll take a look at in a second.
    • 0:09:18We've allocated a bunch of global tables here.
    • 0:09:20So we're taking the design decision of even
    • 0:09:23though I mentioned that we will be sort of taking a lot of the global variables
    • 0:09:26out of our application assets, we're going to keep
    • 0:09:29all of those in some global variables.
    • 0:09:31And we'll see in the future how we can maybe implement a resource manager
    • 0:09:34class that takes care of this for us.
    • 0:09:36But for now, for simplicity's sake, in love.load,
    • 0:09:38we're just going to have a few global tables that contain,
    • 0:09:41in this case, global fonts.
    • 0:09:43So by key, we can index small, medium, and large fonts,
    • 0:09:46which are just new fonts at different sizes.
    • 0:09:488, 16, 32.
    • 0:09:49And we're using it.
    • 0:09:50We have a fonts folder now instead of just keeping it at the parent level.
    • 0:09:53We're going to set it to small.
    • 0:09:54We have global textures.
    • 0:09:55So background, main, arrows, hearts, particle.
    • 0:09:59So we have the background, which was the background of our screen.
    • 0:10:02Main has all of our bricks, paddles, the balls, et cetera.
    • 0:10:05Arrows are going to be for the paddle select screen.
    • 0:10:07The two left and right arrows.
    • 0:10:09Hearts are going to be for our health.
    • 0:10:10And then particle is a single, small, tiny little texture
    • 0:10:13that we'll use to spawn all the particles in our particle systems
    • 0:10:16later on as we get towards the end of the demonstration.
    • 0:10:20So this is push.
    • 0:10:21We're setting it up just like normal.
    • 0:10:22Nothing new there.
    • 0:10:23Except the virtual width, virtual height, and all that stuff,
    • 0:10:26those have been moved out, if we look into source in a constants file.
    • 0:10:29So this file here, instead of having all the constants in main,
    • 0:10:32it kind of makes sense just to take them out, put them
    • 0:10:35in a file called constants.lua, and we can sort manage all that.
    • 0:10:39We can know immediately when we're looking at capital window
    • 0:10:42width, window height, et cetera.
    • 0:10:43And these are all constants.
    • 0:10:44If you have a constants file, we just can more easily
    • 0:10:46track it rather than having to grab through all of our files
    • 0:10:48to try and figure out what we were looking at.
    • 0:10:53And the constants are used here in our set up screen as before.
    • 0:10:59And then another sounds global table, just as before.
    • 0:11:03We have a bunch of different sound effects.
    • 0:11:05I've separated the music from the sound effects
    • 0:11:07just so that we can see at a glance, oh, this is the music,
    • 0:11:10these are the sound effects.
    • 0:11:11Pretty straight forward.
    • 0:11:12We have a state machine, as always.
    • 0:11:14And we're just going to use a StartState for this demonstration.
    • 0:11:18Setting it to Start.
    • 0:11:20Love.resize, love.update.
    • 0:11:22These are all functions we've seen before.
    • 0:11:23Nothing too new.
    • 0:11:25Love.keypress.
    • 0:11:25We have a global input table.
    • 0:11:26So as in the case of Flappy Bird, we can index into that input table anywhere
    • 0:11:30in our application and call love.keyboard.wasPressed[key],
    • 0:11:34which allows us to take input exclusively from main and use it
    • 0:11:38in other modules.
    • 0:11:40Here we're drawing the--
    • 0:11:42so this is the actual rendering code.
    • 0:11:45And we're doing this in our love.draw as opposed to a specific state
    • 0:11:49because this is actually going to apply to all states.
    • 0:11:51We're always going to have this background.
    • 0:11:53So rather than duplicate it over and over again,
    • 0:11:55in this instance, this minor bit of code,
    • 0:11:58we're going to display the background behind all the states.
    • 0:12:02So all the states are going to render over this background
    • 0:12:04and make it seem a little more cohesive.
    • 0:12:07We're going to draw at 0, 0 without rotation.
    • 0:12:09And then this bit of math here, the virtual width
    • 0:12:11divided by, and then background width minus one, end up being a scale factor
    • 0:12:15so that we can always scale it to be our virtual width.
    • 0:12:18Because the texture by default is some amount smaller than our actual window
    • 0:12:22or our actual virtual width and height, but by dividing virtual width
    • 0:12:26by whatever the background width of that image is by one,
    • 0:12:29we'll get a scale factor because virtual width is larger than the image.
    • 0:12:32We'll get a scale factor on X and Y that equates to it completely stretching
    • 0:12:37to fill our virtual width and height.
    • 0:12:41And recall that these two parameters are the scale on the X and the Y.
    • 0:12:44So it's going to be some, like, one point something or two point something.
    • 0:12:47Whatever it takes to end up filling the screen.
    • 0:12:50And then lastly here, the new bit I implemented
    • 0:12:52is just a display of frames per second function, which I think
    • 0:12:55is kind of important generally, and it's very easy to do.
    • 0:12:58I don't recall, I don't think we talked about it yet, but just
    • 0:13:00love.timer.getFPS.
    • 0:13:02And then I just draw in the top left in green
    • 0:13:04so that we can see it throughout all of our iterations of the game, what
    • 0:13:07are frames per second are.
    • 0:13:08If you want to monitor without having to look through your terminal or anything
    • 0:13:12like that, just displaying at the top, it's
    • 0:13:13standard practice in a lot of games.
    • 0:13:16If you've gone to the debug console or whatnot
    • 0:13:18or sort of looked into some of the hacks,
    • 0:13:20you'll see that in a lot of places.
    • 0:13:23So I talked earlier about dependencies.lua.
    • 0:13:25So this ties in as well to our effort to sort of modularize everything,
    • 0:13:30keep everything organized.
    • 0:13:31Instead of requiring everything at the top of main,
    • 0:13:34let's just put it all in a file and then we'll
    • 0:13:36know at a glance what we're requiring and we don't have to look through main
    • 0:13:40and make main 100 lines, potentially a lot more than it needs to be.
    • 0:13:44So requiring push, requiring class.
    • 0:13:47Same as we've done before.
    • 0:13:49Require source.constants.
    • 0:13:50We have access to those.
    • 0:13:51Require StateMachine, and then BaseState and StartState.
    • 0:13:55So let's go ahead and take a look at our StartState.
    • 0:13:58So I put states in a subfolder of source.
    • 0:14:01This is another effort to sort of keep things modular.
    • 0:14:03In this particular project, we won't have a lot of nested folders of code,
    • 0:14:08but I decided to put the states in their own folder
    • 0:14:10just so easily you can get access to all your states.
    • 0:14:14So we'll look at StartState here on line 21.
    • 0:14:18So recall in the StartState, we just had Breakout in the center of the screen
    • 0:14:21and then we had Start Game and High Scores.
    • 0:14:24So the user was able to highlight which state he wanted to look at.
    • 0:14:27So we need to keep track of which one is highlighted.
    • 0:14:29So all this variable's purpose is just to keep track.
    • 0:14:32So one or two.
    • 0:14:33One being Play Game, and two being High Scores.
    • 0:14:39And then here if we press up and down, then we--
    • 0:14:43because there's only two options effectively,
    • 0:14:45you can just flip whatever highlight is with one or two.
    • 0:14:48If you have a list of options that's more than two,
    • 0:14:50you'll need to increment one until it gets to whatever
    • 0:14:52X is, your number of list options.
    • 0:14:55And then if you press down at that point,
    • 0:14:57you should flip back up to the top.
    • 0:14:58And the same holds true for whether you're at option one.
    • 0:15:02You should go flip, rotate to the bottom of your list
    • 0:15:05so that it looks as if you've gone all the way around.
    • 0:15:08And-- then we're just playing a sound here when we do that.
    • 0:15:11We have a love.keyboard.wasPressed[escape] call
    • 0:15:14here.
    • 0:15:14It's not global anymore because there are some states in our application
    • 0:15:18where we might want to press Escape to actually go backwards,
    • 0:15:20and we'll see that.
    • 0:15:22And so rendering here.
    • 0:15:23We render Breakout with a large font.
    • 0:15:25Now that we can access G fonts at large key in the center of the screen,
    • 0:15:29set medium font.
    • 0:15:30And then we're going to render our two text fields one after the other.
    • 0:15:36But if highlighted is equal to one, then we're
    • 0:15:38going to set it to some blue color, which is one of three--
    • 0:15:40255, 255, 255.
    • 0:15:43And then render it.
    • 0:15:44And then make sure to reset the color after that because recall
    • 0:15:47love 2D is sort of like a state machine in its own right,
    • 0:15:50where if you set the color to something, whatever you draw and render
    • 0:15:53after that, be it images or text, will adopt that color.
    • 0:15:56So having everything be 255, 255, 255, 255,
    • 0:16:00which is pure white, completely opaque, has
    • 0:16:02the effect of drawing everything completely opaque.
    • 0:16:04But if you don't do that, your images or whatnot that you draw afterwards
    • 0:16:07will be tinted or transparent, which you most of the time don't want.
    • 0:16:10But you might sometimes want that, and we'll actually
    • 0:16:13see that in the PaddleSelectState.
    • 0:16:14And same thing holds true here.
    • 0:16:15If highlighted is two, do the exact same thing.
    • 0:16:19And so if we run this application, which is mainly
    • 0:16:22just a subset of what we saw before, we can move up and down
    • 0:16:26between Start and High Scores.
    • 0:16:28But if we press Enter on any of them, nothing
    • 0:16:30happens because we have no event handlers actually taking care of that.
    • 0:16:33But we have the image scaled to the screen, we have Breakout in the middle,
    • 0:16:37and we have our two menu options there.
    • 0:16:40So Breakout1.
    • 0:16:41So this is where we start to dive a little bit into sprite sheets, which
    • 0:16:46is a major component of game development, 2D game
    • 0:16:49development that we'll be looking at in the future and in this application.
    • 0:16:53But a sprite sheet is just, ultimately, rather than have--
    • 0:16:57I don't know how many images there are on this sprite sheet here.
    • 0:17:00But however many of these files, just have
    • 0:17:03one file put them all together, and then using rectangles, define
    • 0:17:07where all the different sprites are.
    • 0:17:08And then when we want to draw, use those rectangles
    • 0:17:11and just tell love.graphics.draw, I want you
    • 0:17:14to draw this texture, this sprite sheet, but I want
    • 0:17:16you to draw just this section of it.
    • 0:17:19You'd pass it in a quad, which is just simply rectangle with height, X and Y.
    • 0:17:23And love 2D will know, OK, I'm going to draw the image, but only this bit.
    • 0:17:27And it has the effect of looking as if you're only drawing tiny little images
    • 0:17:31as opposed to one monstrous image.
    • 0:17:34And the functions that are relevant for us to look at
    • 0:17:36are love.graphics.newquad, which takes an X, Y, width, and a height.
    • 0:17:41And also a dimensions object, which you get from an image.
    • 0:17:45We'll see that.
    • 0:17:46And all that basically is, I believe, is just an X, Y, width and height as well.
    • 0:17:50Or just a width and a height, rather, from whatever image
    • 0:17:54you want to create quads for.
    • 0:17:57And then love.graphics.draw, we've already seen it,
    • 0:18:00but this is a different signature.
    • 0:18:01This has texture, quad, X, Y. Quad being the second argument.
    • 0:18:06And when it takes in this quad, it knows to only draw
    • 0:18:09that defined rectangle of image to the screen.
    • 0:18:13And so we'll go ahead and take a look now at Breakout1.
    • 0:18:17AUDIENCE: [INAUDIBLE]
    • 0:18:22COLTON OGDEN: The question was, are there any tools
    • 0:18:24so that we don't have to guess where the quad is when we're doing the sheet?
    • 0:18:28Yes, there are a lot of the time.
    • 0:18:31I looked and saw a couple, but I haven't tested them thoroughly myself.
    • 0:18:35For simpler examples like this, it's usually
    • 0:18:37easy enough to programmatically do it.
    • 0:18:39But yeah, when you get into having giant sprite atlases where
    • 0:18:42you have especially things that are not necessarily symmetrical
    • 0:18:46or rectangular looking even though they still need to be defined rectangularly,
    • 0:18:50it's often best to use a tool like that.
    • 0:18:54There are, I do believe, I just haven't used them.
    • 0:18:57I can bring it up in a future lecture so we can discuss.
    • 0:19:03Any other questions before we carry into Breakout1?
    • 0:19:08All right.
    • 0:19:09So I'm going to go ahead and open up the very first thing
    • 0:19:12we should look at on Breakout1.
    • 0:19:14In the source directory, we have a new file.
    • 0:19:18And from here on out, I'm going to assume,
    • 0:19:20we're going to always assume that when we introduce a new file,
    • 0:19:24we're going to include it in dependencies.lua.
    • 0:19:28And so in this case, all we need to do is just say require source/util.
    • 0:19:32And as you can see, we're also adding a PlayState to this demonstration.
    • 0:19:38But from here on out, I won't make mention of us actually
    • 0:19:41adding it to our project.
    • 0:19:42So util.lua is the module that contains the code
    • 0:19:47we're going to use to actually generate quads for a given sprite sheet.
    • 0:19:51And this function, all it does, is it takes an atlas or sprite sheet--
    • 0:19:54the names are synonymous.
    • 0:19:56You'll hear them both.
    • 0:19:57Or we pass it an Atlas, we pass it the width of the tile that we want
    • 0:20:02and the height of the tile that we want.
    • 0:20:04It's going to get the width and the height of the sheet here.
    • 0:20:07So every image has a function called get width and get height,
    • 0:20:11so we're just going to do that.
    • 0:20:13And specifically the sheet width and sheet height
    • 0:20:16are the width of the image divided by tile width and tile height.
    • 0:20:19So we know how many times we need to iterate over the sprite sheet
    • 0:20:23to generate a rectangle.
    • 0:20:24We're dividing it up based on the size of our tiles.
    • 0:20:29And then we just basically do a simple nested four loop here.
    • 0:20:32We start a counter and a sprite sheet.
    • 0:20:34This sprite sheet is going to be a table that holds all of our quads.
    • 0:20:37We just say for Y, get zero.
    • 0:20:39Sheet height minus one.
    • 0:20:40So starting at the top left, going down.
    • 0:20:44And starting at the top going down, and then x equals zero,
    • 0:20:46starting at the left going right.
    • 0:20:50At sprite sheet, sheet counter, which is one here because in lua,
    • 0:20:55tables are one indexed.
    • 0:20:57We're going to create a new quad at X times tile width, Y times tile width.
    • 0:21:02Give it the width and the height of our tile.
    • 0:21:05So just whatever we passed into our function signature.
    • 0:21:07Here it will often be in this case be 16 by 32
    • 0:21:10because that's the size of the bricks.
    • 0:21:14And then we pass in the last parameter that we saw in the slide,
    • 0:21:17which is atlas:getdimensions.
    • 0:21:19And then we just increment our sheet counter here.
    • 0:21:21And then at the end of this, when we're all done, we'll return this.
    • 0:21:23We'll have a table of quads that we can then
    • 0:21:25use that are in a sort of one, two, three, four, five, six, seven, eight.
    • 0:21:31Well, I should say, one, two, three, four, five, six, seven, eight top
    • 0:21:34left to bottom right of all the sprites in our sheet to make it
    • 0:21:38super easy to look at.
    • 0:21:40We have another function here.
    • 0:21:42Lua doesn't by default, have a slice function, but we are just adding to it.
    • 0:21:45Table.slice.
    • 0:21:47It takes the table a first, the first entry in the table that we want,
    • 0:21:52the last entry, and then the step between them.
    • 0:21:54Just like Pythons slice function, it just iterates over the
    • 0:22:00for loop, which is first till one.
    • 0:22:02So one by default. Until the last or until whatever this sort of number sign
    • 0:22:10is the size of a table, which I don't think we've introduced yet.
    • 0:22:13But basically, if we pass in last, it'll stop there, otherwise just
    • 0:22:18assume we want the whole entire table.
    • 0:22:20And then this comma here at the end, which has step or one,
    • 0:22:25you can pass in a step at the end of a for loop as a third argument,
    • 0:22:28and that will be however much increments or decrements the loop that you're in.
    • 0:22:33So by default, just one.
    • 0:22:35We go one, then we go to two, then we go to three.
    • 0:22:37But you can set it to negative one.
    • 0:22:39And so if you say four i gets three to one minus one,
    • 0:22:42you'll go three, two, one.
    • 0:22:44And you can't do normally a step, which is what we do here.
    • 0:22:50No, you can do a step, but you can't slice,
    • 0:22:52which is why we have here sliced at number of slice plus one gets table i,
    • 0:22:58and then eventually we return slice.
    • 0:22:59So it just returns just a segment of whatever table we're in.
    • 0:23:04And then the important function here that we're actually
    • 0:23:07going to use in our application, we're going to generate quads paddles.
    • 0:23:11And so this takes X and Y, 0 and 64.
    • 0:23:15And if we look back at our paddles here, we
    • 0:23:19can see that we have various different sizes.
    • 0:23:22So we have a small one, a medium one, a large one, and then a really large one.
    • 0:23:25So if we want to get every single paddle in our sprite sheet,
    • 0:23:30small, medium, large, giant, notice that we have four blocks
    • 0:23:33and within each of those blocks we have four different sizes.
    • 0:23:36So we can just iterate over this four times
    • 0:23:38and then just define whatever the size of this rect
    • 0:23:41is, that rect, that rect, and that rect.
    • 0:23:43And we'll see the math for it here.
    • 0:23:45If I go zero to three, for i, get zero till three.
    • 0:23:50We're going to go ahead because that will give us four.
    • 0:23:53So that's how many times we want to iterate over the sprite sheet
    • 0:23:57to get the separate quads.
    • 0:24:00We'll get the smallest one.
    • 0:24:01So quads counter.
    • 0:24:03We initialize counter to one.
    • 0:24:05Gets love.graphics, that new quad at X, Y, with the 32 and 16.
    • 0:24:11Oh, and X and Y default at 0 and 64 here because the note--
    • 0:24:16recall that these are all 16 tall here.
    • 0:24:21So we're starting Y at 64 so that we start right here.
    • 0:24:24And we're starting X at zero because it's on the left side.
    • 0:24:28So we'll do that.
    • 0:24:29We'll increment counter.
    • 0:24:31Get it at 32 wide by 16 tall.
    • 0:24:34Those are the actual dimensions of the smallest one.
    • 0:24:36The same exact logic applies for medium and for large.
    • 0:24:40Only that we're adding 32 and then we're making it size 64,
    • 0:24:44and then we're adding 96 to X at size 96 because they're getting wider,
    • 0:24:49but they're also offsetting more to the right.
    • 0:24:52And then the last bit is pretty much the same thing as before, except now we're
    • 0:24:56going Y plus 16 back to X because we've gone down a row in our sprite sheet
    • 0:25:01The paddle width at that point is 128, but still 16 pixels.
    • 0:25:06And then here at the bottom because we want to do this four times,
    • 0:25:13we want to go through the chunks are effectively 32 pixels
    • 0:25:16because we're going 16, 16, 16, 16.
    • 0:25:19We're going to add 32 to Y and then go to the next set of four paddles.
    • 0:25:23So this is how we're effectively getting all of the paddle sprites,
    • 0:25:26and they;re going to be stored one through X where I believe X is 16.
    • 0:25:30So we'll have 16 quads defined in our sprite sheet
    • 0:25:34thereafter that we can then return.
    • 0:25:36So I'm going to go back to main.lua now on line 64.
    • 0:25:43Here we have a new global table called gframes.
    • 0:25:47We'll be able to access this anywhere we want to draw stuff.
    • 0:25:49And it's just the same thing that we just saw.
    • 0:25:52Generate quads paddles, and we just pass it in our main texture.
    • 0:25:55And our main texture is this.
    • 0:25:57This is what our main texture looks like.
    • 0:25:59And then we're going to index it.
    • 0:26:01We're going to say it gets the key paddles,
    • 0:26:04because in that particular table was just the quads for our paddles.
    • 0:26:10So in the future, we just need to call love.graphics.drawtexture and then
    • 0:26:14index into gframes paddles at whatever paddle we want.
    • 0:26:19And that's how we can keep track of what we want to draw paddle wise.
    • 0:26:24And in this particular demo we have a new paddle class
    • 0:26:28because paddle is a thing in our game.
    • 0:26:30We can represent it as sort of a class or an object.
    • 0:26:33So we'll define a class for it.
    • 0:26:36Everything is pretty simple thus far.
    • 0:26:38Gets an X and a Y. Dx is zero with height.
    • 0:26:41Skin.
    • 0:26:42The skin is going to be what color it is.
    • 0:26:45We need to keep track of that.
    • 0:26:47And then the size, because size will be how we sort of offset
    • 0:26:51into our paddles, our quads, because the sizes are small, medium, large, giant.
    • 0:26:56One, two, three, four times four.
    • 0:26:58So one, two, three, four for the first set and then five,
    • 0:27:00six, seven, eight for the second set.
    • 0:27:03Those are all sort of by color.
    • 0:27:04So we can just multiply skin times--
    • 0:27:07or we can multiply whatever our size is by skin
    • 0:27:12and that will give us the current frame, the current quad that we want in order
    • 0:27:15to draw to the screen.
    • 0:27:18And then on line seven--
    • 0:27:21so this is keyboard input here.
    • 0:27:23Stuff that we've seen before.
    • 0:27:24If we're pressing left or right, then the paddles should move.
    • 0:27:28Dx should be set left or right.
    • 0:27:31We want to clamp it.
    • 0:27:32We saw this, we've seen this as well.
    • 0:27:33Clamp the input to the left and the right side of the screen.
    • 0:27:37If the dx is less than zero, do math.max and math.min otherwise
    • 0:27:42if we're moving to the right.
    • 0:27:43And then here, this is actually where we tie it all together
    • 0:27:46and we actually use the quads to draw something onto the screen.
    • 0:27:49So we're calling love.graphics.draw just our texture, our main texture.
    • 0:27:54And then gframes at paddles at our current size, which is two.
    • 0:28:01We want to by default have the medium size plus four times whatever
    • 0:28:07our skin is, minus one.
    • 0:28:10So if our skin is one, which is the blue skin, we won't add anything to it.
    • 0:28:18It'll just be four times zero.
    • 0:28:20But if we have the next one, it'll be two minus one,
    • 0:28:22so we'll end up adding four to that.
    • 0:28:24And because we're adding four to it times whatever that skin is,
    • 0:28:28it will just basically put us four quads in,
    • 0:28:32which is the next, the exact same paddle, but the next color.
    • 0:28:36And then lastly what we'll look at here is the PlayState.
    • 0:28:41So we had just the StartState before, but now we
    • 0:28:44want to actually test to make sure we can draw a paddle,
    • 0:28:47move it around the screen.
    • 0:28:48So we're going to implement a simple PlayState here.
    • 0:28:50So on line 20, we're just calling self.paddle gets paddle.
    • 0:28:53We're initializing a new paddle object.
    • 0:28:56And then we're keeping track of also this
    • 0:28:58is a simple, like, pause demonstration.
    • 0:29:01If self.paused, then-- actually yeah.
    • 0:29:06Did I say self.paused?
    • 0:29:06I did.
    • 0:29:07OK.
    • 0:29:07I just don't initialize it to anything.
    • 0:29:09I should have set self.paused to false here.
    • 0:29:15If self.paused, we're going to test to see whether we're pressing space,
    • 0:29:19and if we are, unpause it.
    • 0:29:22Otherwise, basically just do the same exact thing in reverse.
    • 0:29:26If we press space, pause the game, play a sound, et cetera.
    • 0:29:31Here on line 39, we're just going to call update on the paddle.
    • 0:29:34Which, just remember, test for left or right input.
    • 0:29:37Here we want to be able to escape the game,
    • 0:29:38so we're going to have a handler for escape.
    • 0:29:41Render the paddle on lines 47, which will do the love.graphics.draw
    • 0:29:45with a quad as we saw before, but it'll use
    • 0:29:47the skin and the size of that paddle to index into the quads tile sheet
    • 0:29:52appropriately.
    • 0:29:54And then here if we're paused, let's just draw
    • 0:29:57some text in the middle of the screen that just says Pause.
    • 0:30:00And we use the large font.
    • 0:30:01So we can go ahead and demo this now and see everything come together.
    • 0:30:08We have as before our StartState.
    • 0:30:11But if we press Enter, now we go to our PlayState
    • 0:30:13and we just have a paddle at the bottom of the screen.
    • 0:30:15It's size two, skin one.
    • 0:30:18Just the blue skin.
    • 0:30:19And we can move it left or right like that.
    • 0:30:21And if it hits the left side of the screen, it will stop.
    • 0:30:24And if it hits the right side of the screen, it will stop as well.
    • 0:30:27So we've made progress, but this is one of the fundamental things
    • 0:30:30I'd like to showcase today is just, like, using quads and categorizing
    • 0:30:35them, organizing them, and being able to draw
    • 0:30:38your assets from a large compiled image rather than keep track
    • 0:30:43of however many images it would take.
    • 0:30:45And you have to name all of them and sort them.
    • 0:30:47It would just be a big pain.
    • 0:30:49So yeah.
    • 0:30:49Definitely going forward when you have more than one sprite,
    • 0:30:52you want to sort of put it together in one sheet,
    • 0:30:55and that's how we can accomplish that.
    • 0:30:57But we don't have bricks, and this is probably
    • 0:31:00the other big main component of Breakout besides the paddle and the ball.
    • 0:31:05We want to have bricks that we can actually hit and aim for on the screen.
    • 0:31:08So this update will address that.
    • 0:31:10So let's go ahead and take a look at Breakout2 in main.lua.
    • 0:31:16I'm going to open it up here.
    • 0:31:21On line 66, you can see we have a new table in our gframes.
    • 0:31:29Because we had one just for paddles, we took out
    • 0:31:32just the paddles from our sprite sheet.
    • 0:31:33We're going to do the same thing for just the balls.
    • 0:31:36So we're going to look at-- if we look here,
    • 0:31:38we can see that the balls sort of come after all of the bricks here
    • 0:31:43and they're just laid out in eight pixels wide by eight
    • 0:31:46pixels tall increments here.
    • 0:31:48So four pixels to one brick, four balls to one brick,
    • 0:31:52two balls to one horizontally, and then two balls vertically.
    • 0:31:56And so what we'll end up doing is just a simple function in our util
    • 0:32:02that takes a look at that.
    • 0:32:04So let's go ahead and take a look at our util.lua, which we've made changes to.
    • 0:32:14And so what this is going to do is sort of do the same thing
    • 0:32:17that we did before.
    • 0:32:20It has to iterate.
    • 0:32:21So notice we have two rows of balls.
    • 0:32:23We have these four and we have these three.
    • 0:32:26So we want to iterate four times.
    • 0:32:27You want to find whatever the offset is here,
    • 0:32:29the X and Y. So it looks like three times 32 and then three times 16.
    • 0:32:35So 96 by--
    • 0:32:36I can't do math.
    • 0:32:37Whatever 16 times three is.
    • 0:32:39And then we'll end up 48.
    • 0:32:40And then we'll have--
    • 0:32:42which is what we do here.
    • 0:32:43So we have two iterations.
    • 0:32:46So a four loop that goes from zero to three.
    • 0:32:48So the top row, the four.
    • 0:32:51We'll set a counter to one here.
    • 0:32:52And notice also 96 and 48.
    • 0:32:54That's the X and the Y that we're setting.
    • 0:32:56That's where the offset is for the individual ball sprites.
    • 0:33:01Quads at counter gets--
    • 0:33:03and notice also quads is a table.
    • 0:33:05We're going to return this.
    • 0:33:06Quads at counter gets love.graphics.newquad at X, Y.
    • 0:33:10Eight pixels wide, eight pixels tall.
    • 0:33:12That's how large the balls are.
    • 0:33:13And then we're going to add eight to it because we're going to the right.
    • 0:33:17So this iteration just goes left to right.
    • 0:33:20And then here we're going to do basically X being set to 96
    • 0:33:25and then Y to 56.
    • 0:33:27And then because we were editing X directly in here,
    • 0:33:30we want to reset X back to 96, but then also add
    • 0:33:33the eight pixels so that we have the start for the next row
    • 0:33:36vertically, so at Y 56.
    • 0:33:38Do the exact same thing here, but only do it
    • 0:33:40three times because recall there is four balls on top
    • 0:33:43and then three balls on bottom.
    • 0:33:44And then return it at the very end.
    • 0:33:46And so now we have just an individual table.
    • 0:33:49We don't need to keep like one monstrous table of quads,
    • 0:33:51which I find sort of disorganized.
    • 0:33:53We can just have a table of frames for the paddles,
    • 0:33:57and the balls, and the bricks as we'll see.
    • 0:34:02Actually, I have it up here I think.
    • 0:34:05Maybe not.
    • 0:34:08So in ball-- oh, actually, hold on.
    • 0:34:12Sorry.
    • 0:34:12So we were looking at--
    • 0:34:17I skipped over this one on accident.
    • 0:34:19So the bounce update.
    • 0:34:19So everything I just said is relevant, but I accidentally
    • 0:34:22hit that right two times.
    • 0:34:23We want to go to the bounce update because this is slightly simpler.
    • 0:34:27So we were just talking about the ball, which is perfect.
    • 0:34:30So we're going to take the ball and then we're going to add that to the scene,
    • 0:34:33and we're just going to implement bouncing off the walls.
    • 0:34:36So actually, pretty identical to the code
    • 0:34:39we saw for Pong where you just detect whether the ball has
    • 0:34:43gone past the left, right, or top edge of the screen.
    • 0:34:45In this case, it will also allow us to go to the bottom of the screen
    • 0:34:48and we'll also implement colliding with the paddle
    • 0:34:51so then get a sense of the actual game play and what that feels like.
    • 0:34:54So everything is currently current.
    • 0:34:57So we're going to go--
    • 0:34:59after talking about the function to actually get the individual ball
    • 0:35:03quads out of the spreadsheet, we're going
    • 0:35:05to look at the ball class which is going to allow us to spawn them in our scene.
    • 0:35:10So a ball takes a width and height of eight.
    • 0:35:12No velocity.
    • 0:35:13But we're going to allow ourselves to initialize the ball with the skin,
    • 0:35:17and we'll see this later just as a cutesy little thing to you
    • 0:35:21use the actual individual sprites rather than just one constant sprite.
    • 0:35:24We're just going to give it a random number between one and seven
    • 0:35:27because there are seven quads.
    • 0:35:30And then we'll just use gframes balls and math dot random number
    • 0:35:35to get the actual ball spread that we want.
    • 0:35:38And so we have a simple collides function within ball
    • 0:35:40that would allow us to check to see whether we've collided with something
    • 0:35:45that has a X, Y width and a height.
    • 0:35:47So it's a simple A, B collision detection.
    • 0:35:54And then here we have reset.
    • 0:35:56Just resets it to the middle of the screen.
    • 0:35:58Update applies velocity.
    • 0:35:59Stuff we've already seen.
    • 0:36:02This is where we actually implement bouncing off the walls.
    • 0:36:05So if X is less than or equal to zero, greater than
    • 0:36:07or equal to virtual width minus eight, or less than or equal to zero,
    • 0:36:11this should be where we reverse the velocity.
    • 0:36:14In the case of it bouncing off the left side, we want to reverse the X velocity
    • 0:36:18but keep it going up.
    • 0:36:19If it hits the top, then we want to reverse
    • 0:36:21the Y velocity to keep it moving in whatever direction it was moving.
    • 0:36:24And same thing with the right hand wall.
    • 0:36:27And then play a wall hit sound.
    • 0:36:29And we're incorporating the sounds sort of as we go today
    • 0:36:31just because they're so simple.
    • 0:36:33And it's also kind of nice just to have a little bit of feedback
    • 0:36:35when you're actually endpoint of the game.
    • 0:36:37And the exact same code is here for drawing.
    • 0:36:39So we have main texture, but now we're using gframes balls,
    • 0:36:43and then we're indexing that at self.skin.
    • 0:36:46And recall that we just set self.skin in here.
    • 0:36:48So all we need to do to just make it random
    • 0:36:49is just wherever we create a new ball, just give it a math.random7,
    • 0:36:53and then that will index into that quads table
    • 0:36:57so we can draw a different ball texture each time.
    • 0:37:01And so let's go ahead and see--
    • 0:37:03oh, actually, no.
    • 0:37:03And one last thing we need to look at is the PlayState
    • 0:37:06has a little bit of new code as well.
    • 0:37:09We're going to spawn a ball, so this is where we do it here.
    • 0:37:11I'm not doing it random, but I could do it random here if I wanted to.
    • 0:37:14I could math.random7, and every time we boot up
    • 0:37:17the game it's going to be a different color because it's
    • 0:37:19going to be a different skin.
    • 0:37:22We need to update the ball.
    • 0:37:23So on line 50 we just update it like we do the paddle.
    • 0:37:26And then on line 52, we're just testing to see
    • 0:37:29whether it collides with the paddle because we're using just simple A,
    • 0:37:32A, B, B. If it collides with the paddle, we can assume it was coming down.
    • 0:37:36We can just reverse as delta Y.
    • 0:37:38Now, does anybody know what might be a current issue
    • 0:37:43with the current implementation of this function?
    • 0:37:46Particularly with this line.
    • 0:37:51AUDIENCE: [INAUDIBLE]
    • 0:38:01COLTON OGDEN: It will.
    • 0:38:02You're on the right track.
    • 0:38:03The answer was, if the ball is coming from the side,
    • 0:38:06it won't necessarily be bounced back up in the right Y direction.
    • 0:38:11If it's coming from the side, it will always, in this case,
    • 0:38:13be coming from up above.
    • 0:38:15So it always still be reversing in the right delta Y.
    • 0:38:17But what's going to happen if it comes in at an angle
    • 0:38:22and then isn't basically reset?
    • 0:38:28Like right now if it comes at an angle and it
    • 0:38:31gets caught-- let's say it's like below the top edge of the paddle.
    • 0:38:36AUDIENCE: [INAUDIBLE]
    • 0:38:37COLTON OGDEN: You're going to get an infinite collision
    • 0:38:40loop because we're not resetting it's position,
    • 0:38:42we're only updating its velocity.
    • 0:38:44If it comes in at the right angle from the side,
    • 0:38:46it's going to get stuck inside the paddle
    • 0:38:50and then it's going to cause a little bit of funky behavior.
    • 0:38:52I'll try and see if I can make that happen in my demonstration here.
    • 0:38:55But that's the gist of all of these updates.
    • 0:38:58So if we go to Start, we can see immediately we have a ball.
    • 0:39:02And when it hits the sides or the top, it bounces accordingly.
    • 0:39:05It hits the paddle.
    • 0:39:06So when it comes in from the top flush on the top, it flips the Y velocity.
    • 0:39:11Let's see if I can get it at an angle here.
    • 0:39:14There it is.
    • 0:39:15It'll get stuck.
    • 0:39:17And so whenever you sort of do A, A, B, B collision detection,
    • 0:39:22just remember to always reset the position of whatever it
    • 0:39:25is that collided that's moving so that it doesn't clip
    • 0:39:29and get stuck inside of something else over and over again.
    • 0:39:31Yes.
    • 0:39:32AUDIENCE: [INAUDIBLE]
    • 0:39:42COLTON OGDEN: The question is I'm always doing love space dot,
    • 0:39:45and as opposed to just running things from using
    • 0:39:50the complete path of whatever the file is, in order to do that-- so are you
    • 0:39:55on a Mac or a Windows machine?
    • 0:39:56AUDIENCE: [INAUDIBLE]
    • 0:39:57COLTON OGDEN: So on a Windows machine it is a little trickier,
    • 0:40:01but I've found a really nice sort of plug-in for VS Code.
    • 0:40:07So if you're VS Code, which is the editor that I use, it has plug-ins
    • 0:40:11and one of the plug-ins that you can download is for Love2D
    • 0:40:14and it has a config where if you just press Alt L,
    • 0:40:19it will run whatever directory you're currently in,
    • 0:40:22whatever project you're currently in.
    • 0:40:24It will call Love.
    • 0:40:25It adds it to your path for you.
    • 0:40:27So download the Love2D plug-in on VS code if you want that to work.
    • 0:40:31I'm on a Mac, so I can edit what's called my batch profile,
    • 0:40:34and alias Love to its complete path in my file system.
    • 0:40:40And you can do the same thing with--
    • 0:40:43I don't know how it would work with Windows in terms of aliasing,
    • 0:40:48but it's essentially the same thing as typing out the entire path to Love,
    • 0:40:52but only I'm changing it to another word.
    • 0:40:55I'm changing it to Love.
    • 0:40:56So I'm setting Love equals to application slash
    • 0:40:59love.app/content/resources et cetera.
    • 0:41:02So good question.
    • 0:41:04I would download on Windows.
    • 0:41:05I'm a big fan of VS Code and the Love2D plug-in.
    • 0:41:07I would recommend looking into that.
    • 0:41:09And I'm sure there are other plug-ins, and there's a page also
    • 0:41:12on the website--
    • 0:41:13I don't have a browser open at the moment.
    • 0:41:15But on the wiki, you can look at the Getting Started page.
    • 0:41:19I believe it's like love2d.com/wiki/gettingstarted.
    • 0:41:21They have a bunch of instructions for different operating
    • 0:41:23systems and different text editors that allow you to get sort
    • 0:41:26of a more efficient workflow going.
    • 0:41:29So any other questions?
    • 0:41:34All right.
    • 0:41:35So we did the bounce update.
    • 0:41:38Now we can finally edit the bricks.
    • 0:41:40Add in the bricks, I should say.
    • 0:41:42So these are pretty simple.
    • 0:41:44So we're going to take a look at it.
    • 0:41:47And right now we're not going to do any sort of fancy procedural generation,
    • 0:41:50we're just going to get some bricks on the screen.
    • 0:41:52Just some easy bricks.
    • 0:41:55Or rather, we will get some very basic procedural generation,
    • 0:41:58but not to the level that we'll see soon.
    • 0:41:59We'll see that very soon.
    • 0:42:01OK.
    • 0:42:02So I'm going to go into my main.lua here.
    • 0:42:05I'm going to go into the Breakout3.
    • 0:42:12And same thing that we did before on line 67,
    • 0:42:15we just have a new bricks table in our gframes.
    • 0:42:19And it just generate quads bricks.
    • 0:42:21We call from util.lua, so we can look at that really quick as well.
    • 0:42:25This one's actually really easy.
    • 0:42:28Sourceutil.lua.
    • 0:42:30Because they start at the very top of the screen,
    • 0:42:33we can assume that-- we could effectively
    • 0:42:37treat this whole thing as if it were just these
    • 0:42:40and just generate quads at a constant width and height
    • 0:42:43because, effectively, we only need a subset of the frames that's generating.
    • 0:42:47Because it's generating them this way, top to bottom, left to right,
    • 0:42:52we can just grab all the way up to here using
    • 0:42:55table.slice, which we saw before, and not
    • 0:42:59worry about indexing into any weird, like,
    • 0:43:01having any constants X and Y that we need to index with in order
    • 0:43:04to get an offset.
    • 0:43:05We can just do a very simple--
    • 0:43:07if we go down to line 57, generate quads bricks, it just does a table.slice.
    • 0:43:15And so within that, we're going to generate quads atlas 32,16.
    • 0:43:19So this is going to have the effect of dividing up our sprite sheet by 32
    • 0:43:23by 16 pieces.
    • 0:43:24It's going to generate all of these just fine,
    • 0:43:27but then it's going to have quads here, here, here, here, here
    • 0:43:30that don't line up with the quads that you see here
    • 0:43:32because it's just blindly assuming that all of the sprites in that sheet
    • 0:43:36are the same size because that's all we're doing.
    • 0:43:38We're just calling generate quads, which if you recall,
    • 0:43:41just generates a fixed size width and height throughout our entire atlas,
    • 0:43:46which is great for a lot of sheets that are symmetrical,
    • 0:43:49but there are cases where we have, like, for example here, where
    • 0:43:52our spreadsheet is asymmetrical.
    • 0:43:53We have paddles of differing sizes, we have
    • 0:43:55the balls which are eight by eight, we have the bricks,
    • 0:43:58we have the other power ups at the bottom.
    • 0:44:01But the generate quads bricks takes in that table
    • 0:44:05that we're generating, which is going to be a bunch of frames
    • 0:44:08that we don't want.
    • 0:44:09Many of them clipped, half clipped.
    • 0:44:11And then we're just going to take it from one to 21.
    • 0:44:14And when we do that, one to 21 is effectively-- that's how many of these
    • 0:44:18there are.
    • 0:44:19So 18 and then one, two, three.
    • 0:44:22So from one to 21, all of those.
    • 0:44:24That will be all the bricks.
    • 0:44:25We can throw away all the rest of the quads
    • 0:44:28and just blindly assume that they're all the same size.
    • 0:44:36So any questions on how quads or how any of these tables are working?
    • 0:44:41OK.
    • 0:44:44So we're going to go ahead.
    • 0:44:45We have a new class now, brick.lua.
    • 0:44:49So simple building blocks.
    • 0:44:52In brick.lua on line 30, we have a flag called in play.
    • 0:44:57self.inplay gets true.
    • 0:44:58And so we're just going to use this to render.
    • 0:45:00We're just going to say, if it's in play, render it.
    • 0:45:02If it's not, don't render it.
    • 0:45:04It's that simple.
    • 0:45:04That way we don't have to worry about object deallocation or anything fancy.
    • 0:45:08We have all of our bricks and whether it's in play
    • 0:45:10or not, render it or perform update logic.
    • 0:45:12And if it's not in play, just pretend it doesn't exist.
    • 0:45:14Just ignore it.
    • 0:45:17We're only going to have like 30 or I don't know how many,
    • 0:45:2113 max by four bricks in our scene at once, so worrying about freeing memory
    • 0:45:26isn't really an issue.
    • 0:45:28But if you have a million different things getting generated all the time,
    • 0:45:34having simple in play is false might not always
    • 0:45:37be viable because you need to store all that memory for all those objects.
    • 0:45:40So just a shortcut here, but not necessarily best
    • 0:45:44practice for very large games.
    • 0:45:47But certainly great and simple for small games.
    • 0:45:53On line 37, we define a function called brick hit.
    • 0:45:56And all this does is just play a sound effect and set in play to false.
    • 0:46:00And so all we're going to do is just check
    • 0:46:03to see whether there's a collision and then just call this hit function,
    • 0:46:06play a sound, and then just pretend it doesn't exist anymore.
    • 0:46:10And then render, all render does is if it's in play, check the in play flag,
    • 0:46:15draw main at bricks or using our bricks table here that we created.
    • 0:46:23And then we're going to start at one and then
    • 0:46:25we're going to index it based on our color minus one times four,
    • 0:46:30and then we're going to add it's tier.
    • 0:46:31So there are, if you recall, one, two, three, four,
    • 0:46:36five colors and four tiers.
    • 0:46:41And so what we're going to do is we're going to jump between the colors.
    • 0:46:44So we'll go value one, value two, value three, value four, value five.
    • 0:46:49That will be our first five or I guess six.
    • 0:46:52That will be our first six bricks.
    • 0:46:55And then we're going to go one, two, three-- or we're going to add,
    • 0:46:59we're going to have a tier basically.
    • 0:47:00It'll be one, two, three, or four.
    • 0:47:04And if it's at tier one, then we can just add--
    • 0:47:07basically to index into whatever tier we're on,
    • 0:47:10we just need to add tier minus one to whatever our index is.
    • 0:47:13So here if our tier is one, then we just want to render this block.
    • 0:47:17We don't want to go to the next one.
    • 0:47:18So we're just going to say tier minus one.
    • 0:47:20We're going to add--
    • 0:47:22so one minus one is zero.
    • 0:47:23So we're going at zero to this, get this.
    • 0:47:25But if tiers two, we'll add one, and two, and three.
    • 0:47:29And then we just multiply whatever brick we want by our color.
    • 0:47:34Multiply it by four to get an offset for whatever our actual color is.
    • 0:47:40So we take our color, figure out where on the sheet it is,
    • 0:47:43and then just add our tier to it in order to index
    • 0:47:46into our spreadsheet accordingly.
    • 0:47:49And so that's what the math here is doing.
    • 0:47:52And if we go back to our PlayState--
    • 0:48:02and I'm going to start moving a little bit faster
    • 0:48:04just so we can keep caught up.
    • 0:48:06But in our PlayState, one thing that we notice here,
    • 0:48:10we have a new class called level maker that we're seeing,
    • 0:48:13which was a function called createmap.
    • 0:48:15We're going to take out all the logic for generating our levels
    • 0:48:17and we're just going to put it in one place.
    • 0:48:19We're going to call that level maker.
    • 0:48:20Rather than in our different states that maybe
    • 0:48:24generate the bricks like the PlayState or I
    • 0:48:26guess it would be the ServeState, VictoryState,
    • 0:48:30I guess, rather than generating all the bricks
    • 0:48:33in that state within it's innate code, let's just make a level maker
    • 0:48:37and we can just say, OK, set bricks to levelmaker.createmap,
    • 0:48:41which will return a table of bricks.
    • 0:48:46Same-- excuse me-- logic as we saw before.
    • 0:48:51In this case, we're just going to iterate for k brick
    • 0:48:53in pairs of self.bricks.
    • 0:48:57If the brick's in play and it collides, if the ball collides with it,
    • 0:49:00then hit it, which will set it not into play.
    • 0:49:02So simple A, A, B, B.
    • 0:49:08And then lastly, we have our render logic here,
    • 0:49:10which is going to take that bricks table and just iterate over it.
    • 0:49:13And the last thing we should probably look at
    • 0:49:15is the actual level maker itself, which in this case is very simple,
    • 0:49:18but we'll see it gets a little bit more complicated later when we do it.
    • 0:49:21When we have a more elaborate procedural generation approach to our levels.
    • 0:49:26But right now, we're just going to say set two random variables here.
    • 0:49:29Number of rows and columns.
    • 0:49:31And then for every row or for basically every row and every column,
    • 0:49:37create a new brick.
    • 0:49:40And then there's some math here.
    • 0:49:41I'm going to kind of skim over it, but basically it
    • 0:49:43calculates where the brick is and then gives us
    • 0:49:46eight pixels of padding on either side.
    • 0:49:50And then based on how many it is, it needs to center all
    • 0:49:53the bricks and shift them by a certain amount to the left
    • 0:49:55and then start drawing all of them.
    • 0:49:57And that's essentially what this code does here.
    • 0:49:59So calculate the center.
    • 0:50:01I wrote it out in comments here, but I'm going
    • 0:50:03just kind of glaze over it for now.
    • 0:50:05But effectively, center all the bricks.
    • 0:50:08Basically calculate what offset on the X-axis you need to put all of them
    • 0:50:12so that they appear centered, and then you're going to draw them all out.
    • 0:50:16And then that's it for the level maker class.
    • 0:50:20So simply number of rows and columns, and then fill a table with bricks
    • 0:50:24but set their X equal to however much we need to center all
    • 0:50:28of them when they're all drawn out.
    • 0:50:29So we need to figure, we need to basically take in our number of columns
    • 0:50:32into account when we do that.
    • 0:50:34And then if we go into Breakout3 and run that, we have bricks.
    • 0:50:43They're getting collided with, and as soon as they get hit,
    • 0:50:47collided are in play on each of those bricks gets set to false
    • 0:50:51and they no longer get rendered.
    • 0:50:52And they no longer get updated in terms of collision.
    • 0:50:55Now, we still have the issue with the ball not getting reset.
    • 0:50:58We'll fix that.
    • 0:50:59That's an easy fix.
    • 0:51:01But we're coming a long way.
    • 0:51:02We have things moving at quite a pace.
    • 0:51:04I'm going to go ahead and move to the next bit of code here.
    • 0:51:08So this is another bit of code.
    • 0:51:09I'm going to sort of glaze over a little bit of the details here.
    • 0:51:12But at a high level what we need to do is it's one thing
    • 0:51:16to detect that we've collided with a brick,
    • 0:51:18but in Breakout, the ball bounces off of the brick
    • 0:51:21depending on which side it hits.
    • 0:51:23And we don't know this necessarily just based off of the collision.
    • 0:51:27We just know whether the collision is true or not.
    • 0:51:29We don't know where it came from and how much it collided with.
    • 0:51:34And then we're also going to fix our paddles so that rather than-- because
    • 0:51:37currently all it does is just negate whatever the Y velocity is,
    • 0:51:40but we want to add a little bit more variety
    • 0:51:43to how we end up sort of ricocheting the ball off the paddle when we play
    • 0:51:47so that we can sort of strategize a little bit,
    • 0:51:50give ourselves a little bit of game play.
    • 0:51:51So if we are moving to the right and we hit the right edge of the puddle
    • 0:51:54with the ball, it should probably go in a sharper direction.
    • 0:51:56Same thing with the left side.
    • 0:51:58And we can effectively do that by taking the middle,
    • 0:52:01figuring out how far away from the center it is,
    • 0:52:03and then just amplifying our delta X in the negative or positive direction
    • 0:52:08based off of that.
    • 0:52:09And that has the effect of causing that to happen.
    • 0:52:13So here we can see we have the ball sort of coming at the paddle,
    • 0:52:18and let's pretend that the paddle is moving to the left.
    • 0:52:21In this case, however far away the ball is from the center,
    • 0:52:24we want to scale that by some amount and then
    • 0:52:27end up making that our negative delta X, because that's effectively
    • 0:52:32how the game normally works.
    • 0:52:34If you move the paddle to the left or the right,
    • 0:52:36hit it on a corner or something, gives it that sharp angle.
    • 0:52:39And that's effectively what the sharp angle is.
    • 0:52:41It's just a strong delta X, and it gets amplified the larger this is.
    • 0:52:44So just basically take this, multiply it by some amount,
    • 0:52:47and then make it negative or positive on your dx.
    • 0:52:50That's your sort of paddle collision V2.
    • 0:52:56Brick collision is a little bit--
    • 0:53:00it's pretty simple, but it's a little bit more complicated.
    • 0:53:03Basically what we need to do is just check and see which edge of the ball
    • 0:53:06isn't inside the brick.
    • 0:53:09And so if the left edge of the-- and we can also sort of
    • 0:53:12simplify this a little bit.
    • 0:53:13If the left-- as you see here by the pseudocode--
    • 0:53:15if the left edge of the ball is outside the brick and the dx is positive,
    • 0:53:20then we can say, oh, we can basically assume
    • 0:53:22we've come in from the left side, so we should probably
    • 0:53:25go in the opposite Y direction on the left side.
    • 0:53:30Or sorry, we should go in the same Y direction, but negate our delta
    • 0:53:35X. Because we're coming in from the left,
    • 0:53:38the left side is outside the brick, so bounce it back.
    • 0:53:42And the same thing for the right edge.
    • 0:53:44And we only do this test, the left edge of the ball, if dx is positive.
    • 0:53:48Because if dx is negative, there's no way
    • 0:53:50the ball's colliding with the left side of our brick.
    • 0:53:52So we can shortcut that effectively.
    • 0:53:54We do the same exact logic here, just on the right edge
    • 0:53:56of the brick instead of the left edge.
    • 0:53:59And then if none of those hold true, we're
    • 0:54:01going to see if the top edge of the ball is above the top edge of the brick.
    • 0:54:06And if that's the case, we know that we've hit from the top.
    • 0:54:09We can trigger a top collision.
    • 0:54:10And if none of those have held true, we know
    • 0:54:12that we have had a collision of some kind,
    • 0:54:14we can just register a bottom collision.
    • 0:54:18And so this is a simple version of this sort
    • 0:54:23of way of doing Breakout collision.
    • 0:54:25It has a few faults when it comes to corners,
    • 0:54:27sometimes corners can be a little bit finicky,
    • 0:54:29but I would say it works 99% of the time.
    • 0:54:31For a much more robust and a better example, I would look at this URL
    • 0:54:37here because he also goes into a full sort of breakdown
    • 0:54:40of how he would implement arkanoid, which is the same thing effectively
    • 0:54:43as Breakout if you just want an alternative look at it.
    • 0:54:46But basically, his solution involved taking how much the X and the Y
    • 0:54:50differed on different points of the bricks relative to the ball.
    • 0:54:54And I believe he also kept the ball as an actual ball with a center point,
    • 0:54:58even though he rendered it as a rectangle.
    • 0:55:04So it's a little bit more robust.
    • 0:55:05I decided to implement it a simpler way, which I'll showcase,
    • 0:55:09which is the way that I demonstrated because it worked well
    • 0:55:12and it wasn't too much code to sort of look over.
    • 0:55:15But I do encourage you to take a look at that.
    • 0:55:17We're going to look at our PlayState now in Breakout4.
    • 0:55:26And in our PlayState, we're going to see--
    • 0:55:32sorry.
    • 0:55:33Line 65.
    • 0:55:35So this is the actual paddle code for influencing the ball's delta X.
    • 0:55:43So basically, if the ball.x is less than the paddle.x plus it's width divided
    • 0:55:53by two, so basically on the left side of the paddle,
    • 0:55:58and the paddle's delta X is less than zero, which means it's moving left--
    • 0:56:02because we don't really want to necessarily influence it if we're just
    • 0:56:04standing still--
    • 0:56:05we're going to do what I described earlier.
    • 0:56:07We're going to give it some scaler, like some start off value.
    • 0:56:11In this case, negative 50 is just sort of seeding this, giving it
    • 0:56:15some sort of initial value.
    • 0:56:18And then we're just going to subtract the ball's X from the middle point.
    • 0:56:24This being the middle point of the paddle
    • 0:56:26And then just multiply it by eight.
    • 0:56:28So whatever the difference is between the ball's X and the middle
    • 0:56:31of the paddle, multiply it by eight.
    • 0:56:34Add it to negative 50 and then negate that.
    • 0:56:36Also negate that whole value so that the whole entire value becomes negative.
    • 0:56:40And we, therefore, get a sharper delta X depending on which angle
    • 0:56:44it's coming at, and also how fast--
    • 0:56:46or not how fast, but whether or not we are moving left.
    • 0:56:50And it's the exact same thing on the right side.
    • 0:56:53Only because we're taking this math, this self.paddle.x
    • 0:56:58plus self.paddle.width divided by two minus the ball.x,
    • 0:57:02the ball.x isn't going to be greater than that point.
    • 0:57:05So this value is actually going to be negative.
    • 0:57:07So we're going to just make it positive with math.abs.
    • 0:57:09So absolute value.
    • 0:57:10Just a lua function.
    • 0:57:12So the absolute value of the difference between the ball's
    • 0:57:14X and the middle point times eight, add it to 50,
    • 0:57:19and that'll give us a positive value that scales depending on whether or not
    • 0:57:23we've hit the middle of the, we've hit the right edge of the paddle
    • 0:57:27and are moving to the right.
    • 0:57:28And so that's, in a nutshell, how we get that collision to work with the paddle
    • 0:57:33and how we can tweak delta X to be scaled a little bit more
    • 0:57:36than just a constant, you know, negative or whatever
    • 0:57:40it's current X was, but negative dy.
    • 0:57:43A little bit more complicated.
    • 0:57:45And then the actual collision code for the bricks
    • 0:57:50themselves is going to take place in a for loop here.
    • 0:57:53So if it's in play, if the ball collides with it, hit it.
    • 0:57:58So I added plus two.
    • 0:57:59So the gist of the math is if ball.x is less than brick.x
    • 0:58:05and the ball is moving to the right, self.ball.dx is greater than zero,
    • 0:58:09then flip it's X velocity.
    • 0:58:10So bounce it to the left.
    • 0:58:11That's what this check is.
    • 0:58:13But it plays a little bit rough with corners
    • 0:58:15because you could theoretically get into a position
    • 0:58:17where you come in at an angle and it's intersecting
    • 0:58:21with the paddle in two positions, both on top and the left
    • 0:58:29or on bottom and the left.
    • 0:58:30So in that case, adding two sort of prioritizes the Y being hit.
    • 0:58:36So it basically takes the check from the exposition of the ball to the X plus 2.
    • 0:58:42And so it ends up fixing the corners a little bit, but the gist of it
    • 0:58:48is just check to see if the ball.x is less than the brick.x.
    • 0:58:50And if it is and we've detected a collision, we can bounce it.
    • 0:58:55There are some subtle corner case bugs without adding this plus two,
    • 0:58:59so we add that.
    • 0:59:01And then flip the velocity here.
    • 0:59:04Oh, this shift here.
    • 0:59:05This is what we were talking about earlier with make sure
    • 0:59:07when you do a collision, shift whatever is
    • 0:59:09moving outside the boundaries of whatever you're colliding with.
    • 0:59:12So self.ball.x gets brick.x minus eight because the ball is eight pixels wide.
    • 0:59:19It should actually be self.ball.width for a better style,
    • 0:59:23but that's essentially what it translates out to.
    • 0:59:26Same thing for the right edge.
    • 0:59:27The plus six because it's on the right side.
    • 0:59:29So it's effectively the same thing as minus two if we're on the left side.
    • 0:59:34Just a sort of fixes corners, weird issues with corners.
    • 0:59:39But check in to see if basically the ball plus its height minus two
    • 0:59:44is greater than the brick plus X plus brick.width, which it means,
    • 0:59:51oh, we've collided with the right edge of the screen, of the brick.
    • 0:59:55And then if the Y is less than the brick.y,
    • 0:59:58then we've collided with the top of the brick,
    • 1:00:00and otherwise, we've collided with the bottom.
    • 1:00:03And with the top and the bottom, just do the same thing we did with delta X,
    • 1:00:06but do it with delta Y, but you're still resetting it.
    • 1:00:09So ball.y gets brick.y minus eight.
    • 1:00:11Ball.y gets brick.y plus 16 because the paddle or the individual bricks
    • 1:00:17are 16 pixels tall.
    • 1:00:21That's the gist of the collision detection.
    • 1:00:24And then if we actually-- oh, and one other thing that I ended up
    • 1:00:27putting here just to make it a little bit more interesting,
    • 1:00:30and this also ties into more complicated collision detection.
    • 1:00:33If your velocity is too fast, a lot of the time it'll skip through objects,
    • 1:00:38and then that causes a lot of problems with these collision detection
    • 1:00:40functions that normally are very sort of mathematically correct
    • 1:00:44and they work well.
    • 1:00:45They don't work well when it skips over what
    • 1:00:47you're trying to actually collide with.
    • 1:00:49So a solution to that, which was beyond the scope of this example but something
    • 1:00:53we're thinking about, is perhaps stepping backwards a certain amount
    • 1:00:57of time, a certain amount of pixels.
    • 1:00:59Perhaps maybe start at where you where your ball was on one particular,
    • 1:01:04on the last frame, and then just add its width and height to itself
    • 1:01:08until it collides with something, until it reaches whatever its current delta X
    • 1:01:12or delta Y plus its position is.
    • 1:01:15That's one way to do it.
    • 1:01:16Sort of just adding a bunch of invisible--
    • 1:01:18whatever you're colliding with or whatever you're using to collide--
    • 1:01:21add a bunch of invisible those to bridge the gap and check into if any of those
    • 1:01:26hold true for a collision.
    • 1:01:27A little bit more computationally expensive, but a lot more accurate
    • 1:01:32in terms of the physics.
    • 1:01:35And aside from that, everything is the same.
    • 1:01:36So if you look at the code in Breakout4--
    • 1:01:40and I'm going to go a little bit faster henceforth.
    • 1:01:42That's probably the meatiest part of the program.
    • 1:01:47We get collisions.
    • 1:01:49And then I'll try and get a strong angle so I can demo the--
    • 1:01:54that didn't work.
    • 1:01:54That actually gave a weaker angle.
    • 1:01:55So if you do this and you do it close to the center, it has the opposite effect.
    • 1:01:59But there you go.
    • 1:01:59That's a sharper angle.
    • 1:02:01So now you can actually influence the ball in a little bit more
    • 1:02:05of a personable way.
    • 1:02:07You know, not just have it be a flat delta Y gets negative--
    • 1:02:11or get negative delta Y effectively.
    • 1:02:14So any questions on sort of how the gist of all of that works?
    • 1:02:23OK.
    • 1:02:24Perfect.
    • 1:02:25So now we're going to get into a little bit more of some fun stuff.
    • 1:02:28We'll do a couple more examples, then we'll take a break.
    • 1:02:30So this is the hearts update.
    • 1:02:32So notice that the very top of the screen, as I've demonstrated in these
    • 1:02:35slides, we have just a few hearts.
    • 1:02:38One of them is empty.
    • 1:02:39We showed this earlier.
    • 1:02:40And then we have a game over screen, which is our final score.
    • 1:02:43So I'm going to go ahead and we're just going to look at the code a little bit
    • 1:02:46faster now since a lot of the stuff is fairly straightforward.
    • 1:02:50I'm going to go ahead and open up the--
    • 1:02:52I'm going to make sure I'm in the right folder first of all.
    • 1:02:54Breakout5.
    • 1:02:55And then in the--
    • 1:03:00so one other thing we're going to start doing is--
    • 1:03:07I mentioned this earlier.
    • 1:03:08And it's going to be it's going to hold true
    • 1:03:10for any of the sort of state transformations
    • 1:03:13that take place going forward.
    • 1:03:15Rather than keep global variables, we're going
    • 1:03:17to sort of do away with that idea outside of the asset tables
    • 1:03:20that we have just because those are kind of an exception
    • 1:03:23and they could reasonably be put into a separate class called the resource
    • 1:03:26manager.
    • 1:03:27We're going to start passing in what is basically our current app state,
    • 1:03:35or at least the variables that make sense.
    • 1:03:37And this is a common paradigm in web development with React as well.
    • 1:03:40But basically, everything that we need to be preserve state to state,
    • 1:03:44rather than just keeping global variables,
    • 1:03:46let's pass them between the states because the state machine allows
    • 1:03:49us to do that in the change function.
    • 1:03:52And then whatever that state is in it's enter function,
    • 1:03:55it'll have access to that and it can just
    • 1:03:56set those values to self dot whatever and use them.
    • 1:04:01But we no longer have global variables.
    • 1:04:02We're just saying, here.
    • 1:04:03Here's the values that are important for you to continue on.
    • 1:04:06And then that state will take its values and go to the next state
    • 1:04:09and say, oh, OK, here are the values that you need to function.
    • 1:04:14Like the serve, play, and all those states
    • 1:04:16that have the core game play involved will probably
    • 1:04:17need to maintain a reference to like the paddle,
    • 1:04:19and to the score, the amount of health we have.
    • 1:04:21But when we get to the end, for example, and then we no longer really
    • 1:04:24need a paddle, we no longer really need bricks or anything like that,
    • 1:04:27we just need to know what our high score is so that we can enter it
    • 1:04:30into our high score list, all we really need
    • 1:04:33to do is just pass in the high score state entry or just our high score,
    • 1:04:37and that's it.
    • 1:04:39So it encapsulates all of our data.
    • 1:04:41And at a glance, we can sort of see what we need to pass between the states
    • 1:04:45and what's going to be relevant at a glance as well.
    • 1:04:48It just clean things up quite a bit.
    • 1:04:52So that's what we're doing now on line 35.
    • 1:04:55And henceforth, we will do this in every state as we see,
    • 1:04:58but I'm going to sort of glaze over it in the future.
    • 1:05:02We have a ServeState now.
    • 1:05:03So a ServeState, this is very identical to what we did in Pong.
    • 1:05:08So we just wait for the user to press Space.
    • 1:05:10They can move around and then when they do press Enter,
    • 1:05:16basically the ball starts moving.
    • 1:05:20And then we change the PlayState here using
    • 1:05:23the current values that are necessary.
    • 1:05:25Paddle, bricks, health, score, and ball.
    • 1:05:27Those are basically the fundamental variables
    • 1:05:30that we need in order to keep track of our GameState.
    • 1:05:35So we have a ServeState, it will wait for us to press Enter.
    • 1:05:38And then our main.lua, we have a new hearts table.
    • 1:05:50And then on line 208, because we're going
    • 1:05:55to need the ability to render health and render our score across several states,
    • 1:05:59Play, Serve, Victory, Game Over--
    • 1:06:02actually not Game Over, but the three before that.
    • 1:06:04We don't want to duplicate those behaviors,
    • 1:06:06so I'm just calling a function called Render Health, which
    • 1:06:08just takes in whatever health is and then
    • 1:06:10we just set an X to virtual width minus 100.
    • 1:06:13And then for however many health we have, draw a heart
    • 1:06:17from the hearts sprite sheet, which I separated the hearts out
    • 1:06:20into a smaller image so you can just split them on like eight by eight
    • 1:06:23or whatever it is.
    • 1:06:25But just draw those and then add 11 to X,
    • 1:06:27and just keep going until we've drawn out however many hearts we have.
    • 1:06:30That will draw full hearts.
    • 1:06:32And then three minus health will give us however many health we're missing.
    • 1:06:35So if we took a point of damage, this is going to be equal to one.
    • 1:06:38So then it'll draw one empty heart after that or it'll draw two empty hearts.
    • 1:06:41So draw however many full hearts we have, then draw the empty hearts.
    • 1:06:44And those are two separate sprites that we get from the image.
    • 1:06:48And that will have the effect of drawing our health.
    • 1:06:51And then our score is simply, it takes a score variable that we pass into here.
    • 1:06:56And also note that the render health [INAUDIBLE]
    • 1:06:58and health variable and pass into it here.
    • 1:07:00And so in our PlayState, we are calling both of these functions on line 135.
    • 1:07:17Well, on line 135, we are calculating whether we
    • 1:07:20go below the edge of the screen, which is another important part of the game.
    • 1:07:23Obviously, we need to detect when we've lost health.
    • 1:07:26So it's as simple as this.
    • 1:07:28If it's greater than the virtual height, decrement health by one.
    • 1:07:31If it's equal to zero, change to Game Over.
    • 1:07:34Else change to the ServeState.
    • 1:07:35And note that we're passing in all these variables to and from our states.
    • 1:07:38The ones that are important.
    • 1:07:39Game Over just needs score, but Serve needs whatever
    • 1:07:41variables we were already using.
    • 1:07:46And then down here we're calling render score and render health,
    • 1:07:50and then the GameOverState is simply--
    • 1:07:54because it takes in score from the parameters list,
    • 1:07:58just wait for keyboard input to go back to the start and then render game over,
    • 1:08:03here's your score.
    • 1:08:04It's self.score, and then that's it.
    • 1:08:06Very simple.
    • 1:08:07Very simple state.
    • 1:08:08AUDIENCE: [INAUDIBLE]
    • 1:08:09COLTON OGDEN: Sure.
    • 1:08:10AUDIENCE: [INAUDIBLE]
    • 1:08:15COLTON OGDEN: The question was, do any of these states
    • 1:08:18have access to their parent file?
    • 1:08:19AUDIENCE: [INAUDIBLE]
    • 1:08:27COLTON OGDEN: Is everything in main.lua global functions?
    • 1:08:30Yes.
    • 1:08:30Functions that you declare.
    • 1:08:31Anything that's basically not specified as local that you define in main.lua
    • 1:08:35will be accessible anywhere in your application, including functions.
    • 1:08:39AUDIENCE: [INAUDIBLE]
    • 1:08:40COLTON OGDEN: You don't have to-- the question was,
    • 1:08:42do you have to declare as public?
    • 1:08:44No, there is no notion of public.
    • 1:08:45In lua, anything that does not have a local specifier
    • 1:08:48is assumed global, even if it's in a nested scope.
    • 1:08:51So you could have a for loop, you could have several nested for loops
    • 1:08:54and declare some variable without local, that variable can be accessed anywhere
    • 1:08:59above it or outside of it.
    • 1:09:01So it's pretty important to use local variables
    • 1:09:03when you're not explicitly allocating something as global
    • 1:09:05just to avoid the bug of for nested loops
    • 1:09:09and you have some variable name like hello and you use it somewhere else.
    • 1:09:14Good questions though.
    • 1:09:16So yeah.
    • 1:09:17We have a bunch of states now.
    • 1:09:18We have a GameOverState, a PlayState, we're
    • 1:09:20rendering our score, rendering our health.
    • 1:09:22If we go and take a look at Breakout5--
    • 1:09:29is it a different window?
    • 1:09:33There we go.
    • 1:09:35We can see hearts at the top.
    • 1:09:37Score zero.
    • 1:09:41Oh, and I forgot to mention the part where we actually add score now.
    • 1:09:44So the bricks themselves in their on hit, or I should say in the PlayState,
    • 1:09:54on line 81 when we detect a hit, we're just adding 10 to the score for now.
    • 1:10:00But later on, we'll do a calculation where
    • 1:10:01we take tier and color into consideration
    • 1:10:04and then perform arithmetic on that to get our total score for each ball hit.
    • 1:10:09But yeah, we have our health, we have our score.
    • 1:10:14And then once we take enough damage, we'll
    • 1:10:16end up going to the Game Over screen.
    • 1:10:18The Game Over screen will go back to our Start screen.
    • 1:10:20So making progress.
    • 1:10:21And then probably my favorite of the updates
    • 1:10:24before we take a short break is the pretty colors update.
    • 1:10:27So what this does is clearly we can have--
    • 1:10:31we've updated our level maker.
    • 1:10:32So rather than just having a bunch of very static bricks,
    • 1:10:37we end up doing a little bit more complicated procedural generation.
    • 1:10:41It's not complicated though.
    • 1:10:42Just in levelmaker.lua in Breakout6, we have a few different constants here.
    • 1:10:48So solid, alternate, skip, or none.
    • 1:10:51Actually, I don't think I use skip or none.
    • 1:10:53Just solid or alternate basically.
    • 1:10:54We have flags now.
    • 1:10:56So number of columns.
    • 1:10:58And we ensure that it's odd because even columns with generating patterns
    • 1:11:02leads to asymmetry.
    • 1:11:04So make sure the number of columns is odd.
    • 1:11:07Generate the highest tier and the highest color based on our level.
    • 1:11:10So in this case, we'll go no higher of a tier
    • 1:11:12than three because we have no higher tiers than three.
    • 1:11:16It goes zero, one, two, three.
    • 1:11:18And then whatever our level divided by five is,
    • 1:11:20and it would just take math.floor.
    • 1:11:21Math.floor takes in basically performing division and then truncating
    • 1:11:26the decimal point.
    • 1:11:28Well, not division.
    • 1:11:29It just literally truncates the decimal point off of a number.
    • 1:11:31So a level divided by five.
    • 1:11:33Whatever that is before the decimal point.
    • 1:11:36Level modular five plus three for the highest color.
    • 1:11:39So we'll cycle.
    • 1:11:40We'll go over and over again.
    • 1:11:42Go highest color one, two, three, four, five, and then we'll go to a new tier
    • 1:11:46with level divided by 5.
    • 1:11:47So basically, every five levels will increment in tier,
    • 1:11:50and then we'll start back at blue.
    • 1:11:52And then we go on, and on, and on like that for every number of rows.
    • 1:11:56So basically I have a few--
    • 1:11:57I'm going to sort of glaze over this a little bit
    • 1:11:59just because we're probably going to run short on time.
    • 1:12:02But we have basically two flags.
    • 1:12:05Whether we're skipping bricks in this row or alternating bricks color wise.
    • 1:12:10And if we do, we need to set a color for it and a tier.
    • 1:12:14And then we basically just say, you know,
    • 1:12:16the same sort of logic that we had before we generated random rows
    • 1:12:19and columns, but if we have the alternate flag on,
    • 1:12:24then as we can see in some of these photos here, here we have skip is true.
    • 1:12:31So the color for that row is set to the blue, but skip is true,
    • 1:12:36so every other brick is just going to skip that iteration of the loop.
    • 1:12:40Same thing here, only it's offset by one.
    • 1:12:42Same thing here.
    • 1:12:43Same thing here.
    • 1:12:44So this is kind of a nice little pattern.
    • 1:12:46And in each of these cases--
    • 1:12:47actually not each of these cases.
    • 1:12:49Notice this third one, it also set alternate to true.
    • 1:12:52So it goes green, purple, green, purple, green, purple.
    • 1:12:55And so the logic there is if alternate is true, then
    • 1:12:57just flip the color every iteration.
    • 1:13:00If skip is true, don't generate a brick every other iteration, and so on
    • 1:13:04and so forth.
    • 1:13:05And then if you have solid or if you don't have alternate equals true,
    • 1:13:09then you have a solid brick like these blue ones.
    • 1:13:11And if you have alternate but no skip, you get this sort of pattern
    • 1:13:16where you have green, purple, green, purple.
    • 1:13:18You know, any random color.
    • 1:13:20And then also the number of columns is random.
    • 1:13:23So it can go--
    • 1:13:24here we have 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 but on this very bottom one,
    • 1:13:30we have that minus two it looks like because it can only go that wide.
    • 1:13:35And in those here too.
    • 1:13:37Smaller size.
    • 1:13:38That one there's no spacing.
    • 1:13:40So these are very simple concepts.
    • 1:13:42Like should we skip a block this iteration?
    • 1:13:44Should we alternate the colors?
    • 1:13:47And when you put them all together, it produces
    • 1:13:49things that look as if they were almost handcrafted.
    • 1:13:52Like this could be made by somebody.
    • 1:13:53Like, that looks like it was made by somebody.
    • 1:13:55Pretty much every iteration of this.
    • 1:13:57I mean, even that, that looks like a shape almost.
    • 1:13:59Its just very simple but the results are pretty awesome in my opinion.
    • 1:14:03And so that's just the gist behind what we're doing.
    • 1:14:05We're just setting flags and just saying, you know,
    • 1:14:08if we're skipping this turn and just every iteration,
    • 1:14:10every time we lay out a brick and we spawn a new brick on this row,
    • 1:14:13just do or don't.
    • 1:14:14Just make it's color--
    • 1:14:16pick two colors if we're alternating and then set
    • 1:14:19its color to whatever the off color is that we're alternating.
    • 1:14:23And if we're skipping and alternating, then
    • 1:14:26we're just doing whenever we're on a brick that we're actually laying
    • 1:14:29is when we change the color, the alternate color.
    • 1:14:32And so like I said, I won't go into too much detail.
    • 1:14:35Happy to talk about the generator after class.
    • 1:14:36But just because we're running short on time,
    • 1:14:39sort of going to wave my hands over it.
    • 1:14:40But that's it in a nutshell.
    • 1:14:42So any questions before we take a break for five minutes?
    • 1:14:47Yes?
    • 1:14:47AUDIENCE: [INAUDIBLE]
    • 1:15:02COLTON OGDEN: The question is, in an instance with this programming
    • 1:15:06if the ball were so fast that it we're actually inside the brick,
    • 1:15:10would it what?
    • 1:15:12AUDIENCE: Would it still bounce back?
    • 1:15:14COLTON OGDEN: Would it still bounce back?
    • 1:15:15The answer is no, it wouldn't.
    • 1:15:18This implementation doesn't take into consideration velocity
    • 1:15:21that goes too fast.
    • 1:15:24Mainly to-- for two reasons.
    • 1:15:26One, it's non-trivial to implement, and two,
    • 1:15:28it's an interesting thing to look at, and observe,
    • 1:15:30and be conscious of as you go forward in implementing your own games.
    • 1:15:34The current code, if it gets clipped inside of the brick,
    • 1:15:38it will have no edges that are peaking outside of the brick
    • 1:15:41and therefore, it will default to the final condition, which
    • 1:15:43is the last else clause, which puts it below the brick.
    • 1:15:47So it'll just go below the brick.
    • 1:15:48It'll almost be as if it came in from the underside and bounced out.
    • 1:15:54But like I alluded to earlier, if you want to implement something
    • 1:15:56like this yourself, you would have to slice up frame X and frame X plus one
    • 1:16:01into the size of the ball if the delta is so wide
    • 1:16:08that it either goes inside of a brick or it goes outside of a brick,
    • 1:16:11or if it skips a brick.
    • 1:16:13And this sort of solves that problem.
    • 1:16:15It solves both of those problems, but it's a little more
    • 1:16:18than we can cover in this example.
    • 1:16:20Any other questions?
    • 1:16:24All right.
    • 1:16:24Let's take five and get back to it.
    • 1:16:28All right.
    • 1:16:29And we're back.
    • 1:16:29So the next step is we have basically a layout dynamically
    • 1:16:35generated of interesting bricks now, but we haven't really
    • 1:16:39implemented scoring any of these.
    • 1:16:41We just have score gets score plus 10, which
    • 1:16:43isn't really particularly interesting.
    • 1:16:46So Breakout7 is what I call the tier update, which
    • 1:16:50should allow us to hit blocks that are a higher tier than just base blue.
    • 1:16:55And if they are of a higher color than base blue, they should go down a color.
    • 1:16:59So the hierarchy was, if we look back, blue
    • 1:17:04goes to green goes to red goes to purple goes to gold.
    • 1:17:08And if something is a higher tier, it goes
    • 1:17:11to the next color below it but at that same tier,
    • 1:17:15unless it happens to be like blue and gray, in which case
    • 1:17:19it'll go back to blue.
    • 1:17:22So how might we implement scoring based on this system?
    • 1:17:30What do we need?
    • 1:17:30What pieces do we need?
    • 1:17:33What pieces do we already have that we can use to make this happen?
    • 1:17:38AUDIENCE: [INAUDIBLE]
    • 1:17:40COLTON OGDEN: I'm sorry?
    • 1:17:41AUDIENCE: [INAUDIBLE]
    • 1:17:52COLTON OGDEN: So the answer was the brick index.
    • 1:17:54So yes, the brick skin and color are the pieces.
    • 1:18:00Yes.
    • 1:18:01So those are fields of brick.
    • 1:18:03So if we open up--
    • 1:18:04I'm going to go up to Breakout7.
    • 1:18:06And I'm going to start probably deferring a lot of this code
    • 1:18:10to future reading.
    • 1:18:12But in brick here, the tier and the color--
    • 1:18:16sorry, not skin, but skin is for the paddle.
    • 1:18:19But the brick has a tier and it has a color.
    • 1:18:22And so we need to perform some arithmetic on that here.
    • 1:18:25And that's essentially what lines 44 through 58 is.
    • 1:18:28So basically-- oh, I apologize.
    • 1:18:32That's not actually where the arithmetic is.
    • 1:18:3644, that does compute, but this is the bit of code
    • 1:18:40that computes how we can actually go backwards if we make a collision.
    • 1:18:45So if we collide with a brick and it's of a higher tier than one
    • 1:18:52and it's a higher color than blue, it should be brought back one step.
    • 1:18:57But if it happens to be blue, in which case self.color gets one
    • 1:19:02because blue is one, then it should just be removed from play
    • 1:19:05just like we've done before.
    • 1:19:06Only now, we're also taking in tier and color.
    • 1:19:10So we're decrementing tier based on what index we're at
    • 1:19:13and we're decrementing color.
    • 1:19:15And then this actually gets used in our PlayState.
    • 1:19:24If we go to line 81, which previously just
    • 1:19:29had self.score gets self.score plus one, there's a little bit of math here.
    • 1:19:32It's very simple though.
    • 1:19:34Just brick.tier times 200.
    • 1:19:36So make the tiers worth 100.
    • 1:19:38Plus brick.color times 25.
    • 1:19:41And so if tier is zero, if it's a base then we're
    • 1:19:43just not going to get that 200 bonus.
    • 1:19:44But the first tier, everything is going to be worth
    • 1:19:4725 times whatever its color is.
    • 1:19:49So one, two, three, four, five.
    • 1:19:52And then add 200 plus the brick.color for when
    • 1:19:55we get to the next set of bricks.
    • 1:19:58And so the result of this is--
    • 1:20:05I believe this is GUI Breakout7.
    • 1:20:14And then if we hit a brick--
    • 1:20:15since this one is blue, it should disappear.
    • 1:20:17And we're playing a new sound as well.
    • 1:20:19New, like, death sound just to make it clear.
    • 1:20:24But notice they change colors.
    • 1:20:26So that's all we're doing.
    • 1:20:27We're just taking their tier or their color
    • 1:20:29and just performing a simple decrement on it.
    • 1:20:32Looping back.
    • 1:20:33In the event that we go down a tier, we should loop back up
    • 1:20:36to the highest color of the lower tier.
    • 1:20:39So I'll let you look at the code for that
    • 1:20:41if you want to sort of get a more low level understanding of it,
    • 1:20:44but that's the sort of high level understanding.
    • 1:20:46The next big concept that I'd like to introduce you guys to
    • 1:20:48is a particle system.
    • 1:20:51And so particle systems are fairly omnipresent in video games,
    • 1:20:57I would say, because they make effects that
    • 1:20:59or otherwise difficult to do with simple sprite
    • 1:21:02editing achievable very easily and realistically.
    • 1:21:05Just like fire, for example.
    • 1:21:07Things that are very organic, and flowy, and have a lot going on
    • 1:21:10are often better represented with particle systems
    • 1:21:12than they are with simple sprite animation.
    • 1:21:15So does anybody know how we might be able to--
    • 1:21:18how a particle system might work underneath the hood?
    • 1:21:22I think I alluded to it previously.
    • 1:21:26Yes.
    • 1:21:26AUDIENCE: [INAUDIBLE]
    • 1:21:33COLTON OGDEN: Yeah.
    • 1:21:34So what he said was in order to make fire,
    • 1:21:38for example, just spawn a bunch of particles
    • 1:21:41close to the center of wherever your fire is spawning and then outside of it
    • 1:21:45spawn fewer.
    • 1:21:46That is absolutely a way to get fire to work,
    • 1:21:49and also taking into consideration the travel of your particles.
    • 1:21:54For example, you might spawn a ton of fire particles really densely,
    • 1:21:57but then maybe they have some logic that makes them go upwards.
    • 1:22:00Maybe they have a negative delta Y and then some sort of acceleration
    • 1:22:04so they've sort of trail off.
    • 1:22:06And then maybe sort of how to get a more realistic fire
    • 1:22:10look, they travel sort of upwards and then fade away.
    • 1:22:13So the way fire works, sort of thinking of things
    • 1:22:16in terms of particles like that, you can achieve a lot of effects.
    • 1:22:20How might we implement, like, smoke, for example?
    • 1:22:23Same system.
    • 1:22:29So we could have maybe a timer in our particle effect,
    • 1:22:33or even a transition because in particle systems,
    • 1:22:36often you have the ability to transition colors between particles.
    • 1:22:39Let's say you start off red, go to yellow,
    • 1:22:41and then maybe your particle system transitions to gray or brown.
    • 1:22:44And then over time, your particles are going up, they're dissipating.
    • 1:22:47And they're also turning dark, they're turning brown,
    • 1:22:50it sort of gives you the illusion of fire.
    • 1:22:52And we won't be doing anything necessarily as
    • 1:22:54complex as this in our code here, but in Breakout8, we
    • 1:22:59will be using Love's sort of integrated particle system which is just
    • 1:23:04love.graphics.newparticlesystem.
    • 1:23:06And it takes in a texture because all particle systems need some sort
    • 1:23:09of texture as their foundation.
    • 1:23:11And then it needs the number of particles that it could maximally emit.
    • 1:23:15And so each individual particle system can emit up
    • 1:23:18to a certain instance of particles.
    • 1:23:20And in the number, and speed, and whatnot of all
    • 1:23:23those particles is ultimately the determining factor
    • 1:23:25for how you can get an illusion.
    • 1:23:28Back to last week's lecture, illusions, like, it's not fire, it's not smoke,
    • 1:23:32it's just a bunch of particles responding
    • 1:23:34with colors and acceleration and stuff.
    • 1:23:36But there's a lot of functions that particle system gives you in Love2D,
    • 1:23:40so I encourage you to look at that link just to explore some of them.
    • 1:23:44Love2d.org/wiki/particlesystem.
    • 1:23:48We'll be using a few of them.
    • 1:23:49Here I'm going to just briefly show you.
    • 1:23:53So each individual brick when it gets hit
    • 1:23:57is going to need a particle system of its own.
    • 1:23:59Because our goal is--
    • 1:24:00I'll run the code for you so you can see it.
    • 1:24:03So if you go to Breakout8 and then you run it,
    • 1:24:09we have a little bit of particles you saw there at the very end.
    • 1:24:18The blue you were probably able to see a little bit better.
    • 1:24:22And then one last time.
    • 1:24:25So it spawns a bunch of little particles.
    • 1:24:27So can anyone tell me how they think the particles are
    • 1:24:30behaving sort of in a nutshell?
    • 1:24:33What the logic is for the particles?
    • 1:24:38AUDIENCE: [INAUDIBLE] slightly random.
    • 1:24:40COLTON OGDEN: Yeah.
    • 1:24:41Slightly random.
    • 1:24:42And if you look at it, you'll also notice that they tend to go downwards.
    • 1:24:49So knowing that, we can probably just assume
    • 1:24:52that they have an acceleration that tends towards positive Y.
    • 1:24:57And that's essentially all we really need to do.
    • 1:24:59We spawn a bunch of particles outwards and then just set them--
    • 1:25:02they have all a lifetime.
    • 1:25:04They last for a certain amount of time.
    • 1:25:06And then they fade between two colors.
    • 1:25:09In this case, we fade from red to transparent or whatever color it is.
    • 1:25:16And then after the lifetimes elapsed, it has the overall effect
    • 1:25:23of sort of this glimmering, gravity based effect,
    • 1:25:25but it's really just a bunch of particles that are
    • 1:25:27set to spawn in different directions.
    • 1:25:28Apologize for that.
    • 1:25:30So we'll take a look.
    • 1:25:32It's going to be in our brick class here in Breakout8.
    • 1:25:38So we're going to go to brick.
    • 1:25:42We have a bunch of colors that we're storing here.
    • 1:25:45So if you notice, the particle systems adopt the color of whatever brick
    • 1:25:49they're hitting just so that it stays sort of congruent
    • 1:25:52with what we're looking at.
    • 1:25:54So we're just storing a bunch of colors here.
    • 1:25:56And I wouldn't worry too much about this.
    • 1:25:57These are just colors from the sprite palette
    • 1:25:59that we used with our sprite art.
    • 1:26:01There's specific colors that are only used in that sprite.
    • 1:26:04And having a palette, generally speaking,
    • 1:26:06allows your art to look a little bit more cohesive
    • 1:26:08when you're doing sprite art as opposed to just picking colors willy nilly.
    • 1:26:11If you say, oh, I'm going to only use 16 or 32 colors for this palette,
    • 1:26:15you'll sort of have a more cohesive look and also
    • 1:26:17a very retro look because often hardware was
    • 1:26:20limited to a certain amount of colors back in the day for older systems.
    • 1:26:25So it's nice to--
    • 1:26:26as an aside-- and we'll look at it next week as well.
    • 1:26:30Looking at when you're doing your own sprite art, try to use fewer colors
    • 1:26:34and then that will give you-- it also makes it easier for you.
    • 1:26:36You don't have to spend time choosing I want to have this shade of green.
    • 1:26:40I wonder if it looks good.
    • 1:26:42If you only have two shades of green or semi shades of green to choose from,
    • 1:26:45that's all you've got.
    • 1:26:46You have to make do with it what you can.
    • 1:26:48So what we're doing here is we're storing five colors from our palette.
    • 1:26:51We're going to use this.
    • 1:26:52And then when we trigger our--
    • 1:26:54so right here we're initializing a particle system.
    • 1:26:57So psystem gets love.graphics.newparticlesystem.
    • 1:27:02And then these are a few functions.
    • 1:27:04So feel free to look in the wiki for how these functions actually behave.
    • 1:27:07But lifetime acceleration and area spread
    • 1:27:10just are sort of the properties that influence
    • 1:27:13the way our particle systems behave.
    • 1:27:15And so using whatever our current color is,
    • 1:27:17we're going to set our psystem's colors using setcolors function.
    • 1:27:21We're going to set it between two colors.
    • 1:27:23Color with 55 times tier alpha and color with zero alpha.
    • 1:27:28So the higher the tier, the brighter the particles,
    • 1:27:30but they'll always fade to zero alpha, if that makes sense.
    • 1:27:34And then we'll just emit 64.
    • 1:27:35And this is all in the hit function.
    • 1:27:37So all we've basically done is just add this particle system trigger in our hit
    • 1:27:42function, and it has the result of the behavior that we saw earlier.
    • 1:27:46So any questions on particle systems or how we use them?
    • 1:27:53So level 9 is the progression update.
    • 1:27:55So the purpose of this update is to allow us to go from level one
    • 1:27:59to two to three to four and start get more interesting level
    • 1:28:04generation that way.
    • 1:28:06The gist of this is in our--
    • 1:28:09so if you look at our StartState--
    • 1:28:16so all we need to really do to store a level is just to store a number.
    • 1:28:22And then where do we increment the number?
    • 1:28:24Or when do we increment the number I should say?
    • 1:28:28AUDIENCE: [INAUDIBLE]
    • 1:28:33COLTON OGDEN: Exactly.
    • 1:28:34So we increment the level.
    • 1:28:40We go to the next level when all of the bricks
    • 1:28:43are in play have gotten there in play flag set to false.
    • 1:28:47So we have no pricks that are in play effectively.
    • 1:28:52So in our StartState--
    • 1:28:54so let's go ahead and look at Breakout9.
    • 1:29:00So StartState.
    • 1:29:05We're passing in level gets one here.
    • 1:29:07We're just going to start off.
    • 1:29:08When we're going to StartState, we're just going to pass level equals one.
    • 1:29:12And then henceforth, anytime we do any state changes from play to serve
    • 1:29:17and to victory, as we'll see, victory being our new,
    • 1:29:19oh, you cleared this level.
    • 1:29:21Here's the next level.
    • 1:29:22We're just going to pass the level between them.
    • 1:29:27And then in PlayState, the important bit of code here is on line 204.
    • 1:29:37So this is just a function called checkVictory,
    • 1:29:39which is exactly as James said.
    • 1:29:44We're going to iterate over the entire table and just say if it's in play,
    • 1:29:48return false because we're not in victory
    • 1:29:50if we have any bricks that are in play.
    • 1:29:52But return true if we didn't meet that condition.
    • 1:29:55And so this is just a simple way for us to check whether or not
    • 1:29:59we are in a victory.
    • 1:30:02And so on line 88 of the same file in our PlayState,
    • 1:30:05we're just checking to say, hey, if self.checkVictory after we do any brick
    • 1:30:09hit--
    • 1:30:10because that's when we've just set a brick to in play is false--
    • 1:30:13just check victory.
    • 1:30:14And if so, play a new sound like a happy sound
    • 1:30:17that we've done a victory, and then just pass everything
    • 1:30:20into the new VictoryState that we have here.
    • 1:30:22And the VictoryState is simply a sort of just a message state.
    • 1:30:27So all it does is just renders everything as before,
    • 1:30:30but it just says your current level complete.
    • 1:30:34Self.level complete.
    • 1:30:36And then press Enter to serve and it'll go back to the ServeState
    • 1:30:39as soon as that happens.
    • 1:30:41And then here is where the actual progression happens.
    • 1:30:47When we go to the ServeState, we have our level but we want to add one to it.
    • 1:30:51So all we need to do when we trigger a transition into our next state, just
    • 1:30:57increment level by one here, and also create
    • 1:31:01a new map because bricks needs to get restarted because we have a new level.
    • 1:31:05Self.level plus one.
    • 1:31:07And that'll have the effect of, oh, we've gone from level one to two
    • 1:31:12to three to four et cetera when we go between PlayState to the VictoryState
    • 1:31:17back to the ServeState.
    • 1:31:19So any questions on how any of this works?
    • 1:31:21Yes.
    • 1:31:21AUDIENCE: Do you have to worry about garbage collection for any
    • 1:31:24of the bricks at all?
    • 1:31:26Or is that handled by the Love engine somehow?
    • 1:31:28COLTON OGDEN: Garbage collection is handled by Love.
    • 1:31:30Yes.
    • 1:31:31Yeah.
    • 1:31:31AUDIENCE: [INAUDIBLE]
    • 1:31:32COLTON OGDEN: Yes.
    • 1:31:33Because the question was, do you have to worry about garbage collection
    • 1:31:37when we are sort of clearing away the bricks and adding new bricks?
    • 1:31:40The self.bricks table, this table here, it's
    • 1:31:45getting assigned to a brand new table from levelmap.createmap.
    • 1:31:48When there are no references to an existing table,
    • 1:31:51lua's garbage collector will trigger at whatever
    • 1:31:54interval it's set to trigger and clear up all that for you dynamically.
    • 1:31:57Just like the same way that Java works.
    • 1:32:00Almost identical.
    • 1:32:02Any other questions?
    • 1:32:06All right.
    • 1:32:07So we have progression.
    • 1:32:10In the sake of speed, I won't demo.
    • 1:32:11It also takes a while just because we have to clear an entire level then
    • 1:32:14get to the next level.
    • 1:32:16But that's how the behavior works.
    • 1:32:17The next sort of iteration of this is high scores.
    • 1:32:22And I will test to make sure whether or not this is actually working.
    • 1:32:30I know I changed some stuff.
    • 1:32:31Yeah.
    • 1:32:32So high score.
    • 1:32:33Let's debug for a second.
    • 1:32:34So HighScoreState line 38 in Breakout10.
    • 1:32:44So HighScoreState.
    • 1:32:49And then the issue was [INAUDIBLE] to index field high scores.
    • 1:32:54A nil value.
    • 1:32:56OK.
    • 1:32:57So that means that--
    • 1:32:59OK.
    • 1:33:00I think I might know the issue, but it's because I
    • 1:33:03transitioned to a new user that doesn't have a saved file active on this.
    • 1:33:06The way that will transition, therefore, into love.file system, which
    • 1:33:12is Breakout10's main new thing that it introduces-- so writing files
    • 1:33:17to your file system is done [INAUDIBLE] with love.filesystem.
    • 1:33:22And there's a few things.
    • 1:33:23So Love automatically gives you a directory, a save directory
    • 1:33:28that's pretty much hard coded.
    • 1:33:30There are a few exceptions as to how to not use that directory,
    • 1:33:33but it assumes that you're always using that directory.
    • 1:33:36And with very few exceptions will you always use that folder.
    • 1:33:42It's like app data local on Windows, and application support,
    • 1:33:47and the name of your application on Mac.
    • 1:33:50But it's a subfolder that Love has read and write access to
    • 1:33:53for files on your file system.
    • 1:33:55You can check whether it exists with love.filesystem.exists at some path.
    • 1:33:59You can write to that path with some data, that data being a string value.
    • 1:34:03And then love.filesystem.lines is an iterator,
    • 1:34:08which will allow you to look over any of the data that's
    • 1:34:11in a file at a given location.
    • 1:34:12Yes.
    • 1:34:13AUDIENCE: [INAUDIBLE]
    • 1:34:16COLTON OGDEN: Yeah.
    • 1:34:17AUDIENCE: Does this work if you [INAUDIBLE]
    • 1:34:20COLTON OGDEN: It should.
    • 1:34:21We can pull that up now actually and see.
    • 1:34:24Because I know on their Love2D--
    • 1:34:28so file system.
    • 1:34:30So the question was he ported his--
    • 1:34:35when you port your Love app to the iPhone,
    • 1:34:39will it have the same sort of behavior if you're--
    • 1:34:47on an iPhone, will it have the same sort of save directory behavior?
    • 1:34:50And it looks like it's not officially on here.
    • 1:34:53I know that there is an iOS port for Love2D,
    • 1:34:57or the ability to send it to Love2D.
    • 1:35:02AUDIENCE: [INAUDIBLE]
    • 1:35:05COLTON OGDEN: I have to imagine yes.
    • 1:35:07It probably has some sort of--
    • 1:35:08I'm not entirely familiar with how iOS handles sort of local storage,
    • 1:35:14but I'm assuming that just in the way that it's been abstracted for desktops
    • 1:35:18and for Android, it's also abstracted for iOS.
    • 1:35:20Haven't tested it myself.
    • 1:35:22I would experiment and see actually maybe with this code.
    • 1:35:25See if you can maybe get it working with persistent high scores.
    • 1:35:29I know that iOS does typically let you store a small amount of data per app
    • 1:35:33in some location, a fixed location, but I'm not
    • 1:35:35entirely sure what that is offhand.
    • 1:35:37I can look into it more and come up with a--
    • 1:35:40AUDIENCE: [INAUDIBLE]
    • 1:35:41COLTON OGDEN: Yeah.
    • 1:35:42I mean, not from firsthand because I don't have an Android,
    • 1:35:45but it has official Android support.
    • 1:35:47So I'm guessing it does, but I haven't tested it.
    • 1:35:50I have not tested it manually on Android to verify that.
    • 1:35:59But yes.
    • 1:36:00I believe-- because in the prior directory we were looking at when it
    • 1:36:03showed--
    • 1:36:05oh, it's actually up here.
    • 1:36:08This path here.
    • 1:36:09This data/user/0/love2d.android.
    • 1:36:14file save.
    • 1:36:15That looks to me like it's the official sort of path
    • 1:36:18that data is stored on an Android device for application.
    • 1:36:22So I haven't tested it myself.
    • 1:36:24But if you have an Android and you're curious or maybe an emulator,
    • 1:36:27give it a shot and see if it works.
    • 1:36:30Oh, and it even says here, there are various save locations.
    • 1:36:32And if they don't work, you can see what the actual location
    • 1:36:35is with this function here.
    • 1:36:37The love.filesystem.get save directory.
    • 1:36:39That may work on iOS as well, so I'd be curious to hear about
    • 1:36:43whether that actually works on that.
    • 1:36:50Yeah.
    • 1:36:51So that's the gist.
    • 1:36:52Using the love.filesystem abstraction lets us read and write files.
    • 1:36:55We can then just paste or we can just save whatever data
    • 1:36:58we want anywhere within that directory.
    • 1:37:02We can just create files in there and then use those to store our,
    • 1:37:06you know, sort of game worlds, or character profiles, or whatnot.
    • 1:37:10How would we maybe go about implementing sort of like a high score list?
    • 1:37:21So I'll look.
    • 1:37:21There's a picture here.
    • 1:37:24So we have 10 scores.
    • 1:37:27We'll assume that's fixed.
    • 1:37:29Each of the scores has a name, and then each of the scores has an actual score.
    • 1:37:34So all we really need to do is just store ultimately the names
    • 1:37:39and then the scores.
    • 1:37:42AUDIENCE: [INAUDIBLE]
    • 1:37:47COLTON OGDEN: So we'll use an array.
    • 1:37:49Their response was we'll use an array as sorted by that score.
    • 1:37:53Yeah.
    • 1:37:53Essentially that's exactly it.
    • 1:37:55We're just going to keep a score table and each table
    • 1:37:57is going to have a sub table.
    • 1:37:59And each of those entries, one through ten,
    • 1:38:00is going to have a name and a score.
    • 1:38:02And then once we're done with our application,
    • 1:38:05we'll just use love.filesystem.write.
    • 1:38:07We'll have to convert all of those into a string
    • 1:38:10because we can't just take a table and then spit that out into a file.
    • 1:38:15We have to actually make it into some form that we can save
    • 1:38:18and then reload back in somehow.
    • 1:38:20What would be the most efficient way, do you think, or a way we can do this?
    • 1:38:28Probably just a new line separated list.
    • 1:38:31The way that I've done it in this application
    • 1:38:33is just names, and then new line, score, new line, name, new line, score.
    • 1:38:3910, so 20 rows.
    • 1:38:41And that gets the job done.
    • 1:38:43Assuming that you don't tamper with the file, then everything should work.
    • 1:38:46And you can write additional code as well
    • 1:38:48to say, oh, if there is a score that's all
    • 1:38:51garbled, we don't have enough scores, then probably
    • 1:38:53should render it accordingly.
    • 1:38:55My code does something similar to this, but not entirely.
    • 1:39:02The relevant code-- and I'm going to sort of just glaze over it.
    • 1:39:06If we're looking at-- this is Breakout11, right?
    • 1:39:08Yeah.
    • 1:39:09Oh, no, this is Breakout10.
    • 1:39:11So in Breakout10, we have to load all the high scores
    • 1:39:14in main.lua, which is here.
    • 1:39:16So set identity to Breakout or create a folder called Breakout
    • 1:39:19that we can save and read files to and from.
    • 1:39:22If it doesn't exist, then just create them.
    • 1:39:24In this case, I'm just seeding CTO my initials.
    • 1:39:26And then I times 1,000.
    • 1:39:28So 10,000 down to 1,000.
    • 1:39:30Just very simple data.
    • 1:39:33Writing into a file called breakout.lst.
    • 1:39:34It can be whatever you want.
    • 1:39:36All we're doing is reading lines from the data, or from the file.
    • 1:39:40And then this is if it doesn't exist.
    • 1:39:42And then if it does exist, then we're going to iterate over it
    • 1:39:50with love.filesystem.line, which will take a file
    • 1:39:53and then just split it on new lines basically and give you
    • 1:39:56an iterator over all those lines.
    • 1:39:58So it can just say, OK, if it's a name, which
    • 1:40:00means that if it's one or three or five or seven in the list,
    • 1:40:06then set the name to--
    • 1:40:08and we're using string.sub just in case they
    • 1:40:11write some long name or some long name gets-- they can't do it
    • 1:40:14through our game, but if it gets written to the file as some long name,
    • 1:40:17it should get truncated to three characters
    • 1:40:19so we can display it appropriately.
    • 1:40:22And then otherwise if we're not on a name line,
    • 1:40:24if we're on, like, an odd line or even line,
    • 1:40:28we should consider that a score and just use to number.
    • 1:40:32Because we're using string data and if we try to assign,
    • 1:40:37do any sort of comparisons numerically on the string data,
    • 1:40:39which we will have to do to compare high scores,
    • 1:40:41it's not going to work because it's going to see that there's strings.
    • 1:40:44So we use to number here.
    • 1:40:45Just a simple Lua function.
    • 1:40:46And then that's it.
    • 1:40:47And then we just return scores.
    • 1:40:50And then I'll sort out what's causing the issue,
    • 1:40:53and then push that to the repo ASAP.
    • 1:40:56But that has the effect of us being able to actually load all of our high scores
    • 1:41:00and display them at the start of the game.
    • 1:41:03It doesn't take care of being able to actually input our score.
    • 1:41:08And so we can do this with Breakout11, which you can see if you run the repo.
    • 1:41:12And you can test just to assign your initial score to some value like 10,000
    • 1:41:17or 20,000, and then just lose on purpose and you
    • 1:41:19get a sense of how it actually works.
    • 1:41:21But essentially, it's just arcade style.
    • 1:41:24You know, you had only three characters you could input your name.
    • 1:41:26So does anybody have any idea as to how we are sort of storing this,
    • 1:41:31or can pitch an idea?
    • 1:41:36So we have three characters and we want to--
    • 1:41:41ideally if we're, let's say I want to go to C on the first one.
    • 1:41:45Let's say I pressed up twice so I get to C. How is it going from A to C?
    • 1:41:52You could just say, you could just render
    • 1:41:54I want to render the character A, the character A, the character A,
    • 1:41:58but how is it going to know when you want to go to B, or C, or D.
    • 1:42:01AUDIENCE: [INAUDIBLE]
    • 1:42:15COLTON OGDEN: The pitch was, you could create
    • 1:42:16a table with all of the characters and iterate through it.
    • 1:42:19You absolutely could do that.
    • 1:42:22It's a little bit bulky.
    • 1:42:23That might be what--
    • 1:42:24actually, that's probably not how arcade systems did it back in the day.
    • 1:42:28Because the way that we're going to do it here in Breakout11 is I
    • 1:42:38added a new state called EnterHighScoreState.
    • 1:42:43And if you recall, CS50 teaches this.
    • 1:42:46But all sort of characters at the end of the day are just numbers.
    • 1:42:51ASCI.
    • 1:42:53In this case, 65, if you recall, is capital A.
    • 1:42:58So all we need to do is just draw out whatever that character
    • 1:43:03cast to a string is, or character.
    • 1:43:06And we do that simply down here in the draw function.
    • 1:43:11If we do string.char, at char is three.
    • 1:43:19All that has the effect of doing is just taking that number
    • 1:43:22and then converting it to a character.
    • 1:43:25So all we need to do then is what?
    • 1:43:27When we want to go from A to B, B to C, C to D.
    • 1:43:33AUDIENCE: [INAUDIBLE]
    • 1:43:35COLTON OGDEN: Exactly.
    • 1:43:36But then what happens if we're at A and we want to go down?
    • 1:43:39AUDIENCE: [INAUDIBLE]
    • 1:43:41COLTON OGDEN: We would.
    • 1:43:42So if we're at A, then if we press downward and we want to go to Z,
    • 1:43:49the logic is in here.
    • 1:43:53But one we've incremented our code, if it's greater than 90, which is Z,
    • 1:43:59then we should set it back to 65.
    • 1:44:00We'll loop back to A.
    • 1:44:01And same thing here.
    • 1:44:02If we press down and we're at A, we've got to go back up to Z,
    • 1:44:06so we just set it to 90.
    • 1:44:08So simple loop back logic.
    • 1:44:09And we just draw it, we highlight.
    • 1:44:11And then once we've done that, the user presses Enter.
    • 1:44:14We transition to the HighScoreState, actually,
    • 1:44:19because this state should only trigger if they entered a new high score.
    • 1:44:22Which means that we need to check in the VictoryState, or not the VictoryState,
    • 1:44:26but rather in the GameOverState whether or not
    • 1:44:29their score is higher than any of the stores in some sort of,
    • 1:44:33quote unquote, global scores table.
    • 1:44:36And then how do we think we're passing the scores back and forth now?
    • 1:44:42Does anybody recall how we're keeping track of app state?
    • 1:44:48AUDIENCE: [INAUDIBLE]
    • 1:44:55COLTON OGDEN: Yep.
    • 1:44:55In the change function.
    • 1:44:57So all we need to do is keep track of-- load
    • 1:44:59our high scores at the beginning of the game,
    • 1:45:01pass them all the way down the line.
    • 1:45:03And then finally-- and we can also load them in our EnterHighScoreState,
    • 1:45:07but we need to keep track of what our high scores are in the GameOverState
    • 1:45:11so that we know, oh, I've got a high score.
    • 1:45:13Let's instead of transitioning back to the StartState, let's transition
    • 1:45:16to the EnterHighScoreState so the user can add their high score to the list.
    • 1:45:22And then once they've entered their high score, which is here,
    • 1:45:30we'll just write it to this file again.
    • 1:45:35Compile a score string, which takes name and score of our scores.
    • 1:45:39We take whatever score that we were at that's--
    • 1:45:43we look through our scores table backwards
    • 1:45:45and when we find a score that's lower than ours,
    • 1:45:47we just keep track of that index until we get to one that's higher than ours.
    • 1:45:51In which case the one plus one, that index plus one
    • 1:45:54is what we should then overwrite.
    • 1:45:55And so we shift all the other ones below accordingly.
    • 1:45:59And we do that in this class if curious.
    • 1:46:01And so I'm just going to breeze through the last couple.
    • 1:46:04The paddle select update is just kind of a fluffy state
    • 1:46:07that lets us add a element of sort of, like, user selection to our game.
    • 1:46:13In our PaddleSelectState here, we transition immediately.
    • 1:46:18Instead of going to the [INAUDIBLE] PlayState now,
    • 1:46:21we're going to go from Start to Paddle Select when we hit Start Game.
    • 1:46:26So we're going to go to--
    • 1:46:27and then the Paddle Select class itself.
    • 1:46:32CurrentPaddle gets one, and then all it essentially
    • 1:46:35is is us drawing two arrows here.
    • 1:46:42And so if we're at number one-- in this case, I think we're at number three--
    • 1:46:47then both of these arrows will be completely opaque.
    • 1:46:49But if we're on the left or the right edge, they should darken to say to us,
    • 1:46:53oh, we can't move left or right anymore because we're
    • 1:46:55at either index one or four or five, and there's only that many colors.
    • 1:47:00And then render whatever that color variable
    • 1:47:02is using the quads table that we had before of the different tables.
    • 1:47:07And then just instructions.
    • 1:47:09And then from there is where we'll end up transitioning to the ServeState
    • 1:47:13rather than going to the ServeState from the StartState.
    • 1:47:18And all the code in that is here.
    • 1:47:20We have sound effects playing.
    • 1:47:22And then making sure that we also play a different sound
    • 1:47:26effect based upon whether they're at the left or the right edge.
    • 1:47:29If they're on the left edge and they try to go left,
    • 1:47:30it should play like a sound that sort of sounds a little rougher
    • 1:47:33to let them know that they can't go left,
    • 1:47:35and the opposite for the right edge.
    • 1:47:38And then once that's all done, once they press Enter on whatever paddle
    • 1:47:42they want, they're going to get the paddle,
    • 1:47:46we're going to instantiate a paddle, pass that into the ServeState,
    • 1:47:50and we're going to take currentPaddle from the state, which
    • 1:47:53is whatever value they got by scrolling between all the different paddles.
    • 1:47:59And then the last update, which is my favorite part of most every lecture
    • 1:48:06I think is the music update.
    • 1:48:09And all that really is is just music set play in main.lua,
    • 1:48:15and then set looping to true, and then we have a game.
    • 1:48:19And this is our Paddle Select.
    • 1:48:20So notice the arrows are semi-opaque on the left and the right.
    • 1:48:25It's kind of hard to hear, but when I press right now
    • 1:48:30it's kind of like there's a bit of a rougher sound.
    • 1:48:33We choose red.
    • 1:48:35We go to level one and we transition to the ServeState
    • 1:48:37from the PaddleSelectState, and then we just play the game as normal.
    • 1:48:43And that's basically all there is to it.
    • 1:48:45And there is a couple of features we didn't have time to really go over
    • 1:48:48like making sure we recover HP if a certain amount of points
    • 1:48:51have been elapsed, but I encourage you to look
    • 1:48:53into that when you trigger a hit.
    • 1:48:56There's some logic in the PlayState to say, oh,
    • 1:48:58if they've gone over a current recovery threshold,
    • 1:49:01let's add one heart to the player, you know, just keep them playing.
    • 1:49:04Just to reward them for their high score.
    • 1:49:08Next time we'll cover a few concepts.
    • 1:49:10So basic shaders.
    • 1:49:11Shaders are like little programs you can run in your graphics card
    • 1:49:13and do fancy effects, but we won't go into too much detail.
    • 1:49:16Anonymous functions.
    • 1:49:17We've seen a lot of anonymous functions in Lua in the context of Love.
    • 1:49:21They're just functions without a name, and you can just
    • 1:49:23use them as function arguments and do all sorts of cool stuff with them.
    • 1:49:26We'll use them for callbacks next week when we do things
    • 1:49:29like tweening, which is taking some value
    • 1:49:31and making it interpolate over time to some other thing.
    • 1:49:35Because right now we've basically just been updating things based on velocity,
    • 1:49:38but we haven't really done anything based on time.
    • 1:49:40So we'll take a look at that in more detail
    • 1:49:42next week with a library called timer, which is really fantastic.
    • 1:49:46Lets you time things and then chain things together.
    • 1:49:50We'll be covering the game Match Three if familiar.
    • 1:49:52It's basically Candy Crush.
    • 1:49:54We'll be using a different tile set, but it's the same idea.
    • 1:49:57And we'll have to calculate how to actually find out
    • 1:49:59whether we've gotten a match in the grid, our tile grid,
    • 1:50:02and then shift the blocks accordingly and do all the other logic, add score.
    • 1:50:06And then basically since it's so fundamental to Candy Crush and games
    • 1:50:10of its nature, we will have to cover how to sort of generate
    • 1:50:13these maps procedurally to have tiles that are laid out in a dynamic way,
    • 1:50:17and also in a way that doesn't start off with any matches
    • 1:50:19because then that wouldn't make any sense because the matches have
    • 1:50:22to resolve.
    • 1:50:23And then we'll take a little time if we have the time next week
    • 1:50:26to talk about sprite art again and palettes.
    • 1:50:28And maybe I'll show you guys how to sort of convert images from one
    • 1:50:32palette to another in, like, a program that I use, Aseprite,
    • 1:50:35but you can do this in any sort of large photo editing software.
    • 1:50:40And then assignment two is a couple of extensions to Breakout.
    • 1:50:44So if you noticed in the sheet there were a few little sprites here
    • 1:50:50at the bottom--
    • 1:50:51so get rid of the quad outlines.
    • 1:50:53So these little things down here are, I'm assuming,
    • 1:50:56they're meant to be power ups.
    • 1:50:57They look like power ups.
    • 1:50:58But the goal of the pset is to implement a power up.
    • 1:51:01And a power up is going to be such that when you grab it,
    • 1:51:05you'll get two additional balls, or however many you want actually,
    • 1:51:08that will spawn in addition to your one and detect collisions on their own.
    • 1:51:12So you'll have several and they'll score points for you.
    • 1:51:14And, of course, only when the last ball comes below the surface of the screen
    • 1:51:18should you trigger a Game Over.
    • 1:51:20And then I want you to add-- and this will also
    • 1:51:22be more detailed than the spec-- but I would
    • 1:51:24like you to add growing and shrinking to the paddle.
    • 1:51:26So currently, we have like four different sizes of paddle,
    • 1:51:29but we're not using them.
    • 1:51:31So it would be nice if when we gain enough points or we lose points, or not
    • 1:51:34points, but lives rather, we increase or decrease the size of the paddle
    • 1:51:37accordingly just to introduce another level of challenge
    • 1:51:40and or lack of challenge.
    • 1:51:42And then finally, one last part which is in the sprite sheet as well,
    • 1:51:46there's a key block here and a key power up here.
    • 1:51:54So sort of let the power up come, pick the power up with your paddle.
    • 1:52:00And then only when you have that power up should you
    • 1:52:02be able to break the block with a key.
    • 1:52:06And you should take this into consideration
    • 1:52:07when generating your levels as well.
    • 1:52:09So you'll have to also get your hands dirty with the level maker.
    • 1:52:12But all in all, that was Breakout.
    • 1:52:14So I'll see you guys next time.
    • 1:52:16Thank you.
  • CS50.ai
Shortcuts
Before using a shortcut, click at least once on the video itself (to give it "focus") after closing this window.
Play/Pause spacebar or k
Rewind 10 seconds left arrow or j
Fast forward 10 seconds right arrow or l
Previous frame (while paused) ,
Next frame (while paused) .
Decrease playback rate <
Increase playback rate >
Toggle captions on/off c
Toggle mute m
Toggle full screen f or double-click video