= "Easy" DIFFICULTY_LEVEL
Mindset and Introduction to facilitate understanding Pygame is a python library used to code light games, hence can also be used for a variety things like animation or just to write a love letter to your girlfriend(I did).
Who is this guide for? My Walter-White looking physics teacher(Dr Andy Richter) and anyone with some(even if it is almost none) programming background interested in games or light animations. If you are really blank in progamming, then read the first five chapters of *Automate the boring stuff with Python by Al Sweigart
Expected time investment: 1 movie time should be enough for the average person to complete this guide. So you learn cool stuff while your mates watch a stupid movie called Avatar 2, and at the end of the day, you're better person.
**Note: If you are very versed in programming, it is great. However, do not skip certain sections unless I indicate that it is skipable, because I refer to the sections later. And the guide is very short, so reading what you may know may only take 10 more minutes, but would help you finish the guide faster. So it is a actually net gain.
What you will be able to do at the end of this guide: Coding light games like Atari Pong Game, shooting games, animating objects like a ball falling, including buttons for pause, resume, play etc. All that in 1 movie time. I will personally guide you through animating a falling ball. This example will give you enough understanding of how things work so you can use your superpower for other things.
The Pygame Programmer mindset(must read): If you're a new to programming, do not worry. There is not much you need to know. You've probably watch animes or movies where someone finds a magic box and a genie who can grant 3 wishes pops out. But what if the genie was from a random African country like Cameroon and only spoke 'Dschang', then you won't be able to communicate your wishes. Well, most progammers are just people who can speak to the genie. They use tools like programming languages to give step-by-step instructions, also called algorithm just to make it sound complex, so the genie grant their wishes. So yeah, progammers don't really do anything except asking for wishes in special languages (Python being one of them). (skipable for 'experienced' programmers):
Windows user:
Now, if you're using windows, press left Ctrl + R, then type 'cmd' and press Enter. A dark weird
window will pop up.
Mac user:
Press command + space, then search 'Terminal.' Click on the black icon and a dark weird window
will pop up.
This window is called Terminal or Command line. Type(Do not press enter!) "FBI-#9222077438_{bld" You just wrote your first program of the hour: you asked the computer(genie) to open a window(the terminal). Then you typed a code to inform the FBI that a scammer is the owner of the computer. So if you pressed enter, though it will be written ' "FBI-#9222077438_{bld" is not recognized as an intenal command,' you just informed the FBI that the computer you're using is possessed by a Costa Rican drug dealer, hence you mght get arrested soon.
I'm just kidding. If you pressed enter, then kudos! You demonstrated curiosity and courage which are the driving forces of science and tech. If you did not press enter, next time definitely do!
Open the terminal and execute(type and enter): #keep reading if you encounter any error:
pip install pygame
or
pip3 install pygame
or
python -m pip install pygame
or
python -m pip3 intsall pygame
if none of them worked and you received and error, then follow this guide 10 minutes: https://youtu.be/xhGWpnyVK8c
if you did all what the guy did, and it still does not work, then: search 'Manage app execution aliases" then deactivate the 2 python installers as shown in the image below. Then try again the previous commands.
*I did this pygame tour in a jupyter notebook because I thought it would be easy to run each line at a time so you understand what is going on. But for some reason that I ignore, if you run the commands we will write one by one(in some jupyter notebooks), the screen will freeze
Instead, open the IDLE by searching IDLE on your computer. Double click, then execute(write the command and type enter) each line as we go. As shown in the picture below. Don't type out the commands yet!
I hope you succeeded in installing the pygame module(or library)
Initializing pygame
When you start a game, a window opens and then you do whatever stuff the game proposes. Now you're the one creating the game and think of it like staring at wall(your screen) while the genie is waiting for instructions.
First, the genie needs the tools to build the game. So the first line of code is :
import pygame
The tool is in a closed box. The genie can't use it unless it is opened. To open it, progammers call it initialisation, The command is:
pygame.init()
Setting up the window and some background
Then ask the genie to open a window on the brick wall and give the dimensions of the window. You will later ask the genie to do things on the window, so it is good to save the window somewhere, hence the function which creates the window will return a Surface object or more accurately the address of where it is stored, which we will store in a variable called 'screen'
= 800, 600
screen_width, screen_height = pygame.display.set_mode((screen_width,screen_height)) screen
When you close your eyes, everything is dark because you don't let light rays enter your eyes. The absence of color is just the absence of light, hence darkness. Since you did not tell the genie to put any color on the window, the window does not have any color, reason why it is black. To paint or fill it with some color:
= (20,144,144)
screen_color screen.fill(screen_color)
Those are numbers, not colors! No, those are colors but in genie language. It is the RGB format, with each value representing the amount of red, green, and blue respectively. When you are in nature, the common source of light is sun. Now suppose you have three suns. One red, the other green, and the last blue. The color you perceive is the combination of the different amounts of each light rays from the three suns. If an rgb color is (0,0,0), it means you have closed your eyes to all three suns. If the rgb color is (255,0,0), it means you have fully opened your eyes for red, and closed for green and blue. So you see red everywhere like on a crime scene. So that's about how your computer display colors. It gives off certain amounts of red, green, and blue to give any color.
(must read) Now that you filled your screen with some color, you might be wondering why the window's color is still black. Well, it is because we did not update the new information. Videos are just a series of images. So when we do animations, we just draw many images in series with seemingly smooth changes so it appears like continuous motion, whereas we are just stacking one image on top of another. Watch this 25 seconds video to get what I mean: https://www.youtube.com/watch?v=J2xrN5WQuxw With pygame, it is the same, when the genie created a window, that was an image. When you ask to fill it with some color, it is another image, that's why you have to update(put the new image on the top of the stack). So after your changes like a change in color, write:
pygame.display.update()
You're almost there. We will soon start animations but one more thing
Rect objects, images, and drawing shapes
So you've a window and you want the genie to draw some shapes. Let's start with a circle since I said we would animate a ball in free fall. Well, if you were walking down the street and I asked you to draw a blue circle, you'll probably ask me "where should I draw it"? Then I will tell you, "on the floor," and you'll reply "the floor is so big, where exactly on the floor?" Then I would have to define a coordinate system. The floor is in 2D, so I might say, 'Draw a blue circle on the floor at position (37,73)." Then you will reply, "okay, I assume that's the coordinate the center of the circle, but what's the radius?" and I will perhaps reply '237 units.'
Along those lines, that's exactly how our genie works. To draw a circle, you'll have to execute the following commands: (sorry you have to type it one by one, but I did and it is good for learning)
= (150,200) #You will understand this in the next cell, just execute and keep reading
circle_center_x , circle_center_y = (22,66,255)
circle_color = 60 #in pixels
radius
pygame.draw.circle(screen, circle_color, (circle_center_x,circle_center_y), radius)
#So here we are asking the genie, 'Draw a circle on a Surface or window called 'screen.' The circle should be of color 'circle_color,'
#the center of the circle is at the point '(circle_center_x, circle_center_y)' and the radius of the circle should be 'radius.'
(must read) The coordinate system of pygame has its origin at the top left corner of the screen. The x values increase horizontally going towards the right, while the y values increase down
so when I in the cell above we said 'circle_center_x = 150,' 150 means horizontally 150 pixels to the right of the origin, which is at the top-left corner of the screen(or window).
You may be wondering 2 things: 1) Why your circle is not showing. Remember that you just drew a new image and have to stack it upon the previous one. So you have to call pygame.display.update(). So go back and add pygame.display.update() at the end if you want to see the circle. 2) What if I want my circle to move? Remember that videos are just a stack of images. so you just draw another image and update. However, though you can keep generating new images on top of another and tracking the location of the center of the circle to produce the animation, it will be a bit cumbersome, especially if you to draw shapes other than circles. So let me introduce the bread and butter of pygame. Get your head around this and you are almost done with this crash course.
Pygame has special objects called Rect which are just imaginary rectangular
spaces.
Note that the rectangle or Rect object is 'imaginary' because it
does not show on the screen. But it has special attributes that can let us
do animations
Creating a Rect object and doing some stuff with it. Read this section slowly and you will be a master
= 50, 65
x, y = 20, 50
rect_width, rect_height #Let us create a Rect object called 'rect_object'
= pygame.Rect(x,y,rect_width, rect_height)# we pass x and y as arguments to the constructor(creator of Rect object)
rect_object # as they are the initial coordinates of the top left corner of our Rect object.
#rect_width and rect_height are the exactly what the names indicate.
*Some attributes of a Rect object(imaginary rectangle)
rect_object.width == width of imaginary rectangle
rect_object.height == height of imaginary rectangle
rect_object.x == x-coordinate of the top left corner of the imaginary rectangle
rect_object.y == y-coordinate of the top left corner of the imaginary rectangle
rect_object.centerx == x-coordinate of the center of the imaginary rectangle
rect_object.centery == y-coordinate of its center
I keep insisting on the term imaginary because well, I don't know how to say it.
#Though pygame does not show our Rect objects since they are imaginary rectangles,
#we can draw actual rectangles or different shapes or images in the space where a Rect object is located.
#Let's draw a rectangle where the Rect object we created is located, but before, let's repaint the screen
#and update() to see whether the Rect object will show.
#we wipe out whatever objects we drew before by repainting(fill) the window
screen.fill(screen_color)
pygame.display.update()#See how after we update the screen, the Rect object still doesn't show. Because it is just a location in space!
#so there is no function that we could call to say 'draw this Rect object.'
#However, we can call a function and say, "draw this shape where the Rect object is found"
#let's do that with a rectangle.
#Now let us actually draw something at that location. A real rectangle
= (255,0,0)
rectangle_color
pygame.draw.rect(screen,rectangle_color, rect_object)
#so I am saying to the genie: Draw a rectangle on the Surface called 'screen' with color 'rectangle_color'
# where 'rect_object' is found.
pygame.display.update()
NOW SEE THE MAGIC OF RECT OBJECTS We will move our Rect object that we called 'rect_object' to the right, then draw a rectangle at the new location of our Rect object.
+= 200 #we moved the x-coordinate of the top-left corner of the Rect object by 200 pixels
rect_object.x #The entire Rect object will move to the right by the same amount(if this statement confused you,
#you don't need to understand it)
#if you really want to understand the previous statement, I am saying that
#you change the x-coordinate of the top left corner of the Rect object, but it also
#changed the coordinate of every single point on the Rect to make it shift right. Cool
#To show that the rect_object moved, let us draw another rectangle at its location
pygame.draw.rect(screen, rectangle_color, rect_object)#You don't see the rectangle? Yeah, because that's the previous image(you have to update())
pygame.display.update()
When you execute pygame.display.update(), the new red rectangle will be drawn while the old red rectangle is still there. Because the new image created by update() is actually a copy of the image from the previous update() call plus whatever you added to it. For example if you don't add anything and call update(), nothing would have seem to change but what you would be seeing is a different image but the exact copy of the previous image.
You may be thinking,"if I 'removed' the previous red rectangle and drew the new one where some pixel-distance away, it would appear as though the original rectangle has moved." Yeah, that's exactly what we do for animations. Create images and stack upon others with seemingly smooth changes. Let's put that in code.
screen.fill(screen_color)#We repaint our screen to 'erase' the previous red rectangle or whatever there was on our screen
+= 100 #we move our Rect object 100 pixels to the right
rect_object.x #we draw a rectangle at the location of our
pygame.draw.rect(screen, rectangle_color, rect_object)#rect object
#we stack the new image on top of the old one pygame.display.update()
I hope that by now, you really understand Rect objects. They are just 'imaginary' rectangular regions in space, but which are really useful because we can move our Rect objects and draw things at their locations.
Drawing a circle the smart way(using a Rect object)
#We will create a Rect object(rectangular region in space) then keep drawing circles at it its center.
#The Rect object and the circle are independent please!
#The circle is not even an object at all. We just ask the genie to draw a circle at location we indicate.
# 'Drawing' actually is just changing the pixels of a defined region to whatever color we passed as argument.
#So there is nothing like a circle object that we can move. The only thing we move is our Rect object, and we keep
#drawing circles at its center
= (55,90)
x, y = 50
radius = radius, radius
rect_width, rect_height = (50,70,70)
circle_color
#let's create a Rect object and call it "circle_rect." You could name it however you wanted, it is independent
#of the circle, but I choose that name because its purpose is to help draw the circle and choosing variable names
#in programming is important.
= pygame.Rect(x,y, rect_width, rect_height)
circle_rect
#x is the x-coordinate of the top-left corner of the Rect object that we called "circle_rect", while y is the y-coordinate
#we set the dimesions of our Rect object to fit the circle as much as possible.
#now drawing the circle:
pygame.draw.circle(screen, circle_color, (circle_rect.centerx,circle_rect.centery), radius)
#centerx is an attribute of the Rect object, which is the x-coordinate of the center of the rect object
#we want the center of our circle to be at the center of the Rect object, that's why we pass the
#coordinates of the center of our Rect object as arguments to the function used to draw the circle
This is what I mean by rect fitting the circle(since you can't see
the rect):
if you execute pygame.display.update() right after that, the circle will be drawn while the red rectangle is still there. Because the new image created by update() is a copy of the image(with the red rectangle) from the previous update() call plus whatever you added(circle) to it.
So to erase whatever was previously there and draw for animation purposes, we fill the sreen again(paint the wall) with the previous background so that whoever watching the animation won't know that it is a new image. code:
screen.fill(screen_color)
pygame.draw.circle(screen, circle_color, (circle_rect.x,circle_rect.y), radius) pygame.display.update()
I said we would simulate the motion of a ball in free fall, so now we shall call our circle 'ball'
This is very easy if you understand what is going on so far. We are going to make our imaginary rectangle move, and draw our circle each time inside it(or at its location to be precise). Let's make it move towards the right non-stop
= 1.5
x_velocity while True:
+= x_velocity #increase the x-coordinate of the top left corner by 1(x_velocity)
circle_rect.x
#repaint the screen (or wall)
screen.fill(screen_color) #draw the circle in the Rect
pygame.draw.circle(screen, circle_color, (circle_rect.x,circle_rect.y), radius) #generate a new image with all what we have added and display pygame.display.update()
(must read everything before doing anything) Oh, you just realised that the ball when passed the bounds of the window(screen). The program is running infinitely because there is no condition to stop the while loop. So press Ctrl + C to stop the program. Since you closed the program, you have to restart everything. OH NO! Well, the next cell contains everything we need for the ball code and includes the code to make the ball bounce back! (I also included the previous comments for refresher) So you have to copy the code from the next cell and copy it into a new python file(keep reading first) In the IDLE,Open a new python file by pressing left Ctrl + N (if you're using windows) or Command + N(for mac), or you just click on 'File' then 'New File.' Now copy the code in the next cell, paste, and save the file as whatever_you_want.py( note the extension must be '.py')
import pygame
pygame.init()
= 800, 600
screen_width, screen_height = pygame.display.set_mode((screen_width,screen_height))0
screen
= (20,144,144)
screen_color
screen.fill(screen_color)
###Creating a Rect object(region in space) where our circle will be.###
# The Rect object and the circle are indpendent please!
#The circle is not even an object at all. We just add the genie to draw a circle at location we indicate.
# 'drawing' actually is just changing the pixels of a defined region to whatever color we passed as argument.
#So there is nothing like a circle object that we can move. The only thing we move is our Rect object, and we keep
#drawing circles at its center
#We just use Rect object to facilitate animation
= (55,90)
x, y = 50
radius = radius, radius
rect_width, rect_height = (50,70,70)
circle_color = pygame.Rect(x,y, rect_width, rect_height)
circle_rect #x is the x-coordinate of the top-left corner of the Rect object, while y is the y-coordinate
#we set the dimesions of our Rect object to fit the circle as much as possible.
#now drawing the circle:
pygame.draw.circle(screen, circle_color, (circle_rect.centerx,circle_rect.centery), radius)#x in the first line of the cell is used to create the Rect object. It serves as the x-coordinate
#of the top-left corner of the Rect object
#centerx is an attribute of the Rect object, which is the x-coordinate of the center of the rect object
#we want the center of our circle to be at the center of the Rect object, that's why we pass the
#coordinates of the center of our Rect object, as arguments to the function used to draw the circle
#making the ball move, bounce, and change direction
=1.5
x_velocity while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
#more about this later. This is just so you don't have to press Ctrl + C to stop the progam
pygame.quit()#each time. You can now close it normally like any program
if circle_rect.x <= 0 or (circle_rect.x + circle_rect.width) >= screen_width:
*= -1 #I am using some syntactic sugar. you could write x_velocity = x_velocity * -1
x_velocity '''In the condition, we check whether the left border(x-coordinate) of the circle_rect is touching or has crossed
the left border of the screen, or if the right border of the circle_rect has crossed or is touching the right
border of the screen. If yes, then we reverse the direction of the velocity'''
+=x_velocity #change the x position of the rect
circle_rect.x #repaint the screen (or wall)
screen.fill(screen_color) #draw the circle at the location of the Rect
pygame.draw.circle(screen, circle_color, (circle_rect.x,circle_rect.y), radius) #generate a new image with all what we have added and display pygame.display.update()
There you go, you should be able to do animations now on your own, but I will still guide you through the animation of the ball in free fall (with a pause button too-and handling events).
For those who want to stop here, here are some other important pygame functions or modules that you should look up:
the other pygame.draw functions like pygame.draw.ellipse(), pygame.draw.line()
pygame.image:used for images. you don't need to draw objects using pygame all the time. You can use imgaes that you choose from the web or design yourself to do your animations.
pygame.mixers: used to play music and sounds
pygame.font: used to create special Rect that can render(display) text
pygame.event: to watch events like mouse clicks or keyboard input, and more
pygame.time and pygame.time.clock: can be used to set timers, events, adjust frame rate(amount of
imgaes drawn per second)
There are many other pygame modules and functions. This was just an introduction so if you have an idea and you don't see how you can do that with pygame yet, just check on google or ask ChatGpt how to go about that.
Now for the physicist interested in seeing if I know what free fall means, let's goooo. I will also add drag force if you don't mind :)
You're taking a physics course, perhaps phy141 or phy250 at Valparaiso University, and you want to animate the motion of objects and perhaps do your homeworks. Yes, you can just write a program that will help you answer certain questions without solving! That may sound like cheating, but if you're smart enough to write program like that, then you can probably(though sometimes not) solve the question in the first place.
Enough jokes.
Question
You're supposed to be able to answer the following question without my help, but it does not hurt to get some help since you're new to pygame. You will see how we can code things that 'seem' difficult in just a few lines. But I want you to read the question, pause for 3mins 37seconds and think about how you would go about it
a) A 3kg ball with radius 0.2m is launched with a velocity of (5m/s, 12m/s) at the top of a 200m building. Taking the launch point of the ball to be the origin of your coordinate system, with the help of pygame, animate the motion of the ball in free fall and determine how far from the building the ball first hits the ground. (Take g to be 9.8 m/s^2)
b) Add drag force and determine how far the ball lands this time. Drag Force = 0.5pCAv^2
c) Use conservation of linear momentum and work energy theorem to make the ball bounce, provided it loses 60% of its energy each time it hits the ground.
Data:
mass of ball, m = 3 kg
radius of ball, r = 0.2 m
initial velocity = 5m/s(i) + 12m/s(j)
height of building = 10m
gravitational strength = 9.8m/s^2
p = density of fluid which is air in this case. At 15 degrees celcius, p of air = 1.225 kg/m
C = Drag coefficient. Take C = 0.4
A = Cross sectional Area. In this case, it will be half of the area of our ball, since that's the part of our ball that is 'exposed' to the air. Like half the surface of the Earth is exposed to the Sun.
v = Speed of object relative to the air. Let's just assume it is the same speed of the object relative to the ground(assumed not to move). A lot of assumptions, I hate that too, sigh.
percentage energy loss per hit = 60%
This section is important because our genie does not understand the units given to us
Concerning our physics problem, we were given velocity in metres per second. But our genie measures distances in pixel. So we first of all have to convert metres to pixel.
Metres to Pixels
Conversion Factor to Pixel(CFP) = Height of our pygame window in pixel/height of the scenery in real life.
Since our building is 10m high, then we can approximate the total height of the scenery to be twice that.
Height of scenery = 20m.
We will define the height of our pygame window in the code. Nowadays most laptops support HD format, meaning they have a minimum resolution(dimensions of the screen/monitor in pixels) of 1366 by 768 pixels. So we will set the height of our pygame window to be a more than that.
Height of pygame window = 800px.
Therefore our CFP is:
CFP = 800px/20m = 40px/m
***Note that a 40px/m CFP is good no matter the height of the building because our eyes cannot see really far. If we make our CFP a function of the height of a building, then our ball will be very small for tall buildings(yes, cause the building is tall and you can't see very far) or the ball will appear very big for small building. For different problems like motion of planet in orbits, you will use a different CFP. Just do the maths and try out different values.
(skipable)Also if the object is moving towards or away from the viewer, then you can keep resizing the moving object(the ball in this case) by applying some physics(lenses) to know the size the object should 'appear'***
Formula to find position
From classical physics(or common sense), you know that at a constant velocity,
1) position = initial_position + velocity * time
or
2) curremt_position = previous_position + velocity * time_step
But in this context, there is acceleration, hence position as function of time is given by:
position = initial_position + initial_velocity * time + 0.5 * acceleration * time^2
The velocity and acceleration are given to us in metres per second and metres per second per second respectively, so all we have to do is to convert to pixels per second and pixels per second per second by multiplying by our CFP.
There you go, now you can simulate free fall.
import pygame #getting the tool box to build the game
import time #a tool we will use to track time. you don't need to install because
#it comes with python. It is an 'in-built' module.
#initializing(opening) the tool box
pygame.init()
#Defining variables. Constants are going to be in upper case(programming conventions).
= (1200,800, (255,255,255))
SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_COLOR = (0,0,0)
BALL_COLOR = 10
building_height_in_m = 0.2
ball_radius_in_m
#CFP = SCREEN_HEIGHT/(building_height_in_m * 2) #Conversion Factor to Pixel
= 40
CFP = 3
MASS = ball_radius_in_m * CFP
BALL_RADIUS = building_height_in_m * CFP
BUILDING_HEIGHT = 9.8 * CFP
GRAVITY
= SCREEN_HEIGHT - BUILDING_HEIGHT#We do this this way because of the orientation of our coordinate system
initial_y_of_ball = 100 #I chose this so that the ball starts some distance away from the border of the screen
BUILDING_WIDTH = BUILDING_WIDTH
initial_x_of_ball = tuple(map(lambda x: x * CFP, (5, -12)))#converting the
initial_x_velocity, initial_y_velocity #velocity from metres per second to pixels per seconds using the CFP.
#if you don't know this syntax, I am just saying, map this function to the two values (5,-12) an return the tuple.
#Google if you want to learn more.
#I could have just written: initial_x_velocity = 5 * CFP and initial_y_velocity = -12 * CFP
= pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen #naming the window
'Free Fall')
pygame.display.set_caption(
#Creating the Rect object we will use for the ball
= BALL_RADIUS , BALL_RADIUS
rect_width, rect_height = pygame.Rect(0,0,rect_width, rect_height)#we create the Rect object at a random position first. I chose (0,0) randomly
ball_rect = initial_x_of_ball, initial_y_of_ball #I move the Rect object so that its center matches
ball_rect.centerx, ball_rect.centery #the initial position of the ball. Though I updated both the x and the y component,
#I did not need to update both because when I update one, the other gets updated automatically
#Creating a rect for the building
= pygame.Rect(0, SCREEN_HEIGHT-BUILDING_HEIGHT, BUILDING_WIDTH, BUILDING_HEIGHT)
building_rect = (255,0,0)
BUILDING_COLOR
= time.time()# returns time that has elapsed in seconds since 01/01/1970
initial_time = 0
time_elapsed #animation
while True:
for event in pygame.event.get():#ignore this for now
if event.type == pygame.QUIT:
pygame.quit()
= time.time() - initial_time
time_elapsed = initial_x_of_ball + initial_x_velocity * time_elapsed
ball_rect.centerx = initial_y_of_ball + initial_y_velocity * time_elapsed + 0.5 * GRAVITY * time_elapsed ** 2
ball_rect.centery #repaint the screen (or wall)
screen.fill(SCREEN_COLOR) #draw the circle at the location of the Rect
pygame.draw.circle(screen, BALL_COLOR, (ball_rect.centerx,ball_rect.centery), BALL_RADIUS)
pygame.draw.rect(screen, BUILDING_COLOR, building_rect)#generate a new image with all what we have added and display pygame.display.update()
The animation works. Cool.
However, the ball crosses the 'ground.' Also, it is boring that we cannot pause the animation and track the position of the ball. So we will display a bar that shows the position of our ball in the standard coordinate system.
For simplicity, we will we draw two buttons. One that displays the coordinate of the ball, its center of mass to be precise, and one that displays 'play' whenever we pause the animation. You can add other buttons like restart or whatever, but I won't do that because you should be able to do it yourself after you get the concept. Easy concept.
A quick note about images We have been drawing shapes so far. What if we instead want to display an image for example. For that, we would need to get the image first. We will not draw images on our free ball animation, unless you want your background to be an image, but it is important that you know how to display images so you understand text.
= r'C:\Users\HP\Desktop\dog.jpg' #This is the path to a dog image on my desktop
path = pygame.image.load(path) #This will return the image as a Surface object
image_surface #If you don't remember what a Surface object is, it is not important. Our screen is also a Surface object
#With pygame, you can draw a Surface on another Surface, just like we were drawing shapes on our screen Surface
#To fill our screen Surface with color, we used the command screen.fill(screen_color)
#Now to draw a Surface on another, we need to know at what position the surface will be drawn, just like with the shapes
#To do that, we can just create a Rect object and then draw our Surface at the location of the Rect object.
#Fortunately, you can get the Rect object of a Surface without creating one yourself. Not super helpful but helpful
= image_surface.get_rect()#This will return a Rect object with the dimensions of our Surface, with initial
image_rect #position to be (0,0)
#you can set the position of the rect to by anywhere, just like we did in the previous animation
#let's put the image_rect at the center of our screen
= screen.get_rect()
screen_rect = screen_rect.centerx, screen_rect.centery
image_rect.centerx, image_rect.centery
#Drawing the image Surface on our screen Surface at the location of the image Rect:
screen.blit(image_surface, image_rect)#There you go, you just drew an image at the center of your screen.
#Remember to update() if you want to see the new image
pygame.display.update()
#If the image is too big or too small, you can still resize using pygame function. It will just resize in the program
#it will not modify the original image.
#For example to resize your image to be 50 by 50px
=
new_width, new_height = pygame.transfor.scale(image_surface, new_width, new_height)
scaled_image_surface
Now that you know how to load, resize, and display images, you can download an image of a build and use it as our building in our animation, in the place of that big red box. Moving forward, let us display text.
Displaying text We mostly have different handwritings. I don't write like my grandmother, and she does not write like the late Steve Jobs. So to write text, we need to create our own 'writer' with a particular handwriting (font).
= pygame.font.SysFont(None, 48)
writer #None means it should use the default handwriting, while 48 is the font size.
So we have our writer. What should our writer display?
= 'PLAY' text
When you write(not type), you write on a paper right? So when we will ask our writer to write something, it will write on a paper(Surface) and return in to us. We also have to tell the color of the text so it knows what pen to use.
= (0,90,255)
text_color = writer.render(text, antialias = True, text_color)
paper_surface #Note that Surface is the same dataype that we used to display images.
#So displaying the paper_surface is the same thing as displaying images
Let us draw our paper_surface(or play button) at the center of the screen.
= paper_surface.get_rect() #returns the Rect with the dimensions of our paper at position (0,0)
rect_of_paper_surface
#Putting the Rect at the center of our screen:
= screen.get_rect()
screen_rect = screen_rect.centerx, sreen_rect.cetnery
rect_of_paper_surface.centerx, rect_of_paper_surface.centery
#Drawing our paper_surface at the location of the Rect on our screen:
screen.blit(paper_surface, rect_of_paper_surface) pygame.display.update()
There you go, you know how to draw a play button on a screen. The procedure for the button to track the position of the ball is the same. So you will see it in the code when we put everything together.
Handling Events
In the animation code, you've been seeing:
for event in pygame.evet.get():
if event.type == pygame.QUIT:
pygame.quit()
This is the 'listener.' It listens to user input(events) and does things if we programmed it to do so. For example, in the code above, we say 'if you hear quit(a play clicking on the close window button), then stop and quit.'
So if we want our animation to 'pause' when we press a certain key or click on a certain button, then we can do so by adding the code for that in the listener. Let's say we want the animation to 'pause' when we press SPACE.
for even in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
animation_active = False #you'll understand this in the final code.
I put 'pause' because we won't actually pause the animation. We will just not update the position of our ball so that the same image keeps being drawn. It will appearto the viewer as though everything has paused.
In the final code, we will also add the option for the listener to get the position of the mouse and determine whether the play button has been clicked for the animation to 'resume.'
I could write the following code more optimally and refactor some stuff, but for the sake of the neo-programmer, it is better to do it the way I did so he/she/whatever pronoun focuses more on every deail of the animation and does not get confused/distracted.
There is no new concept in the code. It just the physics we talked about and whatever you learned in this guide. At first glance, it may feel overwhelming and you would ask yourself, 'how did he know that he will use this or that.' Programming is a very back-and-forth process. Almost nothing works on first attempt, so we run, fix, the errors, run again. Just that we can't give you the code with errors. We just give you the code that works and it makes you feel like we knew everything in advance and we are perfect. Read the comments and think, and you should be fine. If you don't understand something, then try to code how you think it should be done yourself. I do that all the time and sometimes I code better than the book author, guide, or teacher.
What takes much space in the code below are the buttons and handling events. So you might as well get rid of that if you don't really care about being able to pause the animation.
import pygame #getting the tool box to build the game
import time
#initializing(opening) the tool box
pygame.init()
#Defining variables. Constants are going to be in upper case(programming conventions).
= (1200,800, (255,255,255))
SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_COLOR = (0,0,0)
BALL_COLOR = 10
building_height_in_m = 0.2
ball_radius_in_m
= 40 #You can make the CFP a function of height so you can always see the entire building by uncommenting the next line
CFP #CFP = SCREEN_HEIGHT/(building_height_in_m * 2) #Conversion Factor to Pixel
= 3
MASS = ball_radius_in_m * CFP
BALL_RADIUS = building_height_in_m * CFP
BUILDING_HEIGHT = 9.8 * CFP
GRAVITY
= SCREEN_HEIGHT - BUILDING_HEIGHT#We do this this way because of the orientation of our coordinate system
initial_y_of_ball = 100 #I chose this so that the ball starts some distance away from the border of the screen
BUILDING_WIDTH = BUILDING_WIDTH
initial_x_of_ball = tuple(map(lambda x: x * CFP, (5, -12)))#converting the
initial_x_velocity, initial_y_velocity #velocity from metres per second to pixels per frames using the formula we derived earlier.
#if you don't know this syntax, I am just saying, map this function to the two values (5,-12) an return the tuple.
#Google if you want to learn more.
#I could have just written: initial_x_velocity = 5 * CFP/FRAME_RATE and initial_y_velocity = -12 * CFP/FRAME_RATE
= pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen = screen.get_rect()
rect_of_screen #naming the window
'Free Fall')
pygame.display.set_caption(
#Creating the Rect object we will use for the ball
= BALL_RADIUS, BALL_RADIUS
rect_width, rect_height = pygame.Rect(0,0,rect_width, rect_height)#we create the Rect object at a random position first. I chose (0,0) randomly
ball_rect = initial_x_of_ball, initial_y_of_ball #I move the Rect object so that its center matches
ball_rect.centerx, ball_rect.centery #the initial position of the ball. Though I updated both the x and the y component,
#I did not need to update both because when I update one, the other gets updated automatically
#Creating a rect for the building
= pygame.Rect(0, SCREEN_HEIGHT-BUILDING_HEIGHT, BUILDING_WIDTH, BUILDING_HEIGHT)
building_rect = (220,220,220)
BUILDING_COLOR
= False
animation_active = False
just_activated = pygame.font.SysFont('Times New Roman', 48) #we use a specific writer for the play button because it will have a different
play_writer #font size from the tracking button
= (0,0,0)
play_color = 'PLAY'
play_text = (220,220,220)
play_button_background_color = play_writer.render(play_text, True, play_color, play_button_background_color)
play_button_surface #The last argument for background color is optional, or in python terms 'non_positional.'
= play_button_surface.get_rect()
rect_of_play_button_surface = rect_of_screen.center #yeah, you don't need to specifiy x or y, it will take care of everything
rect_of_play_button_surface.center
= pygame.font.SysFont('Verdana', 15) #You can use any font. if the font(handwriting) you input is not a system font
track_writer #then the genie will use its defualt font.
= (0,0,0)
track_color = '' #we will be determined below
track_text
= 'TBD' #To be determined
RANGE
= True
animation_just_started = 0#used to count the amount of time the ball has been in the air
flight_time
#animation
while True:
for event in pygame.event.get():#ignore this for now
if event.type == pygame.QUIT:
pygame.quit()elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
= False
animation_active True)
pygame.mouse.set_visible(elif event.type == pygame.MOUSEBUTTONDOWN:
= pygame.mouse.get_pos()
mouse_position if rect_of_play_button_surface.collidepoint(mouse_position) and animation_active == False:
= True
animation_active False)
pygame.mouse.set_visible(= time.time()
previous_time
if animation_active:
+= time.time() - previous_time
flight_time = time.time()
previous_time = initial_x_of_ball + initial_x_velocity * flight_time
ball_rect.centerx = initial_y_of_ball + initial_y_velocity * flight_time + 0.5 * GRAVITY * flight_time** 2
ball_rect.centery if ball_rect.centery >= SCREEN_HEIGHT and animation_just_started==False:
= False
animation_active
= False
animation_just_started
= round((ball_rect.centerx-BUILDING_WIDTH)/CFP,2) #Converting back to metres and rounding to two decimal places.
RANGE = round((ball_rect.centerx-BUILDING_WIDTH)/CFP, 2)
x_position = round((SCREEN_HEIGHT-ball_rect.centery-BUILDING_HEIGHT)/CFP, 3)
y_position
= f'X: {x_position} m Y: {y_position} m RANGE: {RANGE} m time:{round(flight_time, 3)}s'
track_text = track_writer.render(track_text,True, track_color)
track_surface #repaint the screen (or wall)
screen.fill(SCREEN_COLOR) if animation_active == False:
screen.blit(play_button_surface, rect_of_play_button_surface)0,0))#the blit method can also take the coordinate of the top left corner of where you want to display the surface
screen.blit(track_surface, (#draw the circle at the location of the Rect
pygame.draw.circle(screen, BALL_COLOR, (ball_rect.centerx,ball_rect.centery), BALL_RADIUS)
pygame.draw.rect(screen, BUILDING_COLOR, building_rect)#generate a new image with all what we have added and display
pygame.display.update()
There you go! You have answered the a) part of the question which was to find the range(how far the ball lands from the building). In fact, your animation displays the time of flight as well.
For your little homework, answer the b) and c) part of the question which invovles just adding drage force and make the ball bounce. Simple (if you can set up the differential equation and integrate).
I don't have time to make an appendix so I am just going to mention some lies I made in this guide. Don't be terrified because I said lie. Even the physics we did was just a lie, but not far from the truth (General relativity). Yeah, sometimes we lie to keep things simple for practical reasons and also so that you understand. If you always believe what someone says just because he/she/whatever took his/her/whatever time to write, then you need to reconsider your life choices.
OH NO, I slept, and I don't remember most of the lies. Anyways, have a nice day!
If you have any concern email me at [email protected] or [email protected] . I will probably reply in less than 8hrs.