Learn to Code via Tutorials on Repl.it

← Back to all posts
How to create a fairly basic game using Python w/ Turtle Graphics
minermaniac447 (192)

How to make a simple game with Python + Turtle


Hello! If you're reading this, I can only assume you want to learn about how to make a fairly basic game using Python with Turtle Graphics. If that's not the case, why are you here? Go do something else.
If you're still here, I'm going to assume you have some basic knowledge of Python (syntax, how to use variables, etc.) and some more in-depth of Turtle Graphics. If you don't, please at least read the first 4 chapters or so of this resource. If you need to find more about turtle functions (which you likely will), see these docs.
I know this is long, but if you want to do this right, it's VERY IMPORTANT to read through each section!!! If you miss even one thing, your code may not work!

I - Creating your repl & basic setup


OK, so the first thing you need to do is create a Python with Turtle Graphics Repl. If you already have an account, you can do this by clicking the big red plus button, click Search All Languages, searching for Python, and selecting Python (with Turtle). Alternatively, (and anyone can do it this way), you can just click on this link! Now, the next thing you should do is import the turtle graphics module. To import the turtle graphics module, include the following lines at the top of your code:

#Import statements
import turtle

It's as simple as that! The first line a comment - it is very useful in keeping your code organized and readable. The second line is called an import statement, and it does just what it sounds like it does.

II - Creating the window


Now that we have imported the turtle module, we can get started. To make a Python w/ Turtle repl work, we need to have something for the turtle to draw on. Now, technically this isn't necessary at this stage, it will be if you want to set a background color, and it will be for later things. To create a window, add a blank line, then the following code:

#Creating the window
window = turtle.Screen()

If you would like to have a background color for the window, also include this line after the following lines: window.bgcolor("#UVWXYZ"), where UVWXYZ is the hexadecimal color code of the color you want for the background color. (To get a hex color code, just Google 'color code picker'.) The hex color code has to be surrounded in quotes because it is a string and must be read as such. So, just to recap, your code should now look like this (I have included my own hex color code, feel free to use it or choose your own):

#Import statements
import turtle

#Creating the window
window = turtle.Screen()
window.bgcolor("#444444")

Now comes the fun part - you can press the green 'Run' button and see something visibly happen! Up in the top left corner of the right side of your screen (under the section labeled 'result'), you should see the word "center". If you choose a background color, you should also see that area turn that color. If this worked, great! Continue to step 3. If not - double-check you have entered the code correctly.

III - Creating the game box


Part 1 - Creating the turtle

Now, we're going to draw the area that the game will be played in. This will also define the grid we're going to use for the game. For this example, I'm going to be using a box size of 500x500 (-250 to 250 on each axis), with a grid size of 50. To do this, we need to do a few things. First, in a new section (separated from the previous sections by an empty line), we need to create a turtle to use to create the box. So, we're going to create a comment detailing what this section of code does, then we're going to create our first turtle. Let's add the following lines of code:

#Initializing turtles
outer = turtle.Turtle()

The second line, outer = turtle.Turtle(), creates a new turtle object and calls it outer. We will be doing a lot more of this later, so remember this.


Part 2 - Setting up the turtle

Now, we're going to create our play area. First of all, let's make a new section. The first line we're going to do is set outer's speed. To set the speed of a turtle, you use the function turtle.speed(x), where turtle is the name of the turtle object you're using and x is a number from 0 to 10. Speed increases from 1 to 10, and 0 is the maximum speed - going as fast as python can handle. This is what we want to use, because it will keep the user from having to wait as long to start. So, to set the speed of outer to the fastest possible, we're going to use outer.speed(0).
Next, let's set a border color and fill color for the area. This uses the turtle.color() function, which takes up to 2 arguments (the things in the parenthesis). The first is the pen color, or the color that it will draw with. The second is the fill color, which is exactly what it sounds like - the color that is filled in when using fill. For this example, let's use #000000 (black) for the pen color and #FFFFFF (white) for the fill color. So, underneath the outer.speed, we're going to have the following line: outer.color("#000000","#FFFFFF").


Part 3 - Drawing the area

DISCLAIMER: From this point on, I won't be explaining in as much detail how each command works, though I will continue giving examples. Please see the docs if you want more info.
The first thing to do when drawing the area of the game is to decide how big you want the area to be. In this example, I will be using 500x500, but you can easily change that. Since your turtle's pen defaults to down, if you want to move it without drawing, you have to send outer.up().
Next, you need to go to the coordinates of whatever point you want to be one of the corners of your area - the easiest way to get this is to have your turtle go to (x,y), where x is half of the width of your area and y is half of the height. Since our area is 500x500, use outer.goto(250,250). Next, do start being able to draw, we need to use outer.down(). To begin filling the play area, we need to add outer.begin_fill(), which will continue tracking the inner area of what you have drawn until you tell it to end the fill.
Now, we're finally ready to start drawing. While you can do this with individual turns and forwards, it's much better to use a loop. To make a loop that loops 4 times that will draw us a square of side length 500, we need to use this code:

for i in range(4):
  outer.rt(90)
  outer.fd(500)

This will tell the turtle to make a 90-degree right turn, then go forward 500 pixels 4 times, creating a square. Finally, we need to end the fill by sending outer.end_fill(). If you press run now, you should get a full square drawn and filled in. If it doesn't work, check your code against this:

#Making the outer border of the game
outer.speed(0)
outer.color("#000000","#FFFFFF")
outer.up()
outer.goto(250,250)
outer.down()
outer.begin_fill()
for z in range(4):
  outer.rt(90)
  outer.fd(500)
outer.end_fill()

IV - Hiding turtles


When you press run, you'll see it the play area, but you'll also see the turtles - the little arrows pointing towards the right. Well, if we're making a game, we can't have that! Let's hide those turtles so that they aren't visible to the user. To do this, we need to make a new section - preferably as close to the top as possible, while still below the section where you initialized the turtles. To hide a turtle, you use turtle.ht(), where turtle is the name of the turtle. So, let's quickly make a section and hide the outer turtle. Now, if you run it, you won't see the little arrow after the drawing is finished!

IV.V - Drawing a grid (Optional!)


Now, this whole section is OPTIONAL! You do NOT need to include this section, but I find it helpful especially when creating the game. Now that that's out of the way...


Part 1 - Creating an array

To create a grid for the game, we need to do a few things. First, we need to keep track of each gridline's position. We can do that fairly easily with an array. This array should go in its own section, since we're going to be using more arrays later. The increments and decrements from 0 for each value in the array depend on how big you want each grid box to be - for this example, I'm going to use 50x50. So, we're going to create an array - let's call it gridpos - by doing the following: gridpos = []. We now have an empty array. Now, to get the values for each grid position, we're going to go from the x position of the bottom left corner of your box - if you're using my numbers, it will be -250 - and add the side length of a grid square - in my case, 50 - to it. This is our first value. Continue until you reach the positive version of the first number. Mine looks like this: gridpos = [-200, -150, -100, -50, 0, 50, 100, 150, 200]. Now, we can start to draw it.


Part 2 - Drawing the grid

We're going to need another turtle for the grid, so let's call it... wait for it... grid. We're going to want to hide this turtle, so in the hiding turtles section, let's add a line to hide it. Then, set its speed to 0 and its color to whatever you like - for this example, I used #888888. Then, we're going to need to make a loop, and have it loop for 1 fewer times than the number of grid squares you want for the height or the length. So, if you want a 10x10 grid, then it needs to loop 9 times. Inside that loop, we want it to do the following: Go to the leftmost edge of the box and draw a horizontal line for each value in gridpos, and go to the bottommost edge of the box and draw a vertical line for each value in gridpos. The easiest way to do this is with a large loop. You'll want to loop as many times as items you have in gridpos - in my case, 9 times. Let's break this down into 2 parts - Horizontal Lines and Vertical Lines.


Part 2A - Horizontal lines

Since your turtle will be starting at (0,0), and we want it to draw a horizontal line at each y position in gridpos, we need to get it to go to each without drawing off the lines. We can do this by telling it to pick up the pen, then goto the far-left x value and the y value corresponding to the iterator's value in gridpos. This is a little hard to explain, so let me show it:

for p in range(9):
  grid.up()
  grid.goto(-250,gridpos[p])

What this will do is go to (-250, the value of the p'th item in gridpos), whatever that may be. From there, it's as simple as putting the pen down and going forward the side length of the area, then turning left 90 degrees. So, your current code for the grid should look like this (Initializing of the grid turtle and gridpos array not shown):

#Making the grid
grid.speed(0)
grid.color("#888888")
for p in range(9):
  grid.up()
  grid.goto(-250,gridpos[p])
  grid.down()
  grid.fd(500)
  grid.lt(90)

Part 2B - Vertical lines

Now, it's time to make the vertical lines. Luckily, this part is much easier since we have the first part. All you need to do is copy/paste the code from grid.up() to grid.fd(500) to immediately below itself, switch -250 and gridpos[p] in the grid.goto() statement, and switch the left turn to a right turn. Now, you should have this inside the loop:

  grid.up()
  grid.goto(-250,gridpos[p])
  grid.down()
  grid.fd(500)
  grid.lt(90)
  grid.up()
  grid.goto(gridpos[p],-250)
  grid.down()
  grid.fd(500)
  grid.rt(90)

If you run it now, you should see it create a complete grid in your play area.

V - Initializing the boxes


Now, the whole point of this game is to collect boxes - you can decide how many. In the example I used 7, but I only am going to detail 3 because it's a very simple, repetitive process to make more.
First, we're going to need to create a turtle for each box. Let's go back up to the turtle initialization section, and create 3 turtles - box1, box2, and box3 using x = turtle.Turtle() and hide them. Now, we probably want some margin between each box and the gridlines - for this example, I'm using a margin of 5 on each side. To make this easier, let's make 2 arrays - an xlist and a ylist. Each x value is going to be 5 above the 50 - so -45, 5, 55, 105, etc. Each y value is going to be 5 below the 50 - so -55, -5, 45, 95, etc. These arrays should go in the same section as the gridpos array, for organization's sake.
We also need to declare a whole ton of variables - 4 for each box - which will be used later. For each box, we need boxZx, boxZy, boxZgridx, and boxZgridy, where Z is the number of each box. We can do that just below the array initialization section. So, to make it simple, for box1 we can just use box1x = box1y = box1gridx = box1gridy = 0, so that each of those variables has been initialized but is equal to 0. This saves line space and some time.

VI - Placing the boxes


This is the part where things start getting repetitive. Each section to place a box is almost exactly the same as the last. First, make a new section for box1. First, we want to set its speed to 0 so that it draws as fast as possible. Then, we're going to set its color - in this example, I'm using #8B4513, because it's a nice brownish color. Then, we're going to go up to pick up the pen and go to a set coordinate. For now, let's just go to the top right grid square. To do this, we need to tell box1 to goto xlist[9],ylist[9]. Next, we're going to put down the pen and begin the fill. After that, we need to draw a square of side length 40, because our grid squares are side 50 and we have a margin of 5 on each side. This can be done with the same loop we used to draw the area, but instead of going forward 500 we go forward 40. Finally, we need to end the fill. If you run it now, you should see a brown box appear in the top right corner. That means it's worked! If it hasn't, see this code:

#Making the first box
box1.speed(0)
box1.color("#8B4513")
box1.up()
box1.goto(xlist[9],ylist[9])
box1.down()
box1.begin_fill()
for z in range(4):
  box1.fd(40)
  box1.rt(90)
box1.end_fill()

Now, you just need to do the same thing for boxes 2 and 3, but with a different value in the xlist[] and ylist[]. See what experimenting with it does to the position!

VII - Randomly placing the boxes


Part 1 - Basic randomization

Now, I know what you're thinking. "We already placed the boxes!", you'll say. "Why do we need to do another section on placing boxes?!" Well, you don't want to have it be the same position every single game, do you? That takes away some of the fun! Let's make a randomization aspect. First, up in the import section, we need to import random. This will let us use a very important function - random.randint(). Now, we need to create a new section above the drawing of box1 - let's do it directly below the grid section. This is where we're going to start using those variables we created. First, we need to assign box1gridx. So, we're going to use box1gridx = random.randint(0,9). This will tell box1gridx to become a random integer between 0 and 9, so that it encompasses the whole of the array. Then, do the same for box1gridy. Now, we need to assign box1x to a number in xlist[] based on box1gridx, and the same for box1y. This is simple, based on what we know about arrays. Just use box1x = xlist[box1gridx], and do the same for box1y. Now, just for debug purposes, let's print the x and y positions to the console using print(box1x,box1y). Finally, we need to change the goto positions when drawing box1 from xlist[9],ylist[9] to box1x,box1y. If you did the randomization right, it should look like this:

#Getting coordinates for box1
box1gridx = random.randint(0,9)
box1gridy = random.randint(0,9)
box1x = xlist[box1gridx]
box1y = ylist[box1gridy]
print(box1x,box1y)

If you run it now, it should go to a completely different spot each time.


Part 2 - Specialized randomization

Something you may see come up - very rarely, but it could happen - is multiple boxes in the same spot. Now, while this is technically OK, we don't want it because it limits the game's potential. So, for box2, we're going to use the same code as for box1 (slightly modified to apply to box2), but we're going to add something to it. We need to check to see if box2x is the same as box1x and if box2y is the same as box1y. If they are the same, then we need to give box2 a new position. We can do this by putting the whole of the randomization code for box2 in a while True: loop (There are better ways to do this, but this is a simple way - you do NOT want to run this until you have put a break into it), then putting in an if statement. Since we need to check if box2x is box1x and box2y is box1y, if (box2x == box1x and box2y == box1y): should do the trick. Note that we need to check for both, because just one being the same is OK. Then, under that if it doesn't really matter what we put, so I just put a print statement saying that the placement of this box failed. What is very important is the else. We need to make sure we have an else after the if that contains the line break. Otherwise, we will be stuck in this loop forever. So, before running, double check that you have this code for box2's randomization:

#Getting coordinates for box2
while True:
  box2gridx = random.randint(0,9)
  box2gridy = random.randint(0,9)
  box2x = xlist[box2gridx]
  box2y = ylist[box2gridy]
  print(box2x,box2y)
  if (box2x == box1x and box2y == box1y):
    print("Box 2 placement failed, retrying...")
  else:
    break

Finally, let's fix box2's goto so that it goes to box2x, box2y. Now, to do this for more boxes it's very similar, but you need to check the position against every single previous box's position. So, for box3, you would need the conditional (box3x == box1x and box3y == box1y) or (box3x == box2x and box3y == box2y). Fill in that randomization and its corresponding goto, and then press run. You should see all 3 boxes go to completely random spots each time!

VIII - Drawing the player


Now, it's time to draw our player. This will eventually be moving around on the grid and collecting the boxes, but for now let's just try and draw it. To make it simple and easily distinguishable from the boxes, we're going to choose a different color and shape. In this tutorial, I'm using a circle of diameter 40 and color #ABCDEF (a nice light blue.) To draw the circle, you could use turtle.circle(), but I'm going to use the more obscure turtle.dot(), which draws a dot based on the turtle's position as the center of the circle. Because of this, we need a new array called playerposlist. The values in this will be 25 away from each value in gridpos, because our grid boxes are 50x50. So, we'll start at -225, then -175, -125, etc.
Next, we need to actually create our player turtle and hide it, both in their respective sections. Now, we need to create 2 variables to track player's x position and y position in relation to the grid - playerx and playery. Let's set both of them to 4 for now.
Since we're going to need to draw the character a LOT, let's put the drawing code into a function - drawplayer(). Since this will be called multiple times, we want our first line to clear any previous drawings using player.clear(). Then, set the speed to 0 and have the turtle pick up the pen. Now, we're going to player.goto(playerposlist[playerx],playerposlist[playery]). Finally, we need a dot of radius 20 and color #ABCDEF. We can do this with player.dot(20,"#ABCDEF"). It should now look like this:

  player.clear()
  player.speed(0)
  player.up()
  player.goto(playerposlist[playerx],playerposlist[playery])
  player.dot(20,"#ABCDEF")

Now, just put a call to drawplayer() below the drawplayer() function. If you press play, the boxes should draw and the player should go to the spot that is 4 up and 4 left from the bottom left corner.

IX - Boxcheck


Now, if our player has spawned on or moves onto a box, we want it to be collected and disappear. To do that, we're going to need to create a variable tracking the number of boxes. Since we have 3 boxes in this example, boxes should equal 3. Now, it's time to create a function for checking the boxes called boxcheck. The very first line needs to declare that we are using the global versions of boxes and of each box's grid coordinate variables (boxZgridx, boxZgridy). Then, we just need to repeat 5 lines of code for each box (replacing Z with the proper box number):

  if playerx == boxZgridx and playery == boxZgridy:
   boxZ.clear()
   boxes -= 1
   boxZgridx = boxZgridy = -1

What this will do is check the player's position against the box's position, and if it's the same it will remove the box and decrement the number of boxes remaining, and then 'put' the box somewhere it can't be counted again. We need to put a call for boxcheck() in the beginning of the drawplayer() function.
After you have this code block for each box, we just need to check if boxes == 0, and if it does, then we need to call a new function:

X - win()


Now, to make our win function, we need one more global variable - haswon. Declare it to be 0. Then, create a new turtle called winner and hide it. When the player wins, we're going to tell them they won and move their player out of bounds so they stop moving. So, now create the win() function.
Inside this function, we need to access the global variables haswon, playerx, and playery. Then, we should print something to the console telling the player they have won - something like "You won!!". Then, we need an if to check if the win() function has already run. We do this by checking if haswon == 0.
Inside this if, we need to first set haswon to 1. This prevents it from running this section of the code again if win() is called again. Next, we need to set winner's speed to 0 and have it pick up the pen. Then, we're going to draw our 'YOU WIN!' message.
To do that, we're going to need an array called winlist[]. In it, we need to have one string for each character (we can ignore the leading or ending spaces). Since we want to say 'YOU WIN!', we need 8 strings, each with a character from that in it (so "Y", "O", "U", etc.). Then, we will need a loop that loops 8 times, and each iteration will draw one character. For my loop, I used o as the iterator.
Now, let's create the code to draw each letter. We're going to have winner goto xlist[o+1] (because it's 8 characters on a 10 square grid) for the x position, and xlist[5] for the y position. Then, we need to draw each character using winner.write(). This function recieves 4 arguments - what to write (winlist[o]), whether or not to move (leave this on False), the alignment of the text (align="left"), and the font information (font=("Arial",40,"Normal")). So, the final write command should look like this:

winner.write(winlist[o], False, align="left", font=("Arial",40,"

Finally, out of the loop, we need to set playerx and playery to 10 and redraw the player. Now, if you run this, if you won, you'd see that Y and W are not centered. The simple fix to this is to put the winner.goto() statement inside an else, and have 2 if statements before: one if o == 0:, and one if o == 4:. Inside the first, we're going to use winner.goto(xlist[o+1]+2,xlist[5]), and inside the second, we're going to use winner.goto(xlist[o+1]-5,xlist[5]). Now, if you run it and won, you'd see that the Y and W ARE centered. However, we should probably make it so that you can actually... play the game.

XI - User input


To get user input, we're going to need to create 4 basic functions. Each one will be very similar, except for a few number changes. The first is up(). This will need to declare the global playery. Then, you just have a simple if: if playery < 9:, which will check to make sure the player isn't at the top of the map. Inside the if, we just need to add one to playery and call drawplayer() again.
The same will be done for all three other methods, with slight changes. down() will still global playery, but will instead check if playery > 0 and playery < 10:. This is because the player gets put at playerx and playery position (10,10) at the end of the game, and we want them to no longer be able to move. Inside the if, we're going to decrement instead of increment the playery value, and we're still going to drawplayer().
For left(), we need to global playerx instead of playery, and the if will reflect this change. In fact, left() is the same as down() except you have to replace each occurance of playery with playerx and the name is changed. The same can be done for right(), where you can just copy up() and replace every occurance with playery with playerx and change the name.
After you have done this, you still need a way to intercept the user input. We need to add keylisteners for 8 keys: Up, Down, Left, Right, W, A, S, and D. This will be done using the window.onkey() function, which takes 2 parameters: the function to call, and the key to listen for. The latter has to be passed to the function as a string. So, we're going to have 8 different onkey statements. To save time explaining, I will just paste them here:

window.onkey(up, "Up")
window.onkey(up, "W")
window.onkey(down, "Down")
window.onkey(down, "S")
window.onkey(left, "Left")
window.onkey(left, "A")
window.onkey(right, "Right")
window.onkey(right, "D")

This will call the appropriate function for each keypress. Finally, to make this work, we just need to add 2 lines to the bottom of the code: window.listen() and windown.mainloop().

XII - Conclusion

And with that, you should be done! If you press play, you should now have a functioning game. Now, if you want to, you can apply the principle learned in X - win() to add in an introduction to the game, like in my example, but that is not necesary. If you enjoyed this tutorial, be sure to upvote it. If you want to make a fairly simple random spiral generator with python, go here. If you want to check out my crash course on LOLCODE, go and click on this cat head (or here) 🐱

Commentshotnewtop
Robin_Andrews (0)

Great stuff. I like the idea of seeing what can be done with Turtle alone without more complex modules.

PhatMinh1 (0)

nice, very thank you !