The real world can be a difficult place to understand. Whether you’re trying to make sense of the people that make up the real world and their actions, or the physical world itself, things are never easy. Sometimes, coding can help understand what’s going on and maybe, just maybe, help find a solution to a problem. Perhaps one day, Python programs will be used to solve all the world’s problems! But I’m not that ambitious in this blog post, so I’ll choose to talk about how we can create a simulation of a bouncing ball using Python.
Often in this blog, I use real-world analogies to understand Python topics. Here, I’m using Python to understand better a real-world process. Granted, the physics of a bouncing ball may not be the biggest mystery in the universe, but the exercise of representing real-world processes computationally often helps you understand that particular process better.
Using The turtle
Module to Simulate A Bouncing Ball in Python
It’s most fun and most instructive to work on this simulation from first principles without using any "fancy" modules. The turtle
module is a brilliant tool to create such physical simulations as it’s relatively straightforward to use, and it gives you full control over whatever happens in your animation. The turtle
module is part of the standard library, and therefore, you already have it on your computer if you’ve installed Python!
A very brief primer of turtle
basics
The main class defined in the turtle
module is Turtle
. The "turtle" you create will obey your instructions and move around the screen, drawing lines and other shapes as it goes along. You can create an instance of turtle.Turtle
and explore some of its methods:
import turtle ball = turtle.Turtle() ball.forward(100) ball.left(45) ball.pensize(5) ball.color("dark salmon") ball.forward(50) ball.right(90) ball.penup() ball.forward(100) ball.right(135) ball.pensize(10) ball.color("light green") ball.pendown() ball.forward(200) ball.shape("circle") turtle.done()
I’ll let you try this code out yourself to see the artistic masterpiece it produces! Once you create the instance of the Turtle
class, which you call ball
, you apply a number of operations that move and steer the turtle, change its colour and the thickness of the line it draws, and more. The final line in this code keeps the program running in a loop until you choose to close the window to terminate the program.
Creating A Ball That Falls
Let’s look at the first steps you’ll need to create this animation. You’ll start by creating a falling ball. You’ll make it bounce later:
- Create a
turtle.Turtle
that will represent the ball - Change the shape and colour of the
Turtle
as required - Move
Turtle
by a small step that will represent the speed of the ball - Repeat in a
while
loop
Let’s translate these steps into Python code:
import turtle ball = turtle.Turtle() ball.penup() ball.color("green") ball.shape("circle") while True: ball.sety(ball.ycor() - 1)
The initial position of a Turtle
object is in the middle of the window. This position corresponds to the coordinates (0, 0)
. Although you can use the method forward()
to move the ball, you’re using a combination of the methods sety()
and ycor()
to move the ball. You’re setting the y-coordinate using sety()
to its current value, which is what ycor()
returns, minus 1
pixel.
The ball is now falling. However, this is not how real balls fall in the real world. Gravity accelerates the ball towards the ground so that the speed of the ball increases with time.
Taking Into Account The Effects Of Gravity
In this post, I want to focus on the most basic translation from physics to simulation. Rather than using the distance in metres and time in seconds and the physical constants that are required to work out how much a ball accelerates, I’ll use distance in pixels and time measured in iterations of a while
loop. By using arbitrary units, I can focus on the key points that make this simulation work. A later post in the Bouncing Ball series will take care of the details.
You can define a variable called velocity
which represents the velocity of the ball at any time during the simulation. The velocity will be measured in pixels per iteration rather than metres per second.
The velocity will increase by a fixed amount each iteration of the while
loop, which is the base time unit used. The amount the velocity increases by is the acceleration due to gravity:
import turtle gravity = -0.05 # pixels/(time of iteration)^2 velocity = 0 # pixels/(time of iteration) ball = turtle.Turtle() ball.penup() ball.color("green") ball.shape("circle") while True: ball.sety(ball.ycor() + velocity) velocity += gravity
I’m choosing the value for acceleration due to gravity arbitrarily, as in this version, I’m not using real-world units. I’m also ignoring air resistance to keep things on the simpler side. This is what the simulation looks like at the moment:
The ball starts off with a velocity of 0
, but then accelerates downwards. Before turning the ball into a bouncing ball, I’ll say a few words on how to avoid making the animation lag too much.
Better Animation Control
This post is not about animation or the turtle
module, so I’ll keep this section brief. When a Turtle
moves or turns on the screen, one of the slowest processes is to update the image on the screen. In the turtle
module, when a Turtle
moves, by default, several steps of that motion are displayed. For example, if you turn a Turtle
by 90
degrees, you’ll see the Turtle
turn relatively gradually on the screen as it goes from its starting heading to its final one.
When you have a lot of movement occurring in an animation, you would like the coordinates and the values of the angles to change as required "behind the scenes", and then to update the picture only once every so often. The simplest way to do this is to choose to update the frame of the animation only once each iteration of the while
loop. Each iteration of the loop represents a frame of the animation.
In the turtle
module, you can achieve this with the following steps:
- Create an instance of a
turtle.Screen
object. This object is created automatically when you use theturtle
module, but creating it explicitly allows you to assign it to a variable. - Use the
tracer()
method of theScreen
object using the value0
as an argument. This turns the animation off. Whentracer()
is set to0
, the graphics will not be displayed until you call theupdate()
method. - Call the
update()
method of theScreen
object within thewhile
loop to update the graphics once per iteration.
The modified code is now as follows:
import turtle gravity = -0.005 # pixels/(time of iteration)^2 velocity = 0 # pixels/(time of iteration) window = turtle.Screen() window.tracer(0) ball = turtle.Turtle() ball.penup() ball.color("green") ball.shape("circle") while True: ball.sety(ball.ycor() + velocity) velocity += gravity window.update()
Note that once you set the animation to update only once per iteration, the animation will go faster. Therefore, you’ll need to adjust the velocity
value to a smaller number to avoid the ball shooting off the screen too quickly. You may choose a different value to suit what works best on your computer.
Bouncing Ball in Python
You’re getting there, but to create a simulation of a bouncing ball in Python, you now need to make the ball bounce.
The first step is to set the size of the window you’re using for your animation so that you know the coordinates of the floor. You can use the Screen
method setup()
for this with the width and height in pixels as arguments.
You can then rely on the Turtle
method ycor()
to return the ball’s y-position in every frame of the animation. An if
statement will help you determine whether the ball has hit the floor:
import turtle gravity = -0.005 # pixels/(time of iteration)^2 velocity = 0 # pixels/(time of iteration) width = 600 height = 800 window = turtle.Screen() window.setup(width, height) window.tracer(0) ball = turtle.Turtle() ball.penup() ball.color("green") ball.shape("circle") while True: ball.sety(ball.ycor() + velocity) velocity += gravity if ball.ycor() < -height / 2: velocity = -velocity window.update()
Recall that as the coordinates at the centre of the screen are (0, 0)
, the bottom of the screen is represented by -height/2
. You now have a bouncing ball in Python:
However, this is still not quite what happens in real life. The ball always bounces up to the same height and will keep doing so forever. You’ll need one more modification.
Losing Some Energy With Every Bounce
Each time the ball bounces on the ground, it loses a bit of energy. This is why the height of a bouncing ball is lower with every bounce in the real world. This is not happening yet in the simulation. You can fix this by taking away some of the ball’s velocity with every bounce:
import turtle gravity = -0.005 # pixels/(time of iteration)^2 velocity = 0 # pixels/(time of iteration) energy_loss = 0.95 width = 600 height = 800 window = turtle.Screen() window.setup(width, height) window.tracer(0) ball = turtle.Turtle() ball.penup() ball.color("green") ball.shape("circle") while True: ball.sety(ball.ycor() + velocity) velocity += gravity if ball.ycor() < -height / 2: velocity = -velocity * energy_loss window.update()
The simulation of the bouncing ball is looking a lot more realistic now:
At the moment, all you can simulate is dropping a ball vertically downwards. The final step in this simulation will allow you to toss the ball in any direction.
Moving The Ball Sideways
In the first line in the while
loop, you’re using sety()
to move the ball along the y-axis. You can include a similar line to move the ball along the x-axis. You’ll need to do some refactoring to change the name velocity
to y_velocity
everywhere in the code, as now you’ll need two velocity components, y_velocity
and x_velocity
. You can set the initial values of these two velocity components to any values you wish to simulate "tossing the ball":
import turtle gravity = -0.005 # pixels/(time of iteration)^2 y_velocity = 1 # pixels/(time of iteration) x_velocity = 0.25 # pixels/(time of iteration) energy_loss = 0.95 width = 600 height = 800 window = turtle.Screen() window.setup(width, height) window.tracer(0) ball = turtle.Turtle() ball.penup() ball.color("green") ball.shape("circle") while True: ball.sety(ball.ycor() + y_velocity) ball.setx(ball.xcor() + x_velocity) y_velocity += gravity if ball.ycor() < -height / 2: y_velocity = -y_velocity * energy_loss window.update()
The gravity adjustment only affects the y_velocity
component, and the if
statement dealing with the bouncing off the ground also only affects the y_velocity
.
Bouncing Off The Walls
However, the ball now soon goes off the screen. You need to put some walls in too. I’m also going to add some comments in the code at this stage to make it more readable:
import turtle # Set key parameters gravity = -0.005 # pixels/(time of iteration)^2 y_velocity = 1 # pixels/(time of iteration) x_velocity = 0.25 # pixels/(time of iteration) energy_loss = 0.95 width = 600 height = 800 # Set window and ball window = turtle.Screen() window.setup(width, height) window.tracer(0) ball = turtle.Turtle() ball.penup() ball.color("green") ball.shape("circle") # Main loop while True: # Move ball ball.sety(ball.ycor() + y_velocity) ball.setx(ball.xcor() + x_velocity) # Acceleration due to gravity y_velocity += gravity # Bounce off the ground if ball.ycor() < -height / 2: y_velocity = -y_velocity * energy_loss # Set ball to ground level to avoid it getting "stuck" ball.sety(-height / 2) # Bounce off the walls (left and right) if ball.xcor() > width / 2 or ball.xcor() < -width / 2: x_velocity = -x_velocity window.update()
You now have a bouncing ball in Python, using the turtle
module.
This simulation is not perfect. There are other refinements you could make, but for the purpose of this blog, this works perfectly fine.
Final Words
Using only relatively basic knowledge of the laws of motion and the effect of gravity on a falling ball, you’ve been able to create a reasonably realistic simulation of a bouncing ball in Python.
In the second blog post in the Bouncing Ball Series, I’ll extend this simulation using object-oriented programming in Python to create many balls all moving at different speeds and in different directions. The class Ball
that I’ll discuss in that post can also form the building block of several ball-related games.
Subscribe to
The Python Coding Stack
Regular articles for the intermediate Python programmer or a beginner who wants to “read ahead”