Hello, everybody, and welcome to another YouTube video.
In today's video, I'm going to be showing you how to make the famous game Pong in Python.
Now, this is a great project for beginner or intermediate programmers.
We are going to be using a module called PyGame to accomplish this.
However, you do not need to know PyGame, and the features we're going to use to
our very limited.
A lot of what we need to code out is the logic related to moving the ball around the screen,
having it bounce off the different paddles, implementing the score,
all of that type of stuff, and you're going to learn a lot in this project
if you are not, say, an expert Python programmer already.
With that said, let me give you a quick demo, and then we'll actually start writing some code.
So in front of me, I actually have the finished code for this project.
Whenever I do a tutorial, I code it out first, and then I reference that code
while I'm teaching it to you guys.
And all of the code that we write in this video will be available in the description.
Anyways, let me just run this here, and then we can have a look at the finished product.
Okay, here we are.
So this is Pong.
We have two paddles.
You can move one paddle with W and S, and the other paddle you can move with the arrow keys,
and you also would be able to implement this as a single player game if you wanted to code
like an AI for one of the paddles, which would be pretty easy to do.
You can see this is kind of the basics.
We have an implementation of Pong, and then of course they're scoring at the top,
and I've just made it so that when you get to 10, then the game is over.
I'm not going to play through the entire thing, but there you go.
That is the project, and I'm going to show you how to make it.
So with that said, let's get into the code after a quick word from our sponsor.
Thanks to Backtrace for sponsoring this video.
Backtrace provides application monitoring as well as error and crash reporting for games.
We've all been there, excited to launch a brand new video game just to be tormented
by crashes, bugs, and an overall bad user experience.
Backtrace wants to help limit that by providing a platform that gives game developers the best
error and crash reporting with the most complete and helpful information.
Backtrace works with any platform, any engine and at any scale, and provides 24-7 monitoring
so you can retain more players and get better ratings.
Backtrace helps you fix issues fast by providing accurate call stacks,
regression detection, querying and analytics, and integration with Microsoft Teams,
Discord Slack, and more.
You can get started with Backtrace today and manage up to 25,000 monthly errors
with one month retention and 10 gigabytes of storage completely for free.
Check it out from the link in the description, and thanks again to Backtrace for sponsoring this
video.
All right, so let's go ahead and get started.
The first thing we need to do is just set up our environment and install the Pi Game package.
Now, again, I want to reiterate here that you do not need to know Pi Game for this tutorial.
I will show you the limited features from it that we are going to use.
Now, if you want to learn more about Pi Game and make some more advanced games,
I have tons of tutorials on my channel.
So feel free to check those out.
You can probably just go on YouTube and search Pi Game,
tech with Tim, and you'll see like 10, 20, 30 different videos,
all going through different Pi Game projects.
All right, with that said, let's get this set up.
So right now, I'm in visual studio code.
This is the editor I'm going to use for this video.
Feel free to use whatever you want.
You can use the default idle from Python.
You can use sublime text.
Really doesn't matter.
I'm just going to use VS code.
Now, I'm using Python version 3.9.
You can use pretty much any version as long as it's above the 3.6.
Other than that, what we need to do here is open up a terminal or a command prompt and install
Pi Game.
Now, to do that, we're going to type in the command,
pip install Pi Game like that.
Now, I already have this installed, so I'm not going to run it.
But for you guys, run this command.
It should install Pi Game.
Now, this command does not work sometimes.
If it doesn't work, try the following Python.
And then you're going to do hyphen m and then pip install Pi Game.
If that doesn't work, try Python 3, hyphen m pip install Pi Game.
And finally, if that doesn't work, try pip 3 install Pi Game.
Now, if none of those commands work for you,
I have two videos, one for Mac and one for Windows.
I will put them up on the screen and they show you how to install Pi Game
for the respective operating systems.
Okay, so we now have Pi Game installed and we can actually start writing some code.
Now, we want to create Pong.
So the first thing we're going to do here is just import Pi Game.
We're going to set up what's known as a display.
So in Pi Game, we have a main kind of window or display.
And that's where we draw everything too.
Once we have the display, then we'll implement things like the paddle, the ball,
the scoring, a handling collision, all of that type of stuff.
So I'm going to import Pi Game at the top of my program.
And now I'm going to set the width and the height for my window.
Now, you're going to notice here when I'm coding everything out,
that I'm putting everything in variables.
This way our game will be dynamic and you can simply change the value of a variable
and everything will just work and adjust according to that.
So rather than using kind of hard-coded values,
we're going to put everything inside of variables,
use the variables for everything which will make it a little bit more complicated to code out.
However, it's going to be really nice because if you want a bigger window or a smaller
window or a larger paddle or whatever, you just change the variable.
So for the width and the height, I'm going to go with 700 and 500 for right now.
By the way, this is a way that you can declare kind of two variables on the same line in Python.
Okay, so now that I have that, I'm going to set up my window.
So I'm going to put this in a variable called win in all capitals.
Whenever I do something in all capitals, I'm making a constant,
meaning that this variable is not going to change.
So to do this, I'm going to say pi game dot display dot set underscore mode.
And then inside of here, I'm going to put a tuple and I'm going to pass the width and the height.
And I guess at this point in time, it's a good idea to mention that you should have some
familiarity with Python. Of course, you don't need to be an expert.
You can be a beginner, but you should know things like if statements while loops for loops,
because I'm not going to explain all of the very basics.
Anyways, to set up a window of this, so we do pi game dot display dot set underscore mode.
And then we pass a tuple, so just the brackets like this with width and height.
Great. Now that we have our window, we can make a caption for it.
So the caption is just going to be the title of the window.
And to do that, we do pi game dot display dot set underscore caption.
And for the caption, I am just going to call this pump.
Okay, so this will just be the title at the top of the window when it's actually loaded up.
So now that we have this, what I want to do is implement what's known as the main loop
of my program or the event loop of my program, which is actually going to display the window
and then draw something onto it. So I'm going to define a function here.
I'm going to call this main and inside of this function, I'm going to declare a few variables
that we're going to use to actually kind of show the display and handle all of the events that
are occur. So I'm going to make a variable here called run. I'm going to make this equal to true.
And I'm going to do a while to appear and say wild run.
Now whenever we have a pi game game, I guess pi game program, we need a main loop.
And the main loop is just a loop that's constantly going to be running that's handling everything
related to our game. So it's handling collision. It's moving the ball. It's allowing us to move
the paddle. So that's what this is right here. So inside of this main loop, I'm going to write the
following for event in pi game dot event dot get. Now this will get all of the events like
clicking your mouse, clicking the keyboard, closing the window. That's what this loop is doing
here, looping through all of the different events that have occurred. We're going to handle those
events and then do something. So in here, the first event that I want to check is if we are actually
quitting the window. So I'm going to say if event type is equal to pi game dot quit with all
capitals for quit here, then I'm going to say run is equal to false. And I'm going to break out of
this for now. What this is going to do is check if we hit the red button in the top right hand
corner of our window. So the close button, if we hit that, we want to stop the main loop. So we
actually end up closing the program. And then we want to break. Now outside of my while up here, I'm
going to say pi game dot quit. And quitting is just going to quit pi game and close the program for
us. Okay, awesome. Now, one thing I need to do here is right after our import pi game. I need to
initialize it. So I'm going to say pi game dot init. You should run this whenever you import pi
game just directly below it. It just initializes a few things that you need. I will use this later
or this will allow us to do some things later. Okay, so here we go. We have a basic program.
We have main. We have run equals true. We have our while loop here, which is going to be the main
event loop inside of this. We're checking all of the different events. And then what I'm going to do
here is call the main function. So I'm going to say if name is equal to main, then call main. Now,
what this does is ensure that we are running this module to call this function. So essentially,
if we were to import this module, so the solution dot pi file, then what would happen is this
would not run because the name of this would not be main. Now, I'm not going to explain
exactly how this works, but it really just makes sure that you're only going to run this main
function. If you directly run this Python file, not if this Python file was imported from
another project or from another file. Okay, hopefully that makes sense. But when we do this,
we should see a pi game window pop up. There won't be anything on the window. It'll just be a
black screen, but that's the thing we need to start out with. Okay, so there we go. We can see it
it says pong. We have our pi game window. And when we click the exit should quit,
great, there we go. It quits. Okay, so that's our starting thing. Now, the next thing I'm going
to do is implement something known as a clock. Now, a clock is going to regulate the frame rate
of our game. So that it's going to run at the same pace on every single computer. So I'm going
to say clock is equal to pi game dot time clock. And then inside of here, I'm going to say clock
dot tick. And I'm going to pass this in all capital FPS variable, which I'm going to define up
here as 60. Now, the FPS is the frames per second. And when you put this inside of a wall loop,
it makes sure that you cannot run faster than 60 frames per second or 60 ticks per second,
which means that this wall loop here is going to run a maximum of 60 times per second. So that
if you're on a really fast computer, it's not going to be running quicker than if you were on a slow
computer. Hopefully that makes sense, but that's why we want the clock. It just regulates the speed
of this wallet. Now, it's worth noting that if you're on a very, very slow computer, this wall loop
may run slower than the FPS that you're putting here. This is simply limiting the amount of times
it can run. It's not making it run 60 times per second. So on almost all modern computers, it will run
at least 60 frames per second or I guess exactly 60 frames per second. But on a very slow computer,
you may run under this. And so you may see some lag in your game if it's a really slow computer.
Just want to note that because some people have mentioned that in my previous pie game tutorials.
Okay, so now we have our clock. And the next thing I want to do is implement something to actually
draw some stuff onto the screen. So I like to handle all of my drawing in a separate function,
so it's really easy to see where I'm drawing everything. So I'm going to make a new function here,
and I'm just going to call this draw. And this is going to take in one variable, which is going to be
the window that we want to draw on. Now inside of here, all I'm going to do for now is fill
the window with a specific color just to show you how that works. So I'm going to say wind up
fill. And then here I have to pass an RGB value. Now RGB is red green blue. Now I could just pass
the RGB value directly in here, but I like to define all of my colors or RGB values as variables.
So I'm going to make one called white. And this is going to be equal to 255, 255, 255. And then I'm
going to make one called black. And this will be equal to zero, zero, zero. Okay, so those are my two
colors up here. And what do I want to fill the window with? Well, we are going to fill it with black,
but for now, I'll just put whites. We can actually see what's showing up. Now whenever we do some
type of drawing operation in pi game, we need to update the display manually. And then it will
actually do all of the drawn. So when I do something like a wind up fill, this is filling the entire
window with white. So it'll change the background color essentially to white. But for this to actually
happen, I need to save pi game, dot display, not clear, but dot update. Now this will update the
display and perform any of the drawing operations that we've done. So maybe I've done a few other
drawing operations. So I'm going to do all of those and then update the display and updating the
display is kind of the most intensive part. So doing the actual drawing does not take very long,
but updating the display where it applies all of the drawing. That's going to take the longest.
So you only want to do this after you've done all of your drawing. Okay, I'll continue
to explain that in a second. But for now inside of my wall loop, I want to call this draw function.
So every single frame or continually redrawing the window. So let's call draw here and let's pass
to it the all capital win, which is going to be the window here that we want to draw on.
Okay, hopefully that makes sense. Let's run the program now and see if we're getting a white
screen. Okay, so notice we're getting a white background. Perfect. That is what we wanted because
we are filling the window with white. Now I'm just going to change this to black
because we actually want a black background. I just wanted to show you how wind up fill works.
So now that we've done that, what I'd like to do is implement B paddles. So I want to add a
paddle on the left side and a paddle on the right side. And then I want to see the paddles
actually be able to move when we hit the different keys on the keyboard. So I'm going to make a
class here. And I'm going to call this paddle. And the reason why we're going to do this
is because we're going to have multiple paddles. And we want their movement and different properties
to be stored as an object so that we don't have to kind of repetitively code this up.
Now if you're unfamiliar with object oriented programming, I'll explain kind of the basics
of what I'm doing here. But I do have a ton of videos on my channel explaining object
oriented programming in Python. Okay, so for the panel, I'm going to say define a knit.
Now this is essentially what's going to be called when we initialize a paddle or create a new
paddle. And what I want to take in for the paddle is an x, y, width and height. Now our paddles
is just going to be a rectangle. One will be on the left hand side, one will be on the right hand
side. And they're going to have different x and y coordinates on the screen. And we're going to
change the y coordinate based on where we're moving, right? So if the user presses the up arrow
key, want to move it up, they press the down arrow key, want to move it down. So I'm going to say
self dot x is equal to x. Self dot y is equal to y. Self dot width is equal to width and self
dot height is equal to height. Now these are the attributes or properties of this paddle,
which means that each paddle I create will have different x and y and different with the height,
corresponding to what we passed here when we created the paddle. So there we go. We have our
initialization for the paddles. Now I want to write a method on the paddles, which is essentially
a function you can call on them called draw. Now draw is going to do exactly what it says.
It's just going to draw the paddle. Now our paddle is going to be a rectangle. It's going to have
the color of white. So let's define a class attribute here called color. And let's just make this
equal to white because this will be a constant. It's not going to change. So inside of draw,
I want to use the window to actually draw my paddle on the screen. So I'm going to say actually
pigame. Let's make sure the indentation is correct. Pigame dot draw. And then not dot circle,
we want dot rectangle like that. And I keep messing up my indentation. So let's fix that.
And when I'm drawing a rectangle with python, well, this how you do it, pigame dot draw,
dot rectangle. And I need to pass to it where I want to draw it, which is going to be the window,
right? And then I need to pass a color. Well, the color is going to be self dot color. That's
going to reference this right here, which is equal to white. And then I'm going to pass a rectangle.
Now a rectangle is an x, y, width and height. So in pigame, when we draw something, we draw from
the top left hand corner. So in pigame, 0, 0 is the top left hand corner, okay, of the screen.
So if I draw it something like 10, 10, that's going to be 10 pixels, sorry, right. And then 10
pixels down. And then if we're drawing something like a rectangle, for example, the x, y,
that we're drawing the rectangle at is the top left hand corner of the rectangle. And then the
width and height is well, the width and height of the rectangle based on the top left hand corner.
So you'll see what I mean here when I draw this, but I'm going to do self dot x, self dot y,
self dot width and self dot height. And that's all I need to draw the rectangle. Okay.
So now we have our paddle. So let's create two pals and let's draw them. So I'm going to go
here. And I'm going to say my left paddle is equal to a paddle and I need to pass this in x, y,
width and height. So for the x, I'm just going to make this 10, so be 10 pixels off the
left hand side border of the screen. Then we're going to pass a y, the y I want to be directly
in the middle of the screen. So we're going to say height, which is the height of our window.
We're going to integer divide this by two, just to get a whole number. And then we're going to
subtract this by whatever the height of our rectangles going to be or the height of our paddle.
So I'm going to make a variable up here. I'm going to say paddle. Yeah, paddle underscore height
and paddle underscore width. And this is going to be equal to just let me look at my screen here,
120. And I realized here that I probably want to go with first and then height second just to stay
consistent with width and height up there. So I'm going to say paddle width, paddle height,
and we're going to make this 20 and 100. So that's the values for our width and our height.
Okay. So we're going to use that now here. And we're going to say paddle height divided by two.
Now to explain to you why we want this, let's just quickly open up paint. And I can show you.
So let's zoom in a bit. Let's say this is our window. Okay. Now as I said, here is going to be
zero zero. Now I'm drawing with my mouse. So just excuse me, but let's write this zero zero. Now
our height is going to be all the way down here. So height is h. This will be h. Okay. So if our
height is something like I guess we have 500, then this coordinate right here would be zero comma
500. So the middle of this is going to be height over two, right? So they have h over two.
But the issue is if I start drawing my rectangle here, I'm going to draw it like this because the top
left hand corner is where I start drawing it from. So what I need to do is draw the top left hand
corner so that my rectangle will be perfectly center. So this will be the center of the rectangle.
Hopefully that makes a bit of sense. But to find this position here where we want to start drawing
the rectangle from, we need to know the height of the rectangle because if the height of the
rectangle is say 100, then what I'm going to do is take whatever the height of my window is divided
by two. And I'm going to subtract half of this height, which is going to be 50. And that will tell me
where I need to start drawing my rectangle so that it's perfectly center in the screen. Okay.
That hopefully, again, that makes a bit of sense. We're going to take the height over two.
We're going to subtract the height of our rectangle over two. And then that tells us the correct
height to draw a rectangle at such that it's perfectly in the middle of the screen. Okay. So let's
close this here and let's now implement that. So this is going to be height over two minus
paddle height over two. Okay. I guess we already have that. And then what we want for the width
in the height is just going to be the paddle width and the paddle height. Now let's copy this
because it's going to be very similar for our right paddle. So here I'm going to say right
paddle except all I'm going to change is where I want the x to be because this is going to be
different. And I'm going to say this is going to be the width. And then this is going to be
subtracted by 10, subtracted by. And then this is going to be the paddle width like that. Now
again, the reason for this is that I actually have to go in paint to explain this. If we want
our paddle to be say 10 pixels off the right border, okay, then what I need to do is make
the x coordinate be here, right? So let's say we want this to be 10 kind of a gap between the
right border. Then I need to account for the width of this rectangle as well as kind of the padding
I want between the right border. So what I'm doing is I'm taking whatever the width of the screen
is, which is going to be right here, okay? So this would be width and then zero, this position.
So I'm taking the width. I'm subtracting from the width this 10, which is the padding that I want.
And then I'm subtracting the width of the rectangle. And that's telling me the exact
x coordinate that I want to put this paddle at. And then the height is going to be the same thing.
That's why I've left it the same. Okay, hopefully that makes sense. That is what we have for
the right paddle. And now what I want to do is draw the two paddles on the screen. So to do
this, I'm going to pass to my draw function a list that contains both my paddles. So I'm going to
say a left paddle and a right paddle. Now I'm going to go to draw. I'm going to take in paddles
like that. And I'm going to use a for loop to draw both of the paddles. So I'm just going to say
for paddle and paddles, paddle dot draw. And then we'll pass to this the window. Now why am I
getting there here indentation? Okay, let's move that over by one. And now we're good. All right,
so the reason I'm doing a list is just because I'm going to do the same thing to draw both paddles.
So we can just do a for loop and draw both of them and maybe some some time down the future we
add a third paddle, a fourth paddle. Now we just pass in the list and it will draw all of them
for us. Okay, so let's now see if our paddles are showing up. And let's draw this. And we got an
issue. Pigeom dot draw has no attribute rectangle. Am I spelling rectangle incorrectly? Let's see,
pigeom dot draw dot rectangle. Um, hmm. Ah, my apologies guys. This needs to be wrecked. Not
rectangle. Uh, that's actually how you draw the rectangle is with rect not recting. Okay,
let's try this. Let's run. And there we go. Now we have our two paddles. They are perfectly
center in the screen and they're at the correct X coordinates. Now we want to move the paddles.
So let's figure out how we can do that. Okay, so to move the paddles, we need to change their Y
coordinate, right? Move it up and move it down. Now we need a velocity to move the paddles at.
So essentially how much should we go up or down when the user hits a specific key? So I'm going
to add a velocity here on my paddle and make this equal to four. Now notice anything that's going
to apply to all of my paddles, I'm putting as a class attribute, meaning I'm defining it here
rather than inside of the initialization or inside of a method. So I'm doing Bell equals four.
Now I'm going to implement a method on my paddle. And I'm going to say define move self. And I'm
going to say up is equal to true. Now what we're going to do here is call this method on the
paddle. And if I pass up equals true, we're going to move the paddle up. If I pass up equals
false, we're going to move the paddle down by the velocity. So I'm going to do here say self.y
and then this is going to be plus equals and then the self dot velocity. But that's only going
to be the case if up is equal to false. So I'm going to say if up, then we'll do something.
Otherwise do this. Let me just copy this and then I will explain what's going on here.
Okay, so if self.y, then minus equals self. So if we are going up and we want to move the paddle
up, then to move the paddle up, we need to subtract from the y coordinate, whatever the velocity
is, right? So we'll do that here. Otherwise, though, we need to move the paddle down. So we'll say
self.y plus equals whatever the velocity is. So as the y value increases, we're going down as
the y value decreases, we're going up. So let's now move the paddle up and down. Okay, so now that we
have this, we need to actually call this. We're only going to call this when we are pressing the up
or down arrow key. Now to do this, we're going to have to get the keys that the user is pressing
and then allow the paddle to move when they press a specific key. Now for this game, I'm making a
two player. So we're going to have W and S allowing the left paddle to move. And then we're going
to have the arrow keys allowing the right paddle to move. Now again, you could implement an AI for
Pong. Maybe we'll do a video where we'll implement AI for Pong. Let me know if you want a specific
video on that. But for now, we're just going to do it with two players. So I'm going to say that
keys is equal to pi game dot key dot get underscore pressed. And this is going to give us a list
containing all of the different keys that have been pressed. Specifically, it's actually going to
give us a believe a map. I'd see there a map or a list either way I'll show you how we can access
and check if a key was pressed. So I'm saying keys equals pi game dot key dot get underscore
press. Now what I want to do is make a separate function that will handle moving the paddles for
me because it's going to be a good amount of logic. And I don't want it to kind of clog up my main loop
so I'm going to call a function and this will be handle underscore paddle underscore movement
like that. We're going to need to pass to this the keys and we're also going to pass to it
the left paddle and the right paddles that we're able to move them. Okay, so let's make a function
now. Let's say define handle paddle movement. Let's take in our keys and let's take in our left
paddle and our right paddle. Okay, so inside of here, we're going to check if the user is
pressing the WRS key and move the left paddle and then the arrow keys and the right paddle.
So I'm going to say if keys and then this is going to be pi game dot and this is a capital K
underscore W. Notice this is a lowercase W. So if we are pressing the W key, then we want to move
the left paddle up. So we're going to say left paddle dot move up is equal to true. Okay, and then we'll
say if keys and this will be pi game dot K underscore S, then we want to do the same thing,
but we want to move it down. So left paddle dot move and then not down equals true, but this is
going to be up is equal to false. Okay, this is now handling the movement of our left paddle.
Now let's copy this and do the same for the right paddle except we're going to do this with
the arrow keys. So just a note here when you want to check a specific key, if it's a letter key
or yeah, I guess a letter key, then you're just going to do a lowercase of whatever the letter is.
So a Q W, whatever. If it's something like the shift key or the enter key, it's usually in all
upper cases. So for the arrow keys, we're going to say key underscore up or K underscore up and for
the down arrow key, it's going to be K underscore down in all caps. Now all we have to do here is
change this to be the right paddle and this one to be the right paddle as well. Okay, so now
this should actually be working, handling our paddle movement. So let's see if this is working by
running our code. Okay, so when I run my code now, I can move my paddles, but notice that my paddles
actually go off the screen. So now we need to implement something so we're not going to be able to
move the paddle off the screen. So to do that, I just need to check to see that if when we move the
paddle, it's going to go off the screen or not. If it's going to go off the screen when we move it,
then we're not going to let the user move it, right? So what I'm going to do here is say,
we will allow us to move the paddle if we are going up. So for hitting the W key and
the left paddle dot Y and this is going to be minus the left paddle dot velocity
is greater than or equal to zero. Okay, so if left paddle dot Y minus the velocity because that's
how much we're going to subtract from it when we move it is greater than or equal to zero,
which is the top of the screen, then we'll let it move. However, if it's not going to be greater
than or equal to zero, so it's going to go off the screen even by a slight amount, then we're
not going to let you move. Okay, now let's do the same thing down here, except we're going to have
to check if you're going to hit the bottom of the screen. So we're going to say and the left paddle
dot Y. This time we're going to add the velocity, right? So plus the left paddle dot bell,
we also need to add though the paddle height. So I'm going to say plus left paddle dot,
and this is going to be height like this. And we're going to check if this is going to be
less than or equal to the height of the screen. So the reason we need this is because the
Y that we're referencing is the top left hand corner of our paddle. So we're moving up,
it's fine to check that because that's the top of the paddle. But the bottom of the paddle is
the left paddle dot Y plus whatever the height of the paddle is, right? Because that's going to
give us where the bottom of the paddle actually is on the screen. If we didn't have this,
then what would happen is we'd be able to move it all the way down until it was just barely off
the screen because that's when the Y coordinate is at the very bottom. So we need to add the height
to make sure that we're not going to look like we're moving off the screen, right? And now we're
checking if it's less than or equal to the height, not greater than, right? Because if it is less
than the height, then that's fine. We can continue to move, otherwise it's off the screen.
Okay. So that's what we're doing. Now we're just going to check the exact same thing here.
I accept for the right paddle for these ones. So I'm just going to change this to be the right
paddle and the right paddle and then same thing with moving down. So let's copy this and put it here
and now let's go right paddle, right paddle and right paddle. Okay. Perfect. So now this should
make it so we cannot move off the screen because we're only going to move if we are not going
to move off the screen. Okay. So let's run this and see what we get and notice now that it stops
at the very bottom of the screen and stops at the top of the screen. Let's just check the opposite
and it is all good. Our paddles now do not move off the screen. Nice. Now that we've done that,
what I would like to do is draw a line in the middle of the screen just because I think that looks
nice. I want to draw a dashed line or a dotted line and I will show you how we do that. So let's
go inside of our draw function. Just need to reference my cheat sheet here because this is a
little bit complicated to do. So I'm going to say 4i in range. We're going to start at 10 pixels.
We're going to draw up to the height and the increment 4r4 loop is going to be height over 20.
Now the idea here is that I want to draw a bunch of rectangles to represent kind of a dashed
or dotted line. Now I want the space between the different rectangles to be the same and I want
the rectangle, I guess, height to be identical for every single rectangle. So what I want to do is
draw one rectangle, not draw rectangle, draw another rectangle, not draw rectangle and kind of have
a gap between each of them. So that's what this 4 loop is going to do and you'll see how I implement
that. So I'm going to say if i mod 2 is equal to 1, then continue. Now essentially what this means
is that if i is an even number, then I'm going to continue. So I'm going to skip this iteration
and I'm not going to draw a rectangle. Otherwise, though, I will draw a rectangle and the way I'm going
to draw it is the following. I'm going to say pi game dot draw dot rect. I'm going to draw this
on my window. The color is just going to be white so we'll draw it white and then the x is going
to be in the middle of the screen. So to do this in the middle of the screen, we're going to say
width over 2 but we need to subtract half of the width of the rectangle for the same reason we did
that with the height when we were initializing the y of the paddle. So now if we're looking at
it horizontally, right, if we want to draw it right in the middle of the screen, then the x coordinate
can't be directly in the middle. It needs to be whatever half the width of the rectangle is
to the left of the middle so that it looks like it's directly in the middle. So I'm going to say
width over 2 minus 5 because the width of my rectangle here, I'm going to make 10. Actually,
let's see if that's what we're doing. I think yeah, that's fine. Okay. Now for the y coordinate,
I'm going to make this i. Now the reason I'm making it eyes because I'm saying I want to have
20 rectangles on the screen, right? So when I say height over 20, that means that we're going to do
this for loop 20 times because we're going up to height and we're starting at 10. So we'll do
it either 20 or 19 times. Either way though, the reason I'm doing eyes because we're going to be
essentially picking what y value to draw at based on whatever the for loop is currently at. So
we're going to start at 10, then we're going to skip one, then we're going to be at whatever height
over 20 times 2 plus 10 is because that's how much we're incrementing the for loop by every single
time. And so this will make it so our rectangles are evenly spaced out because that's how much we're
incrementing the i by every single time. So I'm using i for my height. So width over 2 minus 5 i,
then the width of my rectangle is going to be 10. Notice this value here is half of the width,
right? And then the height of my rectangle is going to be height over 20 because that's how much I'm
incrementing the for loop by. So I need to make sure that's my height so that we're getting kind of
the correct spacing for our rectangles. I understand it's a bit complicated. This is how you do a dash
line. There is some other ways to do it, but they take up a lot more codes. So I'm going to do it
in this way. Okay. So let's just see if this is going to work now before we go any further.
Let's draw this and notice now we get a nice dashed line or dotted line through the middle of
the screen. The top and bottom spacing might be slightly off. But for me, this looks fine. Now you
could make this a solid line if you want. That would be a lot easier. But we're going to go with
dashed for now. Okay. So I think that is all good. Now what I want to do is implement the ball. So
we want a ball that's going to be moving on the screen. Now let's just go into paint here and
quickly discuss kind of some theory behind this because this is not trivial to do. So we want a
ball and this ball needs to move in two directions, right? It's going to be moving in the Y direction
and in the X direction. Now it's also going to collide with paddles. We'll talk about the collision
in a second. But for now we want the ball to move around the screen and to be able to collide with
something like the ceiling. Now to move it as I was saying, we need a velocity in the Y direction
and a velocity in the X direction. Now we'll start by moving the ball just in the X direction.
And based on where it hits the paddle, we'll change its Y velocity. We'll just understand that we
have kind of two components of movement in the X direction and in the Y direction. And we're going
to have to calculate what those velocities are and then move the ball by that velocity every single
frame. So hopefully that makes a little bit of sense. But that's kind of the idea here behind
the ball. And I just want to explain that because that's what I'm about to start coding out.
So just like we had a class for our paddle, I'm going to do one for the ball.
We don't necessarily need one, but it's just going to make things a little bit simpler.
So I'm going to say class ball. I'm going to say define underscore underscore init.
We're going to take in a self X Y and a radius. Okay, we're going to do a circular ball.
I know some pong games do a rectangular. I guess you can't really call ball, but rectangular
object moving around. We're going to go with a circular one. So for here, I'm going to say self.x
self dot Y equals Y self dot radius is going to be equal to radius. And then self dot X
underscore vell is going to be equal to vell. It's actually going to be equal to something called
max vell, which I will implement in a second. And we're going to say self dot Y underscore vell is
equal to zero. I'm going to implement my max velocity. So I'm going to say max velocity is equal
to and we'll go with five. Now, the idea here is that I want to initialize the X velocity as
whatever the maximum velocity is in the positive direction for a velocity, we can have positive
or negative. And that will change the direction that we're moving. Right. But I'm going to make
this positive, meaning it's going to be going actually to the right first, I believe. Yeah,
should be going to the right. Anyways, I'm initializing the X velocity as the maximum velocity,
meaning that we're going to be moving to the right at the maximum velocity when the program starts.
Then once it hits the paddle, we will simply reverse the X velocity and calculate the Y
velocity based on where it hit the paddle at. But the maximum velocity is sung us with the
max one possible velocity is in either direction. So we'll always be moving at the max
velocity in the X direction. But the Y direction velocity is going to change depending on where
we hit the paddle because we're going to have to change the angle at which we want to move the
ball. Okay, again, I noticed getting a little bit confusing, but hopefully that makes a tiny
bit of sense. Now we're going to implement a method called draw. So very similar to our paddles.
We're just going to say self win and we're going to go in here and say this will be pigame
dot draw dot and then circle. We're going to pass our window. We're going to have a color
which will define up here as just white. Again, just doing this so we can very easily change the
the color and then we want a radius. So we're going to pass the window drawn, the color,
the radius and the X and Y position. Now I'm actually not sure if this is correct.
I think we have to do the X and Y before we do the radius. So we're going to do self dot X
self dot Y. Make sure that's in a tuple and then the radius comes after and I think that is
correct. Perfect. Okay, so now we have draw. We have initialization and let's implement move while
we're at it because this is pretty straightforward. So we're going to say define move. And all
we do to move is we move the X by the X velocity. So self dot underscore X self dot X underscore
velocity and then self dot Y plus equals the self dot Y underscore velocity. And so that's how
we move the ball. This is our draw. I think that is all good for now. And the reason we can do
plus equals is because if the velocity is negative, then that will be equal to minus equals, right?
It'll just move it in the other direction. Okay, so now we have our ball. We need to initialize
our ball. So let's go in where we have our left and right paddle and let's make the ball and say
the ball is equal to ball. Now what's nice about the ball is that the X and Y position forward is
the center of the ball, meaning that if we want to put it in the center of the screen, we can just
actually calculate the direct middle and then place it there rather than having to do kind of
the calculations here we did by subtracting, you know, half the width and that type of stuff.
So for the X and Y position of my ball, I'm going to put it directly in the middle of the
screen. So I'm going to say width over two. And then I'm going to say height over over two.
Now the reason I'm doing two division signs here is because this is integer division. It just
going to give me the rounded division because I can't draw at a floating point position. All right,
so we're going to go height over over two. And then lastly here for the radius, we're going to
make a variable. We're going to call this ball underscore radius. And for now we'll go with a
radius of something like seven. And we can of course change that later on if we want. So let's
now pass the ball underscore radius. Now we want to draw the ball. So we're going to have to pass
that to our draw function. So we'll pass the ball. We're going to take in the ball here in draw.
And it's very easy to draw this because of the method we have. We're just going to say ball
dot draw and draw it on our window. Great. So now we have our ball. This is the class. And I think
that's actually all we need for right now. So let's run the program and see what we're getting.
And we got an issue. Let's see what the errors says. Max Bell is not defined. My apologies.
We need to add a self dot max bell here rather than just max bell because I have to reference it
from the class. Okay, let's try this now. And we got another issue says ball object has no
attribute max bell. Ah, we need an actual L here or an uppercase L rather than a lowercase one.
Let's see if we get any other errors. Okay, there we go. So the balls in the middle of the screen
is not moving right now because we're not calling the move function or the move method story.
But once we call that, we'll see that the ball actually starts moving on the screen. Then we
need to handle collision. So let's move the ball to do that. We're going to go to under actually
where do I want to move the ball? Yeah, it's going to do it right here. I'm just going to call ball dot
move inside of my main loop here. And now we're going to be moving the ball every frame by whatever
it's velocity is. Okay, so let's run this now. And let's see and notice the ball is moving to
the right. Of course, it's going to go off the screen because we're not handling any collision.
Let's run it one more time in case you missed it. You can see the ball starts moving to the
right. Now if we change the vibe, why velocity would move on an angle, but for now we're just moving
it in the x direction. So that's what's happening. Okay, so now that we've done that, we need to
start handling collision. Now I will admit this is not the easiest thing in the world to do,
but of course, I'm going to try my best to explain it to you. And you will have, you know,
collision handled by the time of this is done. So let's go into paint again and let's just talk
about how we're going to handle collision. First of all, with like the ceiling and then we'll talk
about the panels. So we have this right here. Okay, now let's say we have a ball and let's say it
trajectory is here. Okay, so it's kind of moving up in this direction and this direction
and the average of the components makes it's the angle it's moving at is here. So if the ball hits
here and it's going in this direction with the y, all we actually need to do to make it bounce
off of the wall is we need to kind of bounce it off on the exact same angle that it hit the wall
out, right? So if you have like a wall like this and you hit like this, you go here, right? Pretty
straightforward. If you hit coming pretty straight on, you're going to come out going pretty straight
on as well. If you head out of huge angle, you're going to come off at a huge angle. So that's
how the collision is going to work works either in the X or Y direction. Now when we're talking
about the ceiling, all we actually need to do to implement this is we just need to change the
direction of the Y velocity. So here, this Y velocity would actually be negative because we're going
upwards. So to make it bounce off, all we do is swap whatever the current Y velocity is to the
positive direction and then that moves us down. Hopefully that makes sense. That's all you need
to do to actually handle collision with the ceiling, which we'll do shortly. Now that's the easy part.
Collision with the paddle is a little bit more difficult because we're not going to do this in kind
of a physics real way. So let's say we have our two paddles here. Now in Pong, the way that the
collision works, at least my understanding of the way that the collision works is you're going to
bounce the ball off of the paddle based on where the ball hits the paddle, not based on the angle
that is coming in it. Now if the ball was coming at an angle like this, if we're talking about
real physics, we should just bounce it off at the exact same angle. Now the reason why we don't
want to implement it like that is because that means our games are always going to be the exact same
assuming you can line up the paddle. You never can change the direction of the ball. Whatever direction
it hits the paddle out is the direction it's going to come off at. So we need a way to actually
manipulate that so we can have a game that's playable. So the way that I'm going to do this is I'm
going to figure out how to move the paddle based on its direction from the center of the paddle.
Or sorry, not move the paddle, how to move the ball based on its kind of displacement or distance
from the center of the path. So if the ball hits here, then I want to bounce it off on an angle
like this. Okay. If the ball hits here, then I want to bounce off on an angle like this.
If the ball hits in the direct center of the paddle, then I want to bounce it like that.
So the further away from the center it is, the higher the angle is I'm going to bounce it off on.
So we're going to have something along the lines of this and we'll calculate this using a
custom function that'll show you how to write. But that's the idea here with the ball.
So that's going to be more complicated to do. But when the ball hits like here,
we're going to bounce it off pretty much in the center because this is almost directly in the
middle, right? So we'll bounce it at tiny bit higher than that. But that's kind of how we're going
to do the collision with the paddles. So let's say we have a paddle here. Okay. This is the middle
of our paddle. This is going to be the x, y coordinate. And then we have a ball. And this is the center
of the ball. So let's just go through some of the math here. So we first need to figure out where
the middle of this paddle is. So we can calculate the displacement between this and this.
Okay. So to do that, we're going to take the y coordinate, which is right here. So let's
just call this y one. Let's put a y one here. And we're going to add to this half of the height of
the paddle. So h over two. Okay. So we're going to add h over two. Again, I'm using my mouse here.
This is pretty hard, but this is what's going to give us the middle. So we can say this here is
equal to M. Okay. So this is M the middle. Now, once we have M, all we're going to do is subtract
M from the y coordinate of our ball. So we can call this y two. Okay. So we're going to take M
and we're going to subtract y two. Now, if this gives us a negative value, that means we are above
M. If it gives us a positive value, that means we are below it. So once we have the displacement,
we can call this d, we need to figure out what the velocity should be in the y direction based
on this displacement. That's where it gets a little bit more challenging. And I'm going to dive
into that math in a second. But for now, we understand how to calculate the displacement.
Once we know the displacement, we just need to make it so that when you're at the maximum
possible displacement in the, you know, up direction or the down direction, you're going to move at
the maximum possible velocity. Whereas when you're at say a zero displacement, you move at a zero
velocity in the y direction. Okay. Hopefully I'm not confusing you guys too much. I just wanted to
give a quick rundown because now we're going to implement the collision, which as I've said is fairly
complicated. So let's do a function here and we're going to say handle underscore collision. Now
to handle the collision, we need the ball, the left paddle and the right paddle. Okay. Now what's
nice is that we only need to adjust the y velocity of the ball really. That's the only thing
we're going to do here, but we need to handle it on the left paddle, right paddle and the seal.
So let's start with the ceiling because that's the easiest. So we're going to say if the ball dot x
are actually not x, we only care about the y because we're going up and down. We don't care about
the left, right collision right now. So we're going to say if the ball dot y plus the ball dot
radius is greater than or equal to the height of the window, then we're just going to say ball dot
y underscore val multiply equal by negative one. So simply going to reverse the direction.
Now we're going to say L if the ball dot y plus and it's actually minus sorry minus the ball
dot radius is less than or equal to zero, then ball dot y underscore val. This is going to be
multiplied equal by negative one. Now that's actually all we need to handle the collision with
the seal. So let's just look at this one here. This is saying if we're going to hit the bottom
of the ceiling, right? So the height, we're checking the ball dot y, which is the center of the
ball plus the ball dot radius. It's important you add the radius. If you don't do that, it's only
going to check for collision with the center of the ball. We don't want that. We want it with like
the edge of the ball, which is the radius, right? So if that's the case, then we just what is it
change the direction, reverse the direction. Now we do the exact same thing in the other direction,
except this time we need to subtract the ball dot radius, not at it because we're checking up,
not down. Okay. There you go. We're now handling collision with the ceilings. Now after we
check that, we need to check if you're hitting the left panel or the right panel. Let's start with
the left panel. So we're going to first check the direction of the ball because we're only going
to see if we're hitting the left panel. If we're moving left, right? So if the x velocity is negative,
that means we're moving left, then we'll check if we're colliding with the left panel. Otherwise,
there's really no point in doing that. So I'm going to say if the ball dot and it's going to be
x underscore velocity is less than zero, then I'm going to check if we're colliding with the
left panel. Otherwise, I'm going to check the right paddle. So I'm just going to add a comment.
So to check if the ball is colliding with the paddle, we need to check with the x and y coordinate
and check if the y coordinate is within the range of where the paddle is on the screen.
So essentially, if the y value of the ball is greater than the y value of the paddle,
but less than the y value of the paddle, plus the height of the paddle,
that's going to tell us if it's where the paddle is on the screen, essentially.
Then once we check that, so if the y coordinates are correct, if it's in the right range,
we need to check if the x coordinate is the same as the edge of the paddle. That's essentially
what we want to look at. So I'm going to say if the ball dot y, we're going to say is greater
than or equal to the paddle dot y, because remember, this is the top left hand corner of the
paddle. And the ball dot y is this is going to be less than or equal to the panel dot y plus
the panel dot height. So that's the first thing that we want to check. Now after we check that,
we want to check if the x coordinates are correct. Now I could do this on the same line,
but just so it doesn't get too messy, I'm going to do another if statement. And I'm going to say
if the ball dot x, and this is going to be, I need to think about this for a second, minus the
ball dot radius. And we're going to check if this is less than or equal to the paddle dot x plus
the paddle dot width. Okay. Let me check my cheat sheet to make sure I did this correct. I think
I did looks good to me. Okay. So the reason we're doing this, let's go to paint quickly,
is because we're checking the left paddle. So if this is the paddle right here, we know the top
left hand corner, x, y is here. And we know the edge of the paddle in terms of the x coordinate is
going to be the x plus whatever the width is. So that's what we want to check. Now we have our ball.
This is the center of the ball. So we need to account for the radius, which is what we're doing.
So we're taking the ball dot x, we're subtracting the radius, which gives us the edge of the ball.
And we're checking if that edge is less than or equal to this edge right here. If it is,
we're just going to change the direction of the x, and that will then change how we move the
ball off of the paddle. Okay. So let's try this out. Let's go here. And for now, all we're going
to do is change the x velocity. We'll deal with the Y velocity later. So I'm going to say ball dot
x underscore. Well, multiplied equal by negative. Okay. So it's going to change the direction.
So now we move from the left to the right. Okay. So that is handling the collision for the
left paddle. Now handling for the right paddles a little bit different. So we're going to say,
if the ball dot Y, we actually can copy this for the Y. So we'll do this, except we're going to check
paddle. I keep saying paddle. Sorry, this needs to be the left underscore paddle. This needs to
be the left underscore paddle. This needs to be the left underscore paddle. My apologies, guys,
fix that. So this will be left underscore paddle. And then for here, this makes it actually
easier. We just make this right paddle. Okay. So right paddle and right paddle. Okay. So now
we've checked the Y for the right paddle. Now we need to check the X. The X is a little bit different.
We're going to say ball dot X plus ball dot radius. And we're going to check if this is greater
than or equal to the right paddle dot X. Okay. And if it is, we're going to say ball dot X
underscore. Well, multiplied equal by negative one. Okay. So the reason why this is a little bit
different for the right paddle is because when we're moving to the right, we're going to be
checking the left edge of the right panel. And that is represented simply by the X coordinate of
the right paddle because that's the top left hand corner. Whereas when we're moving to the left,
we're checking the right edge of the left paddle, which means we had to add the width. Now,
the reason we're adding the radius is because we're moving to the right. So the radius is going
to be to the right side that we're checking rather than the left, whereas here we were subtracting
the radius. Hopefully that makes a little bit of sense. But I think this is going to be good to
allow us to move the ball left and right on the paddles. Let's check this out. And then we'll
deal with the Y, which is a bit harder. Okay. So let's see this now. It should bounce off the paddles.
Okay. So it didn't bounce off the paddle, which means I made a mistake here. Let me check the
mistake and I'll be right back. All right. So I found the air. This is kind of a silly one. I'm not
calling this function. So I read this wrote this function, but I didn't actually call it. So of
course, the collision is not going to be handled. So we need to call this now. So after we move the
ball, then we'll handle the collision. So we'll say handle. And then this will be collision like
that. And we're going to pass ball, left panel and right panel. Okay, figures crossed. Hopefully
this should work now. Let's run this and let's see if it bounces off. There we go. So it bounces
off. And notice that it doesn't really matter where I have the paddle. It's just going to bounce
off at the same direction. Because that's all we're doing right now. Now if I move the paddle,
it should just go off the screen and it does because while it's not going to collide with the
wall, we haven't implemented that. Great. So there we go. That is now dealing with the collision.
Now we want to deal with the collision in the Y direction. Again, a little bit more complicated.
We need to calculate the displacement between the ball and the center of the paddle and then
determine the angle that we want to bounce it off on. Now we don't actually need to use trigonometry
because of the way that I've implemented this, but you're going to see the math is not not super
straightforward. So the first thing we need to do is calculate the middle Y of the panel. So we're
going to say middle underscore Y is equal to. And then this is going to be the, in this case,
it's the left panel. So the left paddle dot Y. And then this will be plus the left paddle dot
height divided by two. Okay. That's fine. Now the next thing we want to do is calculate the
difference in Y between the ball and the middle of the Y panel or the middle of the left
paddle. So I'm going to say difference in Y is equal to. And this will be the middle Y subtracted
the ball dot Y. Now that we've done that, we need to figure out essentially how much we need to
divide this value by to determine what the velocity should be. So let's go back to paint here and
let's deal with some more math. So we have a max velocity. So let's call this V. Okay. The max velocity
is equal to, I believe we had five. Okay, that was a rough five. That's better five. So I just
call this mx for max velocity is equal to five. Now what we need to do is make it. So when we're at
the maximum possible displacement, that's when we get this maximum velocity, right? So if we're
hitting the very, very edge of the paddle, so we hit right here, that's when we get a maximum velocity
of five. Now when we don't hit this, we want it to be a little bit less, right? So I want the velocity
to maybe only before when we hit here. So essentially, how do we figure this out? Well, what we need
to do is figure out what value we need to divide whatever the distance is to make it. So the
maximum possible value we can get is five. And then any other value we get will be smaller than five.
I understand this is a bit weird, but we want it so that the distance or the displacement
divided by some value gives us five when we're at the maximum possible displacement value.
And then when we're not at the maximum, it gives us something a little bit less than five.
And as we get closer to the middle, it gives us a smaller value that should reach zero.
So our range is zero to five. We need to take our displacement and then squeeze it in the values
zero to five. Hopefully this following a little bit, again, not the easiest math in the world,
but we're going to have a maximum velocity of five. Okay, that's our MV. And our maximum
displacement, okay, is going to be equal to half of the panel. So if we're taking the displacement
from right here, then the maximum possible displacement we can have is that the center of the
ball is right here at the very edge of the paddle, which is just going to be half the height of
the panel. Okay, so our maximum displacement is height of the paddle divided by two. So we have
these two values. And now we need to figure out what I'm going to call a reduction factor. And
the reduction factor is how much we take the displacement between the paddle and the what he
call it and the ball and divide it by. Okay, so now we also have D. So let's write a variable D.
Let's go back to this. No, I don't want to pencil. I want brush. Okay, D. So we have D
this equal to the different distance between the middle of the paddle and the the ball.
So the way that we figure this out is we say that our reduction factor, which I'm going to call R.
So we want to have D over R is equal to MV when specifically D is equal to MD. So when the
displacement is equal to the maximum possible displacement that we can have and we divide it by R,
we want to get the maximum velocity. That's what I'm saying here. Now it doesn't matter if
it's positive or negative because we'll get either the maximum positive or maximum negative velocity.
Anyways, that's kind of the equation that we have. Now fortunately, we can solve for R because we
have a value for D and MV. Now once we solve for R, we just use R as the reduction factor. And
then that's going to give us what we need for the velocity for our ball. So if we plug in D,
which is going to be MD, which is just H over two, then we have H over two over R is equal to MV
and we know MV, which is five. Now H over two is going to be variable based on the height of the
paddle, but I think we actually know this value is 50. Anyways, I'll show you how we solved just using
the variables. So if we have H over two over R is equal to five and we're looking for R,
then what we need to do to actually solve this is we need to do the phone. We're going to have to
flip R and H over two. So we're going to say R over H over two is equal to one over five. Okay,
because what we do to one side we need to do the other side, then we're going to take H over two
and we're going to multiply it by the one right here. So we're going to have H over two over five
is equal to R. Okay, so it's going to be whatever the distances, the maximum distance over five.
In this case, it would be just be our max velocity so that could potentially change
is equal to R and then we take in the future, whatever the displacement is,
divided by R and that gives us the value. I know that was a lot. I'm sure it's not 100% clear.
Once I write this, maybe it'll make a bit more sense, but I just want to go in and try to explain
how that works. Okay, so now I'm going to calculate the reduction factor. So I'm going to say
reduction factor is equal to this is going to be left paddle dot height divided by two. Okay,
divided by two. And then we're going to take all of this and we're going to divide this by
the maximum velocity of the ball. So I'm going to say ball dot max underscore vell like that.
Okay, now we have the reduction factor. Now that we have this, we need to use this to calculate
the Y velocity. So I'm going to say the Y velocity is equal to the difference in Y divided by the
reduction factor. Okay, so now this is going to squeeze our difference in Y within the range of
negative 525, which is now going to allow us to update the Y velocity. So now I can say ball,
the Y value is equal to the Y value. Okay, now it turns out that we do the exact same thing here
with a few minor changes inside of this function. We just need to change the left paddle to be
right paddle and it will give us the exact same result. Now there's there's a lot of repetitive
code here. Theoretically, we could maybe write a function to calculate this for us. But for now,
I'm just going to put it inside of here, because I think this is fine. So let's run this now and see
if it's working. So if I look at this paddle here, it's actually bouncing in the incorrect
direction. Okay, I need to fix that. Yeah, same with the other paddle. So it's giving us the
reverse of what it should be. So I'll show you how we can fix that. But it is bouncing on an angle,
which is what we want. So the reason it's giving us the reverse of what we want is essentially
because we just need to multiply the Y velocity by negative 1. I'm going to say negative 1 by Y
velocity and negative 1 multiplied by Y velocity. How did I mess that up twice? Okay, let's
rewrite this negative 1 multiplied by Y velocity. Now it should give us in the other direction.
You can look at the math and you'll probably figure out pretty quickly why it's giving it to us.
But it's because we're taking the middle line subtracting from ball Y. If we do ball Y and
subtract by middle Y, then we don't need to do this. But I think this makes more sense. So that's
what I'm doing it this way. Anyways, let's try this. Let's see what we get. Okay, now it's bouncing
in the correct direction. Nice. That's what we wanted. Notice when it hits the edge of the paddle,
it's going to bounce more down. Okay, there we go. We got it to bounce down. Nice. Awesome. And it
bounced off the ceiling completely fine. So this is working as I anticipated. Now what I want to do
is I want to make it so when it goes up the screen, we actually increment some type of score
and display them. Okay, so let's get started on that. Our collision is working properly.
As you notice, the closer we got to the center, the more the ball bounced off straight
and the further away from the center, the more we had an angle, that's why I was doing all this math.
That's kind of how that works. Okay. So now we want to implement some type of score. So to do that,
we need two variables. We're going to say left score and right score. So the left score will be
equal to zero. And the right score will be equal to zero as well. Now to check the score,
we just need to see if the ball moves off the left or the right hand side of the screen.
So I'm going to say if the ball dot X is less than zero, then this means that the right
player scored because it came off the left hand side of the screen. So we're going to say right
on a score score plus equals one. L if the ball dot X is greater than and this will be
the width of the screen. And then we are going to say left score plus equals one.
Now that we have both the scores, we can pass those to the draw function. So I'm going to say left
score like this and right score. And then if I go to my draw function, I can now take it in the
scores. So we'll say left score, right score. And we want to draw them on the screen. Now to draw
them on the screen, I need something like a font, right? I need to actually draw a text on the screen.
So to do that, I need to write a font. So to make a font, you can do the following score,
underscore font is equal to pi game dot font dot s y s fonts like that. Now this takes in the type of
font, which I always just use as comic sands. And then the size of the font, which I'm going to go
feel free to modify these values, but this is the font you want. And this is the size of the font.
And I use this font object to actually render text that you put onto the screen. So inside of my
draw function here, I'm going to use score font. And I'm going to draw this at the top just so that
the ball can be drawn over top of the score. So whatever I draw first will be drawn first, meaning
that whatever I draw last is going to be on the very top. So I want to draw the ball last,
it will be over top of my score. Okay. So the way that I do this is I say the left score text is
equal to the score font. And then I call dot render. And I pass to this the text that I want to
render, which is going to be an f string containing the left score. Okay. And then I'm going to
pass one. And I'm going to pass the color, which is just going to be white. Now one stands for
anti aliasing, just always make this one. Okay. So you do the text anti aliasing, the color,
and that's what we have. Now f strings allow you to embed expressions directly inside of a string.
So I'm just doing the score here directly inside of the string to avoid having to convert it to
a string using the string function. Now for the right score, it's going to be the exact same thing
except right score. Okay. So now this actually gives me a drawable object. Now that I have a
drawable object, I want to draw it on the screen. So I'm going to say wind up blit. And I'm going to
to blit the left score text and then I need to pass an x and y location for it. Now I want the
left score text to be exactly in the middle of the screen on the left half of the screen.
So this is actually fairly easy to do, but I'm going to say width divided by four,
not divided by two because I'm trying to put this on kind of the first quarter of the screen
or the first half of the screen in the center. So I'm going to say width over four. And I'm going
to subtract this from the left score text dot get underscore width over two. Okay. And then for
the height, I'll just pick a height of like 20 pixels. Now we'll do the exact same thing for
the right text and then I'll explain why I'm doing this calculation. So this is going to be
right score. But for the right score, I want this to be in the middle of the right hand side of
the screen. So I actually want it to be three quarters of the width of the screen. Right. That's
the middle of the right hand side of the screen. And then I'm going to subtract that from the
right score tax dot get width over two. So for this, I'm going to do width. And I'm going to multiply
this by three over four. Okay. So hopefully that makes sense. I think I can actually just do it
like this and say width multiplied by three over four. And then minus the right square to get
underscore width over two, that should work properly. So to go to paint to quickly explain this again,
if we want this to be in like, let's do this, we have half the screen, the middle of this half
is right here. Okay. So this is width over four. That gives me this X position. Then I have my text.
Now if I draw my text here, it's going to go like that. I don't want it to be like that. I want
it to be directly in the middle. So I need to draw it like this. So I'm going to take half the width
of the text and subtract that from this position. That gives me the top left hand corner for where I
I need to draw. Now exact same thing over here. This is three over four width, right? So I take
that, subtract it from half of the width of the text I'm drawing. And I get it directly in the
center of the screen. Okay. And of course, this is how you get the width of the text object you
used to get with. Perfect. So that should now draw my score. So let's see if this is going to work.
Okay. Wait. I think I'm in the Python terminal here. Let's just quit that. Yes. Okay. Let's
rerun and notice now that if we score, this goes up and it's going to continue going up because
I don't reset the ball. So I need to reset the ball. You can see that is in the drawing in the middle
of the screen. And it's changing based on what the number is, right? Okay. So how do we do this now?
So once the ball goes off the screen, we need to reset the ball. So I'm going to put a method
on my ball. It's called reset. So I'm going to say define reset. And what I need to do in here is
I need to reset the Y velocity as well as the X velocity and then change the X and Y position to
be originally what it was set. So I'm going to add some new values here. I'm going to say this is
equal to self dot original underscore X. And this is equal to self dot original underscore Y.
Now let's make those lower cases. So the idea here is when I initialize my ball, I'm going
to store what the original X and original Y value was in a separate variable. So that's how you do
this here. And then I'll change the X and Y as I go through the program, but I'm storing the
original X and the original Y. So I can reset the X and Y to be equal to the original X and
original Y when I reset. So I'm going to say self dot X is equal to self dot original X. Okay.
And then we're going to say self dot Y is equal to self dot original Y. Then I'm going to reset
the Y velocity to be zero. Okay. And I'm going to say the self dot X velocity multiplied equal by
negative one. Now the reason I'm doing this for the X velocity is if I go off the screen going left,
then I want the velocity of the ball to be hitting my opponent when the ball resets. Okay.
Now same the other way around. If I go off the screen going right, then I want the ball to go
left and hit my opponent's paddle first. You could change this if you want by just making it
equal to the max belt or not even changing the X. Well, you could do that. That'd be fine.
But I'm just going to reverse the direction. So if it just went off your screen, then it will go
to the other person. Hopefully that makes a little bit of sense. That's what I'm doing with
the X velocity here. Okay. So that's reset for the ball. So now if they score one or reset,
so I'm going to say ball dot reset and ball dot reset. Okay. Easy enough. Let's try this
and see what happens. Okay. So let's score. And now notice it comes off and hits my opponent's
paddle. So that's what I'm trying to do just to give my other player some time to recover.
So if we score, now it comes off on that side. Okay. Let's score on the other side and see if
our score goes up. Okay. Score goes up and then it comes back to me. All right. Now again,
you can change that if you want, but that was kind of my idea. Now one thing we could do is also
reset the paddles. When we score, that might not be a bad idea. For now, I'm not going to do that,
but I think I've showed you how you would go about doing that. Just implement the same logic on the
paddles and then call reset on left paddle and right paddle. Okay. Nice. Now that we have that,
the last thing I need to do is handle winning the game. So if the left player wins, I want to
say, you know, left player wins. If the right player wins, I want to say right player wins. So
I need to first come up with a score that I want to end the game at. So I'm going to say the winning
underscore score is equal to 10. And now we want to check if either player has a score of 10
after we increment the score. So I'm going to go here. And I'm going to say if the left score is
greater than or equal to the winning score, then we want to do something. And I'll say,
L if the right score is greater than or equal to the winning score, then we want to do something.
Now, the first thing that we want to do is we want to say ball dot reset. Okay. And we want to
say ball dot reset here as well. Now, I actually want to reset the paddles of someone wins because
we're just going to play a new game. So let's actually implement the reset method on the paddles because
I guess we're going to need that anyways. So let's go to our paddles. And in the same way that we
did this for our ball, we're going to say self dot original X and self dot original Y. And then
we'll just implement reset. So define reset self self dot X is equal to self dot original X and
self dot Y is equal to self dot original Y. Okay, paddle reset done very easy. So if one player wins,
I want to say left underscore paddle dot reset, right underscore paddle dot reset,
I and say nice, left paddle reset writes paddle reset. Now, the thing is this is pretty
repetitive. So we could try to wrap this in a way where we're only going to write this code once.
In fact, let's actually do something. I'm going to say reset. Actually, I'm going to say one
is equal to false. And then inside of here, I'm going to say one equals true. Okay. And one equals
true. And then I'm going to say if one do this. So this is just handling all our resets in one
place now. So we just have a Boolean telling us, okay, did we win or do we not? Well, if someone won,
then we'll do the reset. And then inside of the left score and right score, if statement,
we'll handle specifically what to do with the left player one and specifically what to do
if the right player one. Now, actually what I want to do is put some text on the screen,
show it for five seconds saying, Hey, left player one, right player one. And then just immediately
reset the game. So to do that, I'm just going to render some fonts. I'm going to say win text
is equal to an actually, yeah, we could just do win text. We could say left player one,
exclamation point, we can say win underscore text is equal to right player one. So now that
we have this in a variable, what I can do inside of one is I can use that variable to render
text depending on who actually want. So I can say my win underscore. Yeah, I'll say win. Actually,
let's just say text is equal to. And then this is going to be the score font. We could make a new
font. I'm just going to use the score font dot render. We're going to render the win text one.
And then we're going to render this in white. And we're going to blip this to the middle of the
screen. So I'm going to say win in all capitals. Dopplet. And I'm going to blip the text. And then
I'm going to blip this at when are I'm going to blip this at height. Nope at width over two minus
the text. Get underscore width over two. And it's going to be at height over two minus. And then
this is going to be the text dot get height over two. Okay. So we're centering in the X position
and the Y position. Then I need to update the display. I'm going to say pi game dot display dot
update. And I'm going to say pi game dot time dot delay 5000 and 5000 is going to be five seconds.
This is the number of milliseconds that I want to display the program. But let me run through
this because I know I want quickly. We're saying the text is equal to score font dot render. So
we're just rendering a drawable object. The text we're rendering is whatever we put here. Just so
we're not repeating a bunch of code and doing this in both places. Right. I'm rendering this with
anti-aliasing one, the color white. I'm then blidding this directly in the middle of the screen.
I've talked about how we do the middle calculation many times. So I won't go through this again.
We're going to update the display so that it instantly shows. Then we're going to delay by five
seconds. We delay by five seconds. Then we reset everything. Now when we reset, we also need to reset
the score. Right. So we're going to say left score equals zero and right score equals zero.
And I think that's actually all we need to do for reset. Then the game will restart and everything
will just work as expected. Then when you hit X, of course, the game is going to end. All right.
So I think that's actually all good. That should really wrap up the program. Let's run this though
and give it a test and see if it's working. So let's just see if we can score 10. So all this
kind of left this run. And then once it's done, I'll be right back and confirm that it's working.
Okay. So we're at a score of nine. Let's see now if this is going to work. Okay. It says
right player one. Perfect. And then it should restart the program. Let's see. Perfect.
Research. Awesome. Okay. So that is punk. We have finished the game now. As I promised,
hopefully you learned a lot in this video. I just covered collision. We covered some more advanced
math drawing, you know, kind of separating the program out and how we have different functions
handling different stuff. Yeah. I think this is a fun program. Again, great one for beginner
or intermediate programmers. And I am going to end the video here. I will end off by saying
that I do have a course programming expert.io can check it out from the link in the description.
Use discount code Tim. This course is designed for beginner or intermediate programmers looking
to get better at programming. This teaches programming fundamentals, object oriented programming,
advanced programming, software engineering tools, all kinds of great stuff,
check it out from the link in the description. It's definitely a great resource, especially
if you guys want to support me. With that said, I will end the video here. I hope you guys enjoyed.
If you did make sure to leave a like, subscribe to the channel and I will see you in another one.
Introduction to Pong Tutorial
Demo of the Pong Game
Setting Up the Environment
Installing the PyGame Module
Creating the Main Loop
Creating the Paddles
Centering the Paddles
Implementing Paddle Movement
Handling Paddle Movement Logic
Drawing a Dashed Line
Understanding Ball Velocity
Creating a Ball Class
Implementing the Ball's Movement
Handling Collision Mechanics
Implementing Ball Collision
Calculating Paddle Collision Angle
Adding Score Keeping Mechanism
Resetting the Ball Position
Handling Game Winning Logic
Implementing Paddle Reset Logic
Displaying Winning Player Message
Resetting Game Conditions
How do you install PyGame using pip commands?
What is the purpose of the main loop in a game?
How do you fill the window color in PyGame?
Why use classes for paddles in our Pong game?
How do we center the paddles on the screen correctly?
What logic prevents paddles from moving off-screen?
How to draw a dashed line in a PyGame window?
How do we define the ball's Y velocity?
What method ensures the ball moves correctly?
How is collision detection handled with paddles?
How do we correctly handle ball and paddle collisions?
What calculations determine the bounce angle off the paddle?
How to implement a scoring system in the Pong game?
How is the winning condition checked in the game?
What does the paddle reset function do during the game?
How to render the winning player's message on the screen?
In this video, we will show you how to create the popular arcade game Pong in Python using the PyGame module. We'll start with a quick demo and then dive into writing code. You don't need to know PyGame ahead of time, as we'll take it slow and cover the basics. Our goal is to make this tutorial accessible to both beginners and intermediate programmers. By the end, you'll have learned a lot about programming fundamentals and how to apply them to create a fun game like Pong.