You are on page 1of 45

25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

The Python Coding Book ☰ Menu

The friendly, relaxed programming book

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 1/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

Anatomy of a 2D Game using Python’s turtle and


Object-Oriented Programming
Posted on 12th March 2023 by Stephen Gruppetta

When I was young, we played arcade games in their original form on tall
rectangular coin-operated machines with buttons and joysticks. These games had
a resurgence as smartphone apps in recent years, useful to keep one occupied
during a long commute. In this article, I’ll resurrect one as a 2D Python game and
use it to show the “anatomy” of such a game.

You can follow this step-by-step tutorial even if you’re unfamiliar with all the topics.
In particular, this tutorial will rely on Python’s turtle module and uses the object-
oriented programming (OOP) paradigm. However, you don’t need expertise in
either topic, as I’ll explain the key concepts you’ll need. However, if you’re already
familiar with OOP, you can easily skip the clearly-marked OOP sections.

This is the game you’ll write:

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 2/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

The rules of the game are simple. Click on a ball to bat it up. How long can you last
before you lose ten balls?

In addition to getting acquainted with OOP principles, this tutorial will show you
how such games are built step-by-step.

Note about content in this article: If you’re already familiar with the key
concepts in object-oriented programming, you can skip blocks like this
one. If you’re new or relatively new to the topic, I recommend you read
these sections as well.

The Anatomy of a 2D Python Game | Summary


I’ll break this game down into eight key steps:

1. Create a class named Ball and set up what should happen when you create a
ball
2. Make the ball move forward
3. Add gravity to pull the ball downwards
4. Add the ability to bat the ball upwards
5. Create more balls, with each ball created after a certain time interval
6. Control the speed of the game by setting a frame rate
7. Add a timer and an end to the game
8. Add finishing touches to the game

Are you ready to start coding?

A visual summary of the anatomy of a 2D Python game

Here’s another summary of the steps you’ll take to create this 2D Python game.
This is a visual summary:

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 3/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

1. Create a Class Named Ball


You’ll work on two separate scripts to create this game:

juggling_ball.py
juggling_balls_game.py

You’ll use the first one, juggling_ball.py, to create a class called Ball which will
act as a template for all the balls you’ll use in the game. In the second script,
juggling_balls_game.py, you’ll use this class to write the game.
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 4/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

It’s helpful to separate the class definitions into a standalone module to provide a
clear structure and to make it easier to reuse the class in other scripts.

Let’s start working on the class in juggling_ball.py. If you want to read about
object-oriented programming in Python in more detail before you dive into this
project, you can read Chapter 7 | Object-Oriented Programming. However, I’ll also
summarise the key points in this article in separate blocks. And remember that if
you’re already familiar with the key concepts in OOP, you can skip these additional
note blocks, like the one below.

A class is like a template for creating many objects using that template.
When you define a class, such as the class Ball in this project, you’re not
creating a ball. Instead, you’re defining the instructions needed to create a
ball.

The __init__() special method is normally the first method you define in
a class and includes all the steps you want to execute each time you create
an object using this class.

Create the class

Let’s start this 2D Python game. You can define the class and its __init__() special
method:

1. # juggling_ball.py
2.

3. import random
4. import turtle
5.

6. class Ball(turtle.Turtle):
7. def __init__(self, width, height):
8. super().__init__()
9. self.width = width
10. self.height = height
11. self.shape("circle")
12. self.color(
13. random.random(),
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 5/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

14.
random.random(),
15. random.random(),
16. )
17. self.penup()
18. self.setposition(
19. random.randint(-self.width // 2, self.width /
20. random.randint(-self.height // 2, self.height
21. )

The class Ball inherits from turtle.Turtle which means that an object which is a
Ball is also a Turtle. You also call the initialisation method for the Turtle class
when you call super().__init__().

The __init__() method creates two data attributes, .width and .height. These
attributes set the size in pixels of the area in which the program will create the ball.

self is the name you use as a placeholder to refer to the object that you’ll
create. Recall that when you define a class, you’re not creating any objects.
At this definition stage, you’re creating the template. Therefore, you need a
placeholder name to refer to the objects you’ll create in the future. The
convention is to use self for this placeholder name.

You create two new data attributes when you write:


self.width = width
self.height = height

An attribute belongs to an object in a class. There are two types of


attributes:
– Data attributes
– Methods

You can think of data attributes as variables attached to an object.


Therefore, an object “carries” its data with it. The data attributes you create
are self.width and self.height.

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 6/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

The other type of attribute is a method. You’ll read more about methods
shortly.

The rest of the __init__() method calls Turtle methods to set the initial state of
each ball. Here’s a summary of the four turtle.Turtle methods used:

.shape() changes the shape of the turtle


.color() sets the colour of the turtle (and anything it draws)
.penup() makes sure the turtle will not draw a line when it moves
.setposition() places the turtle at a specific _xy-_coordinate on the screen.
The centre is (0, 0).

You set the shape of the turtle to be a circle (it’s actually a disc, but the name of
the shape in turtle is “circle”). You use a random value between 0 and 1 for each of
the red, green, and blue colour values when you call self.color(). And you set the
turtle’s position to a random integer within the bounds of the region defined by
the arguments of the __init__() method. You use the floor division operator // to
ensure you get an integer value when dividing the width and height by 2.

It’s a good idea to define the .__repr__() special method for a class. As you won’t
use this explicitly in this project, I won’t add it to the code in the main article.
However, it’s included in the final code in the appendix.

Test the class

You can test the class you just created in the second script you’ll work on as you
progress through this project, juggling_balls_game.py:

1. # juggling_balls_game.py
2.

3. import turtle
4.

5. from juggling_ball import Ball


6.

7. WIDTH = 600
8. HEIGHT = 600
9.

10. window = turtle.Screen()


11. window.setup(WIDTH, HEIGHT)
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 7/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

12.

13. Ball(WIDTH, HEIGHT)


14. Ball(WIDTH, HEIGHT)
15.

16. turtle.done()

You create a screen and set its size to 600 x 600 pixels. Next, you create two
instances of the Ball class. Even though you define Ball in another script, you
import it into the scope of your game program using from juggling_ball import
Ball.

Here’s the output from this script:

You call Ball() twice in the script. Therefore, you create two instances of the class
Ball. Each one has a random colour and moves to a random position on the
screen as soon as it’s created.

An instance of a class is each object you create using that class. The class
definition is the template for creating objects. You only have one class
definition, but you can have several instances of that class.

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 8/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

Data attributes, which we discussed earlier, are sometimes also referred to


as instance variables since they are variables attached to an instance. You’ll
also read about instance methods soon.

You may have noticed that when you create the two balls, you can see them
moving from the centre of the screen where they’re created to their ‘starting’
position. You want more control on when to display the objects on the screen.

To achieve this, you can set window.tracer(0) as soon as you create the screen and
then use window.update() when you want to display the turtles on the screen. Any
changes that happen to the position and orientation of Turtle objects (and Ball
objects, too) will occur “behind the scenes” until you call window.update():

1. # juggling_balls_game.py
2.

3. import turtle
4.

5. from juggling_ball import Ball


6.

7. WIDTH = 600
8. HEIGHT = 600
9.

10. window = turtle.Screen()


11. window.setup(WIDTH, HEIGHT)
12. window.tracer(0)
13.

14. Ball(WIDTH, HEIGHT)


15. Ball(WIDTH, HEIGHT)
16. window.update()
17.

18. turtle.done()

When you run the script now, the balls will appear instantly in the correct starting
positions. The final call to turtle.done() runs the main loop of a turtle graphics
program and is needed at the end of the script to keep the program running once
the final line is reached.

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 9/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

You’re now ready to create a method in the Ball class to make the ball move
forward.

2. Make Ball Move Forward


Let’s shift back to juggling_ball.py where you define the class. You’ll start by
making the ball move upwards at a constant speed.

You can set the maximum velocity that a ball can have as a class attribute
.max_velocity and then create a data attribute .velocity which will be different
for each instance. The value of .velocity is a random number that’s limited by the
maximum value defined at the class level:

1. # juggling_ball.py
2.

3. import random
4. import turtle
5.

6. class Ball(turtle.Turtle):
7. max_velocity = 5
8.

9. def __init__(self, width, height):


10. super().__init__()
11. self.width = width
12. self.height = height
13. self.shape("circle")
14. self.color(
15. random.random(),
16. random.random(),
17. random.random(),
18. )
19. self.penup()
20. self.setposition(
21. random.randint(-self.width // 2, self.width /
22. random.randint(-self.height // 2, self.height
23. )
24. self.setheading(90)
25. self.velocity = random.randint(1, self.max_veloci

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 10/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

You also change the ball’s heading using the Turtle method .setheading() so that
the object is pointing upwards.

A class attribute is defined for the class overall, not for each instance. This
can be used when the same value is needed for every instance you’ll
create of the class. You can access a class attribute like you access instance
attributes. For example, you use self.max_velocity in the example above.

Next, you can create the method move() which moves the ball forward by the
value stored in self.velocity.

A method is a function that’s part of a class. You’ll only consider instance


methods in this project. You can think of an instance method as a function
that’s attached to an instance of the class.

In Python, you access these using the dot notation. For example, if you
have a list called numbers, you can call numbers.append() or numbers.pop().
Both .append() and .pop() are methods of the class list, and every
instance of a list carries these methods with it.

The method .move() is an instance method, which means the object itself is
passed to the method as its first argument. This is why the parameter self is
within the parentheses when you define .move().

We’re not using real world units such as metres in this project. For now, you can
think of this velocity as a value measured in pixels per frame instead of metres per
second. The duration of each frame is equal to the time it takes for one iteration of
the while loop to complete.

Therefore, if you call .move() once per frame in the game, you want the ball to
move by the number of pixels in .velocity during that frame. Let’s add the
.move() method:

1. # juggling_ball.py
2.

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 11/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

3. import random
4. import turtle
5.

6. class Ball(turtle.Turtle):
7. max_velocity = 5
8.

9. def __init__(self, width, height):


10. super().__init__()
11. self.width = width
12. self.height = height
13. self.shape("circle")
14. self.color(
15. random.random(),
16. random.random(),
17. random.random(),
18. )
19. self.penup()
20. self.setposition(
21. random.randint(-self.width // 2, self.width /
22. random.randint(-self.height // 2, self.height
23. )
24. self.setheading(90)
25. self.velocity = random.randint(1, self.max_veloci
26.

27. def move(self):


28. self.forward(self.velocity)

You can test the new additions to the class Ball in juggling_balls_game.py:

1. # juggling_balls_game.py
2.

3. import turtle
4.

5. from juggling_ball import Ball


6.

7. WIDTH = 600
8. HEIGHT = 600
9.

10. window = turtle.Screen()


11. window.setup(WIDTH, HEIGHT)
12. window.tracer(0)
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 12/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

13.

14. ball = Ball(WIDTH, HEIGHT)


15.

16. while True:


17. ball.move()
18. window.update()
19.

20. turtle.done()

You test your code using a single ball for now and you call ball.move() within a
while True loop.

Recall that .move() is a method of the class Ball. In the class definition in
juggling_ball.py, you use the placeholder name self to refer to any
future instance of the class you create. However, now you’re creating an
instance of the class Ball, and you name it ball. Therefore, you can access
all the attributes using this instance’s name, for example by calling
ball.move().

Here’s the output from this script so far:

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 13/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

Note: the speed at which your while loop will run depends on your setup and
operating system. We’ll deal with this later in this project. However, if your ball is
moving too fast, you can slow it down by dividing its velocity by 10 or 100, say,
when you define self.velocity in the __init__() method. If you can’t see any ball
when you run this script, the ball may be moving so quickly out of the screen that
you need to slow it down significantly.

You can create a ball that moves upwards with a constant speed. The next step in
this 2D Python game is to account for gravity to pull the ball down.

3. Add Gravity to Pull Ball Downwards


Last time I checked, when you toss a ball upward, it will slow down, reach a point
when, it’s stationary in the air for the briefest of moments, and then starts falling
down towards the ground.

Let’s add the effect of gravity to the game. Gravity is a force that accelerates an
object. This acceleration is given in metres per second squared (m/s^2). The
acceleration due to the Earth’s gravity is 9.8m/s^2, and this is always a downward
acceleration. Therefore, when an object is moving upwards, gravity will decelerate
the object until its velocity is zero. Then it will start accelerating downwards.

In this project, we’re not using real-world units. So you can think of the
acceleration due to gravity as a value in pixels per frame squared. “Frame” is a unit
of time in this context, as it refers to the duration of the frame.

Acceleration is the rate of change of velocity. In the real world, we use the change
of velocity per second. However, in the game you’re using change of velocity per
frame. Later in this article, you’ll consider the time it takes for the while loop to run
so you can set the frame time.

You can add a class attribute to define the acceleration due to gravity and define a
method called .fall():

1. # juggling_ball.py
2.

3. import random
4. import turtle
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 14/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

5.

6. class Ball(turtle.Turtle):
7. max_velocity = 5
8. gravity = 0.07
9.

10. def __init__(self, width, height):


11. super().__init__()
12. self.width = width
13. self.height = height
14. self.shape("circle")
15. self.color(
16. random.random(),
17. random.random(),
18. random.random(),
19. )
20. self.penup()
21. self.setposition(
22. random.randint(-self.width // 2, self.width /
23. random.randint(-self.height // 2, self.height
24. )
25. self.setheading(90)
26. self.velocity = random.randint(1, self.max_veloci
27.

28. def move(self):


29. self.forward(self.velocity)
30. self.fall()
31.

32. def fall(self):


33. self.velocity -= self.gravity

The method .fall() changes the value of the data attribute .velocity by
subtracting the value stored in the class attribute .gravity from the current
.velocity. You also call self.fall() within .move() so that each time the ball
moves in a frame, it’s also pulled back by gravity. In this example, the value of
.gravity is 0.07. Recall that you’re measuring velocity in pixels per frame.
Therefore, gravity reduces the velocity by 0.07 pixels per frame in each frame.

You could merge the code in .fall() within .move(). However, creating separate
methods gives you more flexibility in the future. Let’s assume you want a version

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 15/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

of the game in which something that happens in the game suspends gravity.
Having separate methods will make future modifications easier.

You could also choose not to call self.fall() within .move() and call the method
directly within the game loop in juggling_balls_game.py.

You need to consider another issue now that the balls will be pulled down towards
the ground. At some point, the balls will leave the screen from the bottom edge.
Once this happens, you want the program to detect this so you can deal with this.
You can create another method is_below_lower_edge():

1. # juggling_ball.py
2.

3. import random
4. import turtle
5.

6. class Ball(turtle.Turtle):
7. max_velocity = 5
8. gravity = 0.07
9.

10. def __init__(self, width, height):


11. super().__init__()
12. self.width = width
13. self.height = height
14. self.shape("circle")
15. self.color(
16. random.random(),
17. random.random(),
18. random.random(),
19. )
20. self.penup()
21. self.setposition(
22. random.randint(-self.width // 2, self.width /
23. random.randint(-self.height // 2, self.height
24. )
25. self.setheading(90)
26. self.velocity = random.randint(1, self.max_veloci
27.

28. def move(self):


29. self.forward(self.velocity)
30 self fall()
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 16/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP
30. self.fall()
31.

32.

def fall(self):
33. self.velocity -= self.gravity
34.

35. def is_below_lower_edge(self):


36. if self.ycor() < -self.height // 2:
37. self.hideturtle()
38. return True
39. return False

The method .is_below_lower_edge() is an instance method which returns a


Boolean value. The method hides the turtle object and returns True if the ball has
dipped below the lower edge of the screen. Otherwise, it returns False.

Methods are functions. Therefore, like all functions, they always return a
value. You’ll often find methods such as .move() and .fall() that don’t
have an explicit return statement. These methods change the state of one
or more of the object’s attributes. These methods still return a value. As
with all functions that don’t have a return statement, these methods
return None.

The purpose of .is_below_lower_edge() is different. Although it’s also


changing the object’s state when it calls self.hideturtle(), its main
purpose is to return True or False to indicate whether the ball has dropped
below the lower edge of the screen.

It’s time to check whether gravity works. You don’t need to change
juggling_balls_game.py since the call to ball.move() in the while loop remains
the same. Here’s the result of running the script now:

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 17/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

You can see the ball is tossed up in the air. It slows down. Then it accelerates
downwards until it leaves the screen. You can also temporarily add the following
line in the while loop to check that .is_below_lower_edge() works:

1. print(ball.is_below_lower_edge())

Since this method returns True or False, you’ll see its decision printed out each
time the loop iterates.

This 2D Python game is shaping up nicely. Next, you need to add the option to bat
the ball upwards.

4. Add the Ability to Bat Ball Upwards


There are two steps you’ll need to code to bat a ball upwards:

Create a method in Ball to bat the ball upwards by adding positive (upward)
velocity
Call this method each time the player clicks somewhere close to the ball

You can start adding another class attribute .bat_velocity_change and the
method .bat_up() in Ball:
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 18/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

1. # juggling_ball.py
2.

3. import random
4. import turtle
5.

6. class Ball(turtle.Turtle):
7. max_velocity = 5
8. gravity = 0.07
9. bat_velocity_change = 8
10.

11. def __init__(self, width, height):


12. super().__init__()
13. self.width = width
14. self.height = height
15. self.shape("circle")
16. self.color(
17. random.random(),
18. random.random(),
19. random.random(),
20. )
21. self.penup()
22. self.setposition(
23. random.randint(-self.width // 2, self.width /
24. random.randint(-self.height // 2, self.height
25. )
26. self.setheading(90)
27. self.velocity = random.randint(1, self.max_veloci
28.

29. def move(self):


30. self.forward(self.velocity)
31. self.fall()
32.

33. def fall(self):


34. self.velocity -= self.gravity
35.

36. def is_below_lower_edge(self):


37. if self.ycor() < -self.height // 2:
38. self.hideturtle()
39. return True
40. return False
41.

42. def bat_up(self):


https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 19/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

43. self.velocity += self.bat_velocity_change

Each time you call the method .bat_up(), the velocity of the ball increases by the
value in the class attribute .bat_velocity_change.

If the ball’s velocity is, say, -10, then “batting it up” will increase the velocity to -2
since .bat_velocity_change is 8. This means the ball will keep falling but at a lower
speed.

Suppose the ball’s velocity is -3, then batting up changes this to 5 so the ball will
start moving upwards. And if the ball is already moving upwards when you bat it,
its upward speed will increase.

You can now shift your attention to juggling_balls_game.py. You need to make no
further significant changes to the class Ball itself.

In the game, you need to call the ball’s .bat_up() method when the player clicks
within a certain distance of the ball. You can use .onclick() from the turtle
module for this:

1. # juggling_balls_game.py
2.

3. import turtle
4.

5. from juggling_ball import Ball


6.

7. WIDTH = 600
8. HEIGHT = 600
9.

10. batting_tolerance = 40
11.

12. window = turtle.Screen()


13. window.setup(WIDTH, HEIGHT)
14. window.tracer(0)
15.

16. def click_ball(x, y):


17. if ball.distance(x, y) < batting_tolerance:
18. ball.bat_up()
19.

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 20/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

20. window.onclick(click_ball)
21.

22. ball = Ball(WIDTH, HEIGHT)


23.

24. while True:


25. ball.move()
26. window.update()
27.

28. turtle.done()

The variable batting_tolerance determines how close you need to be to the


centre of the ball for the batting to take effect.

You define the function click_ball(x, y) with two parameters representing xy-
coordinates. If the location of these coordinates is within the batting tolerance,
then the ball’s .bat_up() method is called.

The call to window.onclick(click_ball) calls the function click_ball() and passes


the xy-coordinates to it.

When you run this script, you’ll get the following output. You can test the code by
clicking close to the ball:

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 21/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

Now, juggling one ball is nice and easy. How about juggling many balls?

5. Create More Instances of Ball Using a Timer


You can make a few changes to juggling_balls_game.py to have balls appear
every few seconds. To achieve this, you’ll need to:

1. Create a list to store all the Ball instances


2. Create a new Ball instance every few seconds and add it to the list
3. Move each of the Ball instances in the while loop
4. Check all the Ball instances within click_ball() to see if the player clicked the
ball

Start by tackling the first two of these steps. You define a tuple named
spawn_interval_range. The program will create a new Ball every few seconds and
add it to the list balls. The code will choose a new time interval from the range set
in spawn_interval_range.

Since all the Ball instances are stored in the list balls, you’ll need to:

Add a for loop in the game loop so that all Ball instances move in each frame

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 22/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

Add a for loop in bat_up() to check all Ball instances for proximity to the click
coordinates

You can update the code with these changes:

1. # juggling_balls_game.py
2.

3. import turtle
4. import time
5. import random
6.

7. from juggling_ball import Ball


8.

9. WIDTH = 600
10. HEIGHT = 600
11.

12. batting_tolerance = 40
13. spawn_interval_range = (1, 5)
14.

15. window = turtle.Screen()


16. window.setup(WIDTH, HEIGHT)
17. window.tracer(0)
18.

19. def click_ball(x, y):


20. for ball in balls:
21. if ball.distance(x, y) < batting_tolerance:
22. ball.bat_up()
23.

24. window.onclick(click_ball)
25.

26. balls = []
27.

28. spawn_timer = time.time()


29. spawn_interval = 0
30. while True:
31. if time.time() - spawn_timer > spawn_interval:
32. balls.append(Ball(WIDTH, HEIGHT))
33. spawn_interval = random.randint(*spawn_interval_
34. spawn_timer = time.time()
35.

36. for ball in balls:


b ll ()
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 23/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP
37. ball.move()
38.

39. window.update()
40.

41. turtle.done()

You start with a spawn_interval of 0 so that a Ball is created in the first iteration of
the while loop. The instance of Ball is created and placed directly in the list balls.
Each time a ball is created, the code generates a new spawn_interval from the
range set at the top of the script.

You then loop through all the instances of Ball in balls to call their .move()
method. You use a similar loop in click_ball()

This is where we can see the benefit of OOP and classes for this type of
program. You create each ball using the same template: the class Ball.
This means all Ball instances have the same data attributes and can
access the same methods. However, the values of their data attributes can
be different.

Each ball starts at a different location and has a different colour. The
.velocity data attribute for each Ball has a different value, too. And
therefore, each ball will move independently of the others.

By creating all balls from the same template, you ensure they’re all similar.
But they’re not identical as they have different values for their data
attributes.

You can run the script to see a basic version of the game which you can test:

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 24/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

The program creates balls every few seconds, and you can click any of them to bat
them up. However, if you play this version long enough, you may notice some odd
behaviour. In the next section, you’ll see why this happens and how to fix it.

6. Add a Frame Rate to Control the Game Speed


Have a look at this video of balls created by the current script. In particular, look at
what happens to those balls that rise above the top edge of the screen. Does their
movement look realistic?

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 25/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

Did you notice how, when the ball leaves the top of the screen, it immediately
reappears at the same spot falling at high speed? It’s as though the ball is
bouncing off the top of the screen. However, it’s not meant to do this. This is
different from what you’d expect if the ball was still rising and falling normally
while it was out of sight.

Let’s first see why this happens. Then you’ll fix this problem.

How long does one iteration of the while loop take?

Earlier, we discussed how the ball’s velocity is currently a value in pixels per frame.
This means the ball will move by a certain number of pixels in each frame. You’re
using the time it takes for each frame of the game as the unit of time.

However, there’s a problem with this logic. There are no guarantees that each
frame takes the same amount of time. At the moment, the length of each frame is
determined by how long it takes for the program to run one iteration of the while
loop.

Look at the lines of code in the while loop. Which one do you think is the
bottleneck that’s taking up most of the time?

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 26/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

Let’s try a small experiment. To make this a fair test, you’ll initialise the random
number generator using a fixed seed so that the same “random” values are picked
each time you run the program.

Let’s time 500 iterations of the while loop:

1. # juggling_balls_game.py
2.

3. import turtle
4. import time
5. import random
6.

7. from juggling_ball import Ball


8.

9. random.seed(0)
10.

11. WIDTH = 600


12. HEIGHT = 600
13.

14. batting_tolerance = 40
15. spawn_interval_range = (1, 5)
16.

17. window = turtle.Screen()


18. window.setup(WIDTH, HEIGHT)
19. window.tracer(0)
20.

21. def click_ball(x, y):


22. for ball in balls:
23. if ball.distance(x, y) < batting_tolerance:
24. ball.bat_up()
25.

26. window.onclick(click_ball)
27.

28. balls = []
29.

30. spawn_timer = time.time()


31. spawn_interval = 0
32.

33. start = time.time()


34. count = 0
35. while count < 500:
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 27/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

36. count += 1
37. if time.time() - spawn_timer > spawn_interval:
38. balls.append(Ball(WIDTH, HEIGHT))
39. spawn_interval = random.randint(*spawn_interval_
40. spawn_timer = time.time()
41.

42. for ball in balls:


43. ball.move()
44.

45. window.update()
46.

47. print(time.time() - start)


48. turtle.done()

You run the while loop until count reaches 500 and print out the number of
seconds it takes. When I run this on my system, the output is:

1. 8.363317966461182

It took just over eight seconds to run 500 iterations of the loop.

Now, you can comment the line with window.update() at the end of the while loop.
This will prevent the turtles from being displayed on the screen. All the remaining
operations are still executed. The code now outputs the following:

1. 0.004825115203857422

The same 500 iterations of the while loop now take around 5ms to run. That’s
almost 2,000 times faster!

Updating the display on the screen is by far the slowest part of all the steps which
occur in the while loop. Therefore, if there’s only one ball on the screen and it
leaves the screen, the program no longer needs to display it. The loop speeds up
significantly. This is why using pixels per frame as the ball’s velocity is flawed. The
same pixels per frame value results in a much faster speed when the frame time is
a lot shorter!

And even if this extreme change in frame time wasn’t an issue, for example, if
you’re guaranteed to always have at least one ball on the screen at any one time,
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 28/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

you still have no control over how fast the game runs or whether the frame rate
will be constant as the game progresses.

How long do you want one iteration of the while loop to take?

To fix this problem and have more control over how long each frame takes, you
can set your desired frame rate and then make sure each iteration of the while
loop lasts for as long as needed. This is the fixed frame rate approach for running a
game and works fine as long as an iteration in the while loop performs all its
operations quicker than the frame time.

You can set the frame rate to 30 frames per second (fps), which is fine on most
computers. However, you can choose a slower frame rate if needed. A frame rate of
30fps means that each frame takes 1/30 seconds—that’s 0.0333 seconds per frame.

Now that you’ve set the time each frame of the game should take, you can time
how long the operations in the while loop take and add a delay at the end of the
loop for the remaining time. This ensures each frame lasts 1/30 seconds.

You can implement the fixed frame rate in juggling_balls_game.py:

1. # juggling_balls_game.py
2.

3. import turtle
4. import time
5. import random
6.

7. from juggling_ball import Ball


8.

9. WIDTH = 600
10. HEIGHT = 600
11.

12. frame_rate = 30
13. batting_tolerance = 40
14. spawn_interval_range = (1, 5)
15.

16. window = turtle.Screen()


17. window.setup(WIDTH, HEIGHT)
18. window.tracer(0)
19.

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 29/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

20. def click_ball(x, y):


21.
for ball in balls:
22. if ball.distance(x, y) < batting_tolerance:
23. ball.bat_up()
24.

25. window.onclick(click_ball)
26.

27. balls = []
28.

29. spawn_timer = time.time()


30. spawn_interval = 0
31. while True:
32. frame_start_time = time.time()
33. if time.time() - spawn_timer > spawn_interval:
34. balls.append(Ball(WIDTH, HEIGHT))
35. spawn_interval = random.randint(*spawn_interval_
36. spawn_timer = time.time()
37.

38. for ball in balls:


39. ball.move()
40.

41. window.update()
42.

43. loop_time = time.time() - frame_start_time


44. if loop_time < 1 / frame_rate:
45. time.sleep(1 / frame_rate - loop_time)
46.

47. turtle.done()

You start the frame timer at the beginning of the while loop. Once all frame
operations are complete, you assign the amount of time taken to the variable
loop_time. If this is less than the required frame time, you add a delay for the
remaining time.

When you run the script now, the game will run more smoothly as you have a
fixed frame rate. The velocity of the balls, measured in pixels per frame, is now a
consistent value since the frame time is fixed.

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 30/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

You’ve completed the main aspects of the game. However, you need to have a
challenge to turn this into a proper game. In the next section, you’ll add a timer
and an aim in the game.

7. Add a Timer and an End to the Game


To turn this into a game, you need to:

Add a timer
Keep track of how many balls are lost

You can start by adding a timer and displaying it in the title bar:

1. # juggling_balls_game.py
2.

3. import turtle
4. import time
5. import random
6.

7. from juggling_ball import Ball


8.

9. WIDTH = 600
10. HEIGHT = 600
11.

12. frame_rate = 30
13. batting_tolerance = 40
14. spawn_interval_range = (1, 5)
15.

16. window = turtle.Screen()


17. window.setup(WIDTH, HEIGHT)
18. window.tracer(0)
19.

20. def click_ball(x, y):


21. for ball in balls:
22. if ball.distance(x, y) < batting_tolerance:
23. ball.bat_up()
24.

25. window.onclick(click_ball)
26.

27. balls = []
28.
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 31/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP
28.

29. game_timer = time.time()


30.
spawn_timer = time.time()
31. spawn_interval = 0
32. while True:
33. frame_start_time = time.time()
34. if time.time() - spawn_timer > spawn_interval:
35. balls.append(Ball(WIDTH, HEIGHT))
36. spawn_interval = random.randint(*spawn_interval_
37. spawn_timer = time.time()
38.

39. for ball in balls:


40. ball.move()
41.

42. window.title(f"Time: {time.time() - game_timer:3.1f}


43. window.update()
44.

45. loop_time = time.time() - frame_start_time


46. if loop_time < 1 / frame_rate:
47. time.sleep(1 / frame_rate - loop_time)
48.

49. turtle.done()

You start the game timer just before the game loop, and you display the time
elapsed in the title bar in each frame of the game. The format specifier :3.1f in the
f-string sets the width of the float displayed to three characters and the number of
values after the decimal point to one.

Next, you can set the limit of balls you can lose before it’s ‘Game Over’! You must
check whether a ball has left the screen through the bottom edge. You’ll recall you
wrote the method .is_below_lower_edge() for the class Ball. This method returns
a Boolean. Therefore, you can use it directly within an if statement:

1. # juggling_balls_game.py
2.

3. import turtle
4. import time
5. import random
6.

7. from juggling_ball import Ball


https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 32/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

8.

9. WIDTH = 600
10. HEIGHT = 600
11.

12. frame_rate = 30
13. batting_tolerance = 40
14. spawn_interval_range = (1, 5)
15. balls_lost_limit = 10
16.

17. window = turtle.Screen()


18. window.setup(WIDTH, HEIGHT)
19. window.tracer(0)
20.

21. def click_ball(x, y):


22. for ball in balls:
23. if ball.distance(x, y) < batting_tolerance:
24. ball.bat_up()
25.

26. window.onclick(click_ball)
27.

28. balls = []
29.

30. game_timer = time.time()


31. spawn_timer = time.time()
32. spawn_interval = 0
33. balls_lost = 0
34. while balls_lost < balls_lost_limit:
35. frame_start_time = time.time()
36. if time.time() - spawn_timer > spawn_interval:
37. balls.append(Ball(WIDTH, HEIGHT))
38. spawn_interval = random.randint(*spawn_interval_
39. spawn_timer = time.time()
40.

41. for ball in balls:


42. ball.move()
43. if ball.is_below_lower_edge():
44. window.update()
45. balls.remove(ball)
46. turtle.turtles().remove(ball)
47. balls_lost += 1
48.

49 window title(
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 33/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP
49. window.title(
50. f"Time: {time.time() - game_timer:3.1f} | Balls l
51. )
52. window.update()
53.

54. loop_time = time.time() - frame_start_time


55. if loop_time < 1 / frame_rate:
56. time.sleep(1 / frame_rate - loop_time)
57.

58. turtle.done()

You check whether each instance of Ball has left the screen through the bottom
edge as soon as you move the ball. If the ball fell through the bottom of the screen:

You update the screen so that the ball is no longer displayed. Otherwise, you
may still see the top part of the ball at the bottom of the screen
You remove the ball from the list of all balls
You also need to remove the ball from the list of turtles kept by the turtle
module to ensure the objects you no longer need don’t stay in memory
You add 1 to the number of balls you’ve lost

You also show the number of balls lost by adding this value to the title bar along
with the amount of time elapsed. The while loop will stop iterating once you’ve
lost ten balls, which is the value of balls_lost_limit.

You now have a functioning game. But you can add some finishing touches to
make it better.

8. Complete the Game With Finishing Touches


When writing these types of games, the “finishing touches” can take as little or as
long as you want. You can always do more refining and further tweaks to make the
game look and feel better.

You’ll only make a few finishing touches in this article, but you can refine your
game further if you wish:

Change the background colour to dark grey


https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 34/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

Add a final screen to show the time taken in the game


Ensure the balls are not created too close to the sides of the screen

You can change the background colour using .bgcolor(), which is one of the
methods in the turtle module.

To add a final message on the screen, you can update the screen after the while
loop and use .write(), which is a method of the Turtle class:

1. # juggling_balls_game.py
2.

3. import turtle
4. import time
5. import random
6.

7. from juggling_ball import Ball


8.

9. # Game parameters
10. WIDTH = 600
11. HEIGHT = 600
12.

13. frame_rate = 30
14. batting_tolerance = 40
15. spawn_interval_range = (1, 5)
16. balls_lost_limit = 10
17.

18. # Setup window


19. window = turtle.Screen()
20. window.setup(WIDTH, HEIGHT)
21. window.bgcolor(0.15, 0.15, 0.15)
22. window.tracer(0)
23.

24. # Batting function


25. def click_ball(x, y):
26. for ball in balls:
27. if ball.distance(x, y) < batting_tolerance:
28. ball.bat_up()
29.

30. window.onclick(click_ball)
31.

32. balls = []
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 35/45
25/03/2023 18:39
[] Anatomy of a 2D Game using Python's turtle and OOP

33.

34. # Game loop


35.
game_timer = time.time()
36. spawn_timer = time.time()
37. spawn_interval = 0
38. balls_lost = 0
39. while balls_lost < balls_lost_limit:
40. frame_start_time = time.time()
41. # Spawn new ball
42. if time.time() - spawn_timer > spawn_interval:
43. balls.append(Ball(WIDTH, HEIGHT))
44. spawn_interval = random.randint(*spawn_interval_
45. spawn_timer = time.time()
46.

47. # Move balls


48. for ball in balls:
49. ball.move()
50. if ball.is_below_lower_edge():
51. window.update()
52. balls.remove(ball)
53. turtle.turtles().remove(ball)
54. balls_lost += 1
55.

56. # Update window title


57. window.title(
58. f"Time: {time.time() - game_timer:3.1f} | Balls l
59. )
60.

61. # Refresh screen


62. window.update()
63.

64. # Control frame rate


65. loop_time = time.time() - frame_start_time
66. if loop_time < 1 / frame_rate:
67. time.sleep(1 / frame_rate - loop_time)
68.

69. # Game over


70. final_time = time.time() - game_timer
71. # Hide balls
72. for ball in balls:
73. ball.hideturtle()
i d d t ()
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 36/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP
74. window.update()
75. # Show game over text
76. text = turtle.Turtle()
77. text.hideturtle()
78. text.color("white")
79. text.write(
80.
f"Game Over | Time taken: {final_time:2.1f}",
81. align="center",
82. font=("Courier", 20, "normal")
83. )
84. turtle.done()

After the while loop, when the game ends, you stop the game timer and clear all
the remaining balls from the screen. You create a Turtle object to write the final
message on the screen.

To add a border at the edge of the screen to make sure no balls are created to
close too the edge, you can go back to juggling_ball.py and modify the region
where a ball can be created:

1. # juggling_ball.py
2.

3. import random
4. import turtle
5.

6. class Ball(turtle.Turtle):
7. max_velocity = 5
8. gravity = 0.07
9. bat_velocity_change = 8
10.

11. def __init__(self, width, height):


12. super().__init__()
13. self.width = width
14. self.height = height
15. self.shape("circle")
16. self.color(
17. random.random(),
18. random.random(),
19. random.random(),
20. )
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 37/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP
21. self.penup()
22.
self.setposition(
23. random.randint(
24. (-self.width // 2) + 20,
25. (self.width // 2) - 20
26. ),
27. random.randint(
28. (-self.height // 2) + 20,
29. (self.height // 2) - 20
30. ),
31. )
32. self.setheading(90)
33. self.velocity = random.randint(1, self.max_veloci
34.

35. def move(self):


36. self.forward(self.velocity)
37. self.fall()
38.

39. def fall(self):


40. self.velocity -= self.gravity
41.

42. def is_below_lower_edge(self):


43. if self.ycor() < -self.height // 2:
44. self.hideturtle()
45. return True
46. return False
47.

48. def bat_up(self):


49. self.velocity += self.bat_velocity_change

And this completes the game! Unless you want to keep tweaking and adding
more features. Here’s what the game looks like now:

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 38/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

Final Words
In this project, you created a 2D Python game. You started by creating a class
called Ball, which inherits from turtle.Turtle. This means that you didn’t have to
start from scratch to create Ball. Instead, you built on top of an existing class.

Here’s a summary of the key stages when writing this game:

Create a class named Ball and set up what should happen when you create a
ball
Make the ball move forward
Add gravity to pull the ball downwards
Add the ability to bat the ball upwards
Create more balls, with each ball created after a certain time interval
Control the speed of the game by setting a frame rate
Add a timer and an end to the game
Add finishing touches to the game

You first create the class Ball and add data attributes and methods.
When you create an instance of Ball, the object you create already has all

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 39/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

the properties and functionality you need a ball to have.

Once you defined the class Ball, writing the game is simpler because
each Ball instance carries everything you need it to do with it.

And now, can you beat your high score in the game?

Get the latest blog updates


No spam promise. You’ll get an email when a new blog post is published

Email*

Submit

Create your own free form with HubSpot

Appendix
Here are the final versions of juggling_ball.py and juggling_balls_game.py:

juggling_ball.py

1. # juggling_ball.py
2.

3. import random
4. import turtle
5.

6. class Ball(turtle.Turtle):
7. """Create balls to use for juggling"""
8. max_velocity = 5
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 40/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

9.
gravity = 0.07
10. bat_velocity_change = 8
11.

12. def __init__(self, width, height):


13. super().__init__()
14. self.width = width
15. self.height = height
16. self.shape("circle")
17. self.color(
18. random.random(),
19. random.random(),
20. random.random(),
21. )
22. self.penup()
23. self.setposition(
24. random.randint(
25. (-self.width // 2) + 20,
26. (self.width // 2) - 20
27. ),
28. random.randint(
29. (-self.height // 2) + 20,
30. (self.height // 2) - 20
31. ),
32. )
33. self.setheading(90)
34. self.velocity = random.randint(1, self.max_veloci
35.

36. def __repr__(self):


37. return f"{type(self).__name__}({self.width}, {sel
38.

39. def move(self):


40. """Move the ball forward by the amount required i
41. self.forward(self.velocity)
42. self.fall()
43.

44. def fall(self):


45. """Take the effect of gravity into account"""
46. self.velocity -= self.gravity
47.

48. def is_below_lower_edge(self):


49. """
Ch k i bj t f ll th
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/
h th b tt 41/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP
50. Check is object fell through the bottom
51. :return: True if object fell through the bottom.
52. False if object is still above the botto
53. """
54.
if self.ycor() < -self.height // 2:
55. self.hideturtle()
56. return True
57. return False
58.

59. def bat_up(self):


60. """Bat the ball upwards by increasing its velocit
61. self.velocity += self.bat_velocity_change

juggling_balls_game.py

1. # juggling_balls_game.py
2.

3. import turtle
4. import time
5. import random
6.

7. from juggling_ball import Ball


8.

9. # Game parameters
10. WIDTH = 600
11. HEIGHT = 600
12.

13. frame_rate = 30
14. batting_tolerance = 40
15. spawn_interval_range = (1, 5)
16. balls_lost_limit = 10
17.

18. # Setup window


19. window = turtle.Screen()
20. window.setup(WIDTH, HEIGHT)
21. window.bgcolor(0.15, 0.15, 0.15)
22. window.tracer(0)
23.

24. # Batting function


25. def click_ball(x, y):
26. for ball in balls:
27. if ball.distance(x, y) < batting tolerance:
https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 42/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP
( , y) g_
28. ball.bat_up()
29.

30. window.onclick(click_ball)
31.

32. balls = []
33.

34. # Game loop


35. game_timer = time.time()
36. spawn_timer = time.time()
37. spawn_interval = 0
38. balls_lost = 0
39. while balls_lost < balls_lost_limit:
40. frame_start_time = time.time()
41. # Spawn new ball
42. if time.time() - spawn_timer > spawn_interval:
43. balls.append(Ball(WIDTH, HEIGHT))
44. spawn_interval = random.randint(*spawn_interval_
45. spawn_timer = time.time()
46.

47. # Move balls


48. for ball in balls:
49. ball.move()
50. if ball.is_below_lower_edge():
51. window.update()
52. balls.remove(ball)
53. turtle.turtles().remove(ball)
54. balls_lost += 1
55.

56. # Update window title


57. window.title(
58. f"Time: {time.time() - game_timer:3.1f} | Balls l
59. )
60.

61. # Refresh screen


62. window.update()
63.

64. # Control frame rate


65. loop_time = time.time() - frame_start_time
66. if loop_time < 1 / frame_rate:
67. time.sleep(1 / frame_rate - loop_time)
68.

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 43/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

69. # Game over


70. final_time = time.time() - game_timer
71. # Hide balls
72. for ball in balls:
73. ball.hideturtle()
74.
window.update()
75. # Show game over text
76. text = turtle.Turtle()
77. text.hideturtle()
78. text.color("white")
79. text.write(
80. f"Game Over | Time taken: {final_time:2.1f}",
81. align="center",
82. font=("Courier", 20, "normal")
83. )
84. turtle.done()

Posted in Beginners, Beyond Beginners, Turtle, Uncategorized


Tagged Beginners, Beyond Beginners, object-oriented programming, python, turtle

Published by Stephen Gruppetta

View all posts by Stephen Gruppetta

Previous Post
Understanding The Difference Between `is` and `==`
in Python: The £5 Note and a Trip to a Coffee Shop

Leave a Reply

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 44/45
25/03/2023 18:39 Anatomy of a 2D Game using Python's turtle and OOP

Enter your comment here...

All content on this website is copyright © Stephen Gruppetta unless listed otherwise, and may
not be used without the written permission of Stephen Gruppetta

https://thepythoncodingbook.com/2023/03/12/anatomy-2d-game-python-turtle-o/ 45/45

You might also like