SPEAKER: So with pong2, we took a look at how to actually get something more visually interesting on the screen. We ended up using love.graphics.rectangle with the line and fill options just to illustrate the difference to get paddles onto the screen. Now they're not actually functional yet. And that's going to be part of the goal of this update to actually move the paddles. We also ended up drawing the ball. And we chose a different custom font, and we put that a little bit higher up on the screen. Now you'll notice this is pretty largely similar here on this exact example, the slide. We can't really illustrate the moving of the paddles. But we also are going to add the score in the middle of the screen. And we're going to use a larger version of the font that we imported from the last example. And it's incredibly easy to do so. And it will also let us illustrate how to actually sort of flick those state machine switches to change the font-rendering mode of the program. We only have one important function to talk about in this example, and that's love.keyboard.isDown. It takes a key as a parameter, which is a string just like love.keypressed. The difference between love.keypressed and love.keyboard.isDown is love.keypressed is a function, a callback function, that we, ourselves, sort of override, we implemented, and we sort of defined the custom behavior. Love.keyboard.isDown is just a function we can call anywhere and returns a Boolean, depending on whether or not that key is pressed down. We can call that in any other function that we want to. Another difference is that love.keypressed is actually just a single fired function, so it'll only fire if we press a key one time. And we can't check to see whether a key is actually being held down. Now when we move our paddles, we do want the movement to be more continuous. If up is being held down, we want to move the right paddle up and down, as long as it's being held down, and same thing for the left paddle. And we're going to use WNS for that. So I've already set up a distro here, sort of a folder here with some code. We took the code from the last example but given in the distro for pong2. And we're going to add our own custom functionality. So the first thing that I want to do is we have a small font for Hello Pong written at the top. But I want a different font for the actual score. So what I'm going to do is I'm going to say a Largefont. We can call this scoreFont, which is probably what it is in the distro. So actually, that's what I'm going to do. I'm going to say scoreFont is equal to love.graphics.newFont. We're going to actually use the same file, font.ttf. And I'm going to say that this should be a 32 pixel size font. So now what we do, if I say love.graphics.setFont of scoreFont here just as a test-- whoops. And that did not work. So let's go ahead down here. And I'm realizing that this is actually still derived from the older distro, which doesn't have everything divided by 255. You do have to do that. So there we have Hello Pong written out in large text. Now we don't want that obviously for our final thing here. So what I'm going to is I'm going to go back. I'm going to set the font here to the smallFont. And the scoreFont is only going to get set when we draw the score. So actually, I'm going to get rid of this all together, the love.graphics.setFont. I'm going to come down here and where I actually draw the Hello Pong, I'm just going to say love.graphics.setFont smallFont, which functions identically, more or less. But now it's more localized. So when we actually are going to draw Hello Pong, we change the font. When we want to draw the score, well, that's a different situation. So here, I can say love.graphics.setFont of scoreFont. And let's say I want to draw love.graphics.print. I'm just going to do love.graphics.print for now. I'm not going to do printf. I'm going to say, well, actually we need a score to draw. So why don't we go ahead and set that up here at the top? So I'm going to say here in love.load, I'm going to say player1Score is going to be equal to 0, and player2Score is going to be equal to 0. So now we have a score that we can actually draw to the screen. So let's go ahead and do that. So I'm going to say love.graphics.print player1Score. And I want to print this at, let's say, VIRTUAL_WIDTH divided by 2 minus 50. And let's say VIRTUAL_HEIGHT divided by 2-- let's say VIRTUAL_HEIGHT divided by 3. Let's draw it about a third of the way down the screen. And let's do the same thing here, love.graphics.print of player2Score-- if I can type-- player2Score. VIRTUAL_WIDTH divided by 2 plus 30. VIRTUAL_HEIGHT divided by 3. And let's just test this out. Let's see if it works. Let's do that. Yeah, and it works pretty well, pretty perfectly. Awesome. So let's go back to over here. Now the next thing that I want to do is actually get the paddles moving. In order to do that, what I need to do is say-- well, first of all, we've already been keeping track of where the paddles should be drawn on the y-axis. And this is important because the paddles are only aligned to the y-axis. They don't move left and right, which is the x-axis. They only move up and down. So we need a way to sort of manipulate that. That should be a variable now, instead of just a static number. What we've done thus far is actually set this VIRTUAL_HEIGHT minus 50 and VIRTUAL_HEIGHT divided by 2 minus 2-- sorry, that's the ball. The paddle here is at 10, 30, so 30 being the left paddles y-axis coordinate and VIRTUAL_HEIGHT minus 50 being the right paddle's y-coordinate. So I'm just going to change that. I'm going to say that this should be player1, let's just say Y, and player2Y right here. Just like that. Now we don't have those declared just yet. So I'm going to declare them right here. I'm going to say player1Y is going to be equal to 30. And player2Y should be equal to VIRTUAL_HEIGHT minus 40. So let's go ahead and run this. Make sure it works. It looks like it still does more or less work the same exact way. Now, the trickier part is going to be actually manipulating this value. And this is something where we actually haven't used this function yet. But this is a very important part of the game loop that we alluded to before. And this is the update function. So anytime, you want to actually change the position or the configuration or anything about your game, typically depending upon user input but also dependent upon the sort of AI that you have running in the background, you want to override love.update, which should take in a parameter called dt. Now dt is very interesting. dt is what actually allows you to move things independent of the frame rate at which you're running your application. It's often the case, or was often the case, that back in the day, especially for older consoles like the NES, even to this day really for hardware that you know is going to run a particular way, you can just calculate everything on a per frame basis and not worry about how much time has passed since the last frame. If you're running this on multiple computers that might be at different speeds, then the frame rate might actually vary depending on the power of the CPU and the GPU. So in order to sort of normalize this, you multiply all of the calculations that you want to have happen by the amount of time that's passed since the last frame. So this might be 0.016 or whatever the amount is, every 1/60 of a second. It might be like 30 frames has passed, or it might be like it's calculating 30 frames because twice the time has elapsed since the last frame. And so you might want something to move twice the distance to account for that. Otherwise, you'll experience slowdown in your game because fewer frames are actually elapsing over time. It gives you the feeling that your game is actually lagging. So that's what dt is. It's sort of a normalizing scale factor. And we're going to use that to calculate our movements such that it's uniform across all computers. So what I can do is I can say if love.keyboard.isDown, let's just say w. So for WASD, it's very common for games on computers to be WASD for movement. We're going to also use the arrow keys for the right paddle-- so WASD for the left, arrow keys for the right. So if love.keyboard.isDown w then. And then, we'll also say just in advance elseif love.keyboard.isDown s. So they're mutually exclusive in this case. One or the other will trigger but not both. And I'm actually going to also set it up here, love.keyboard.isDown of up, so up for the arrow key up then elseif love.keyboard.isDown down then-- and you'll notice the elseif does not have a space. That is a Lua syntactical feature. Make sure that there is no space therein. So if love.keyboard.isDown w then, well, we need to manipulate the player1Y. So we can say player1Y. And w means up. So remember y going up is actually negative, so we need to subtract some value from the player1's y-axis. So we'll say player 1's y-coordinates. So we'll say player1Y equals player1Y minus. And then we need some value, some sort of speed. So let's define a constant. Let's just say paddle speed up here. So we'll say PADDLE_SPEED is equal to 200. And this is going to equate to 200 pixels per second. So we'll say PADDLE_SPEED is equal to 200. We'll say minus PADDLE_SPEED if I can type. And then, again, this needs to be scaled by delta time such that it's uniform across all frame rates. So we're just going to multiply PADDLE_SPEED by delta time. And that will have the effect of ensuring that the paddle moves at 200 pixels per second regardless of the frame rate. Even if it's at 1 frame per second, that dt will become 1 because 1 second has elapsed. Therefore, it'll still move 200 pixels per second. We'll do the same thing here with player1Y is equal to player1Y plus PADDLE_SPEED times delta time for going down. So again, up is negative. Down is positive. And then we'll say player2Y is equal to player2Y minus PADDLE_SPEED times dt here. And player2Y is equal to player2Y plus PADDLE_SPEED times dt here. Now just make sure that I have also set player1 and player2Y there, which I have. And let's run this just to make sure it works. And I'm pressing up and down, and the paddle is moving on the right. And if I press w and s, the paddle is moving on the left. Now you'll notice, we can move beyond the edges of the screen, which is probably not desirable behavior. And we're going to get to covering that very soon here. So let's go over to the slide deck and then just as a teaser. The next section-- before we actually get into clamping the value of the paddles, and we actually might explore that in the next section, we're going to talk about how to actually get the ball moving. So you'll notice that, currently, the ball is still completely static. That's not changed. And that's really the main driver of our application. So in the next update, we're going to apply a random velocity to our ball such that we have now not the paddles but also the ball moving. So stay tuned for pong4.