Published on 2/10/2022 by
NevuloPython is a beginner-friendly language that makes it easy to dive straight into making your own games with a graphical user interface (GUI). In this guide, we’ll walk through the basics of setting up a Python installation, creating a window to display our game, and updating the window with a fully functional snake game.
If you haven’t already installed Python, you can
. Click “Download Python (version)” and go through the installation process.Having an IDE is helpful for catching errors and knowing when your syntax is wrong quickly, but you can always program using a normal text editor, like Notepad or TextEdit.
Open it up and save the file with the .py
extension, this means it’s a Python script which we’ll be able to run from the command line later.
We want our snake game to appear in a window that the user can interact with, like any other application. To achieve that, we’ll need to install a dependency.
A dependency is code from another location that we import into our code. It’s called a dependency because our code depends on the other person’s code to work.
We’ll need to install the pygame
dependency to make our game, and we can install it by using pip
on the command line, which comes with the Python installation. pygame
allows us to quickly develop a game and handles all the complicated logic for us.
pygame
dependencyOpen a command prompt and type in pip install pygame
, wait a little and pygame
should be available to use in your code.
We can import
it into our code:
1import pygame
Great! We’ve got pygame
in our code. Now what? Well, we want to show a window to display our snake game, how can we do that with pygame
?
pygame
has a few “helper methods” for things like modifying the display window, events, etc. Inside pygame.display
is a set_mode
function which tells the computer to initialise a new window to be displayed on the screen.
1import pygame23screen_width = 7204screen_height = 4805screen = pygame.display.set_mode((screen_width, screen_height))
We set a screen_width
variable to equal 720
and screen_height
to equal 480
to use in set_mode
, so we can initialise a window that is 720×480.
In the code above, we’re saying “let the variable screen
equal the return value of set_mode
”, which is a Surface
object.
Now that screen
is a Surface
object (which represents the contents of the window), we're able to do things like change the background colour:
1screen.fill((0, 0, 0)) # 0, 0, 0 is RGB code for the colour black (0 red, 0 green, 0 blue)
We need to tell pygame.display
to update whenever we make any changes we make to the screen
, like changing the background colour. This is done by calling pygame.display.flip
:
1pygame.display.flip()
There’s just one last thing before we’re past the first hurdle of bringing up a window properly: the event loop. What is it?
The event loop represents any actions or things that are happening in your game, including when the user quits your app.
1snake_speed = 302clock = pygame.time.Clock()3running = True4# While "running" is true5# (always true unless user quits):6while running:7 screen.fill((0,0,0))89 pygame.display.flip()10 clock.tick(snake_speed)1112 # Get the next events from the queue13 # For each event returned from get(),14 for event in pygame.event.get():15 # If the event is "QUIT"16 if event.type == pygame.QUIT:17 # Set running to False, stop event loop18 running = False
You can think of executing pygame.event.get()
like walking the next “step” your game will take. If you don’t have pygame.event.get()
running in a loop, your game won’t really work. It’s also important that we move any updates we make to the screen inside the loop, so we’re constantly updating the contents of the screen on each frame.
The clock
variable allows us to specify how fast our game should update. This is not super necessary, however if you find that the snake moves too fast you can make snake_speed
a smaller value. You can also increase it to make the game more challenging!
If we run the script as it is now, we should see a little window with a black background pop up on the screen!
This is all it is now, but it will become a functional snake game 🐍
Okay, we’re done getting excited about windows now. Let’s get a snake in the damn window.
To draw graphics on the screen like a rectangle for our snake, we’ll need to call pygame.draw.rect
, passing in the following as arguments:
screen
variable in our case)blue
with the HEX value for the colour blue)200
& 150
represent the X and Y coordinates for where to display the rectangle10
by 10
pixels.1blue = (0, 0, 255) # RGB code for blue2pygame.draw.rect(screen, blue, [200, 150, 10, 10])
Include this call before the line where you update your screen, run your program again, and you should see a small blue square near the top left of your screen!
To move the rectangle that’s being drawn to the screen, we need to do three things:
Storing the current position of the snake
1# Set the snake in the middle of the screen2snake_x = screen_width / 23snake_y = screen_height / 2
Storing the current speed of the snake, so we know where (and how much) to move the snake on the next frame
1speed_x = 02speed_y = 2
These first two things should be defined at the top of your script after importing pygame
, so we can use them later on.
1# Handling key events2for event in pygame.event.get():3 # If the event is "KEYDOWN"4 if event.type == pygame.KEYDOWN:5 # Go up on arrow key UP6 if event.key == pygame.K_UP:7 speed_x = 08 speed_y = -29 # Go down on arrow key DOWN10 if event.key == pygame.K_DOWN:11 speed_x = 012 speed_y = 213 # Go left on arrow key LEFT14 if event.key == pygame.K_LEFT:15 speed_y = 016 speed_x = -217 # Go right on arrow key RIGHT18 if event.key == pygame.K_RIGHT:19 speed_y = 020 speed_x = 2
To make the snake move when we press one of the arrow keys, we need to update our event loop to listen for a specific event.type
(KEYDOWN, a key being pressed down) and also the type of key being pressed to know how to change the snake's speed.
With all this, we can now update the speed of the snake on every frame to get our snake moving!
1# Update the speed of the snake2snake_x += speed_x3snake_y += speed_y
It should look something like this so far:
1import pygame23screen_width = 7204screen_height = 4805screen = pygame.display.set_mode((screen_width, screen_height))67# Set the snake in the middle of the screen8snake_x = screen_width / 29snake_y = screen_height / 210speed_x = 011speed_y = -21213running = True14while running:15 screen.fill((255, 255, 255)16 pygame.draw.rect(screen, blue, [snake_x, snake_y, 10, 10])17 # Update the snake's X & Y position each frame based18 # on speed19 snake_x += speed_x20 snake_y += speed_y21 pygame.display.flip()2223 for event in pygame.event.get():24 # If the event is "KEYDOWN"25 if event.type == pygame.KEYDOWN:26 # Movement (up, down, left, right arrow keys)27 if event.key == pygame.K_UP:28 speed_x = 029 speed_y = -230 if event.key == pygame.K_DOWN:31 speed_x = 032 speed_y = 233 if event.key == pygame.K_LEFT:34 speed_y = 035 speed_x = -236 if event.key == pygame.K_RIGHT:37 speed_y = 038 speed_x = 239 if event.type == pygame.QUIT:40 # Set running to False, stop event loop41 running = False
We need to define the position for the fruit near the top of our file, after our imports:
1fruit_x = 3002fruit_y = 4003red = (255, 0, 0)
We’ve also defined a variable for the colour red, which we’ll use to draw the fruit.
To draw the fruit on the screen, draw another rectangle on the screen each frame where the fruit will be located: this code will run in your game loop
1pygame.draw.rect(screen, red, [fruit_x, fruit_y, 10, 10])
Now that we have fruit spawning on the map, we want to do two things to replicate snake:
Let’s knock the first thing out of the way: spawning the fruit in a new place when the snake goes over it. How can we do this? Well, we know the position of the snake at all times, as well as the position of the fruit.
In this case, it’s as simple as checking for if the x
and y
position match for both the fruit and the snake:
1# If the snake is touching fruit (x and y position match for snake head and fruit), set the fruit to a new, random position2if snake_x == fruit_x and snake_y == fruit_y:3 # ⚠️ Make sure to "import random" at the top of your file!4 fruit_x = round(random.randrange(0, screen_width - 10) / 10.0) * 10.05 fruit_y = round(random.randrange(0, screen_height - 10) / 10.0) * 10.0
This logic is saying “pick a random new location for the fruit’s x
position, making sure it’s within the left and right side of the screen”. The same logic applies for the y
position of the fruit.
We’ll need to add import random
at the very top of our file for this code to work, since we’re using the random
module to generate a random range to spawn the new fruit.
When we eat fruit, we want to increment the length of the snake by 1.
We’ll need some place to store the current length of the snake (put this near the top of your file):
1snake_length = 1
We’re also going to need to change how we update the snake on the screen, currently its only drawing one rectangle each frame, but we wish to draw one new rectangle as the value of snake_length
goes up.
Define a new variable called snake_blocks
as an empty list at the top of your file to track all the rectangles we’ll have to draw for each point the user has:
1snake_blocks = []
Now we just need some way to keep appending new blocks to the end of the snake
At the top of your game loop, add this logic to continuously push the snake's current x
and y
position (as a list) to snake_blocks
1# Set the snake head to the current position, append to snake blocks to keep track2snake_head = []3snake_head.append(snake_x)4snake_head.append(snake_y)5snake_blocks.append(snake_head)
But since this is running in a loop, it will just keep adding more and more blocks to snake_blocks
forever. If the user has eaten 7 fruits, the snake should have 7 blocks (plus one for the snake head).
So, inside the game loop, we want to check if the length of the snake_blocks
we have stored is greater than the snake_length
, and remove the first block if so. This will stop our snake from infinitely growing!
1# Ensure the snake is only as big as the length we've set, delete the end blocks2if len(snake_blocks) > snake_length:3 del snake_blocks[0]
Replace the draw.rect
call to update your snake with the code below to loop through each of the snake blocks and draw a rectangle on the screen.
1# Draw a snake block for each point the user has2for block in snake_blocks:3 pygame.draw.rect(screen, blue, [block[0], block[1], snake_size, snake_size])
The value of block
in this for
loop will be a list, the first element (block[0]
) represents the x
position for that block, and the second element (block[1]
) represents the y
position.
Lastly, we want to add 1
to snake_length
when we pass over fruit, so go to the code we added where we’re checking for the x
and y
position of the snake and fruit being the same, and add a line to let snake_length
equal itself plus 1
:
1if snake_x == fruit_x and snake_y == fruit_y:2 snake_length += 13 fruit_x = round(random.randrange(0, screen_width - 10) / 10.0) * 10.04 fruit_y = round(random.randrange(0, screen_height - 10) / 10.0) * 10.0
By this point, our snake game is working great. One thing I haven’t touched on though is the loss condition. Typically, in snake, the game ends when you:
A good place to start would be to define a boolean variable (true or false) for the game over state:
1game_over = False # put at the top of your file after imports
When the snake goes past the edge of the screen, we want to set game_over
to True
:
1# If the snake goes beyond the left or right side of the screen,2if (snake_x >= screen_width or snake_x < 0 or3 # If the snake goes beyond the top of bottom of the screen,4 snake_y >= screen_height or snake_y < 0):5 # Set game over to true6 game_over = True
Here, we’re checking if the x
position of the snake is greater than or equal to the width of the screen OR if the x
position is less than 0.
What does this mean in practice? Since we’ve defined the screen_width
as 720
, if our snake goes too far right on the screen making the snake_x
variable go past 720
(or too far left, making it go below 0
), we know the snake is on or past the edge of the screen, meaning it’s game over.
The same checks above apply for the y
coordinate too (up and down movement), so if the snake goes above the top of the screen or below the bottom of the screen, game_over
will be set to True
.
We also wanted to end the game if the snake’s head touches its tail.
1# Not counting the snake head, check if any other existing snake blocks are crossing over the snake head (dead)2for x in snake_blocks[:-1]:3 if x == snake_head:4 game_over = True
The special [:-1]
after snake_blocks
is a “slice”, and this specific slice means “get all the snake blocks except the last one”.
Slice notation in Python looks like this: x[start:stop]
, and in our code above we’re omitting the start
value (which defaults to 0
) by just putting a colon (:
). Next is the stop
value which says at what index to end the slice, in this case it’s -1
, so what does this mean?
In slice notation, negative indices will roll over to count from the end of the array instead of the start.
So, if we had a snake with a length of 5
, snake_blocks[:-1]
in our case would start at 0
(like it normally does), and end at whatever the length of snake_blocks
is minus 1
.
In practice, this prevents an issue where game_over
will constantly be True
because the snake head position will always equal itself.
Excluding the pygame.display.flip
call, the clock.tick
call and the event for
loop, move all the logic in your game so far into an if condition (if the game has not ended):
1# If the user hasn't lost the game:2if not game_over:3 screen.fill((255,255,255))4 # ...5else:
Our game over screen logic will be inside the else
statement. Let’s start with a blue screen if the game is over:
1# Game over logic (screen showing users score + how to continue)2else:3 screen.fill(blue)
So now, when the user hits the edge of the screen or runs into their tail, the screen will turn blue. Let’s add some text to this screen to let the user know what their score was.
To add fonts and text into our game, we need to define the font we want to use in a variable at the top of the file, after the import
s:
1font = pygame.font.SysFont('Arial', 30)
Then, we can call font.render
with the text we wish to render on the screen. The second argument (True
) is for anti-aliasing the font (you likely want to leave this as True
to make the font smooth), and the last argument is what colour the text should be.
1score = font.render('You scored ' + str(snake_length), True, black)
Something to keep in mind though is that font.render
won’t actually make any text appear on the screen just yet; we’ll need to save the result of font.render
to a variable which we can use to apply the text on the screen in a certain position using screen.blit
. blit
is a confusing name (
1screen.blit(score, (10, screen_height / 2 - 100))
The second argument is where we want the text to appear on the screen – the x
and y
coordinates. In this case, we’re saying we want the score to appear 10 pixels from the left side of the screen and halfway down the screen, minus 100 pixels, just so it appears a bit higher than halfway.
In your event for
loop, inside the check for KEYDOWN
, let’s add a check to see if the spacebar has been pressed:
1for event in pygame.event.get():2 # If the event is "KEYDOWN"3 if event.type == pygame.KEYDOWN:4 # If space is pressed, reset game5 if event.key == pygame.K_SPACE:6 game_over = False7 snake_x = screen_width / 28 snake_y = screen_height / 29 snake_blocks = []10 snake_length = 1
The last 4 lines are just resetting variables to their default value, so when the user restarts the game, they start out in the middle of the screen and have no length on the snake.
Using what we learned from earlier about drawing text on the screen, let’s also draw some instructions when the game is over, so the user knows how to restart:
1# Game over logic (screen showing users score + how to continue)2else:3 screen.fill(blue)4 score = font.render('You scored ' + str(snake_length), True, black)5 screen.blit(score, (10, screen_height / 2 - 100))6 text = font.render('You lost! Press \'Q\' to quit, or Spacebar to play again', False, black)7 screen.blit(text, (10, screen_height / 2))
I’ve done some minor clean-up as well as adding additional comments to explain some logic, but this is all the code to get a fully functional snake game with things like score tracking, eating fruits and a game over state.
1import pygame2import random34### Initialization5pygame.init() # Not necessary6font = pygame.font.SysFont('Arial', 30)78# Colours9blue = (0, 0, 255) # hex code for blue10black = (0, 0, 0) # hex code for black11red = (255, 0, 0) # hex code for red12screen_width = 72013screen_height = 48014screen = pygame.display.set_mode((screen_width, screen_height))1516# Set the snake in the middle of the screen17snake_x = screen_width / 218snake_y = screen_height / 219snake_speed = 3020snake_size = 1021snake_length = 122snake_blocks = []2324fruit_x = 30025fruit_y = 4002627speed_x = 028speed_y = 102930game_over = False3132running = True33clock = pygame.time.Clock()3435# While "running" is true (always true unless user quits):36while running:37 # If the user hasn't lost the game:38 if not game_over:39 screen.fill((255,255,255)) # 255, 255, 255 is hexadecimal for the colour black4041 # Set the snake head to the current position, append to snake blocks to42 # keep track43 snake_head = []44 snake_head.append(snake_x)45 snake_head.append(snake_y)46 snake_blocks.append(snake_head)4748 # Ensure the snake is only as big as the length we've set49 if len(snake_blocks) > snake_length:50 del snake_blocks[0]5152 # Not counting the last block, check if any other existing snake53 # blocks are crossing over the snake head (dead)54 for x in snake_blocks[:-1]:55 if x == snake_head:56 game_over = True5758 # Draw a snake block for each point the user has59 for block in snake_blocks:60 pygame.draw.rect(screen, blue, [block[0], block[1], snake_size, snake_size])61 pygame.draw.rect(screen, red, [fruit_x, fruit_y, snake_size, snake_size])6263 # Update the speed of the snake64 snake_x += speed_x65 snake_y += speed_y6667 # If the snake is touching fruit (x and y position match for snake head and68 # fruit), set the fruit to a new, random position and update snake length69 if snake_x == fruit_x and snake_y == fruit_y:70 fruit_x = round(random.randrange(0, screen_width - snake_size) / 10.0) * 10.071 fruit_y = round(random.randrange(0, screen_height - snake_size) / 10.0) * 10.072 snake_length += 17374 # If the snake goes beyond the left or right side of the screen,75 if (snake_x >= screen_width or snake_x < 0 or76 # if the snake goes beyond the top of bottom of the screen,77 snake_y >= screen_height or snake_y < 0):78 # Set game over to true79 game_over = True8081 # Game over logic (screen showing users score + how to continue)82 else:83 screen.fill(blue)84 score = font.render('You scored ' + str(snake_length), False, black)85 screen.blit(score, (10, screen_height / 2 - 100))86 text = font.render('You lost! Press \'Q\' to quit, or Spacebar to play again', False, black)87 screen.blit(text, (10, screen_height / 2))8889 # Update the screen90 pygame.display.flip()91 clock.tick(snake_speed)9293 ### Event Loop94 # Get the next events from the queue95 # For each event returned from get(),96 for event in pygame.event.get():97 # If the event is "KEYDOWN"98 if event.type == pygame.KEYDOWN:99 # If "Q" is pressed, stop game100 if event.key == pygame.K_q:101 running = False102 # If space is pressed, reset game103 if event.key == pygame.K_SPACE:104 game_over = False105 snake_x = screen_width / 2106 snake_y = screen_height / 2107 snake_blocks = []108 snake_length = 1109 # Movement (up, down, left, right arrow keys)110 if event.key == pygame.K_UP:111 speed_x = 0112 speed_y = -10113 if event.key == pygame.K_DOWN:114 speed_x = 0115 speed_y = 10116 if event.key == pygame.K_LEFT:117 speed_y = 0118 speed_x = -10119 if event.key == pygame.K_RIGHT:120 speed_y = 0121 speed_x = 10122 # If the event is "QUIT" (when user clicks X on window)123 if event.type == pygame.QUIT:124 # Set running to False, stop event loop125 running = False
Using pygame
in Python is an effective way to quickly prototype game ideas and develop simple 2D games without much effort.
Now that you know how to do basic things like drawing rectangles and capturing keyboard events, the possibilities are endless! With enough time and practice, you could make a side-scroller, a top-down shooter, or countless other fun, simple games.
If you’re looking for more fun things to add to your game, take a look at the
. It’s a great resource to continue learning about how to extend your game to do even more, and about making games in general. If you need clarification on any of the topics covered in this article, the gives a good run down.Subscribe to the Nevuletter so you never miss new posts.
Comments
• 0
You need to be signed in to comment