DEV Community

loading...
Cover image for Getting Started With Pygame, Making a snake game

Getting Started With Pygame, Making a snake game

grapejuice profile image Grape Juice ・20 min read

Intro and Setup

Yeah, I know, another snake game... But I wanted to introduce you to pygame, a fun little module capable enough to make simple and fun 2d games in python, so here I am ...

To get started, you must have python and pip installed on your system,
You can get more info at here
To verify that python is installed, on the terminal, run

# check if python is installed
python --version

#check if pip is installed
pip --version
Enter fullscreen mode Exit fullscreen mode

If you get something in return that means you are successful ...
Now it's time to install pygame, just run

pip install pygame
Enter fullscreen mode Exit fullscreen mode

If you faced any trouble, you can check this page

Alternatively, if you don't wanna go through all this setup, you can use repl.it , it has an online pygame compiler that you can use
check it out here

Getting Started

We can finally start writing some code, You can use any favorite ide of yours ... vscode, sublime, atom,pycharm, brackets, etc ... It doesn't matter ...

just make a new python file in any directory and name it anything you want ...
The first job is going to be to import pygame and initialize our game window

import sys,random
import pygame 

# initialize pygame 
pygame.init()

# constants that store our screen width and height ,
# they are in pixels
WIN_X = 800
WIN_Y = 600

# setting up our game window in pygame with 
# our screen width and height constants as tuple
WIN = pygame.display.set_mode((WIN_X,WIN_Y))

# setting the caption of our game
# or more precisely the label that
# you will see on the game window

pygame.display.set_caption('snake game')
Enter fullscreen mode Exit fullscreen mode

Let's discuss the code above,
First, we import pygame and some other local modules, then use the init method on pygame to start our game or start pygame
We make two variables or constants, WIN_X and WIN_Y to store our screen dimensions or our screen width and screen height,
The reason we need this is that pygame creates a game window and we need to declare the width and height of it, you can choose any width and height, I am choosing 800x600
Next, we set up our game window, or tell pygame to initialize our game window with pygame.display.set_mode() method passing in our screen dimensions in a tuple
In the end, I just set up a caption or label for the game window using pygame.display.set_mode() method passing in the caption string, you can pass any string you want here ...

Now if you run this code, you will see the game screen for a short time, and then it exits the program ...
Can you guess why this happens, well cause we didn't define any game loop ...
A game loop is just an infinite loop, that goes on forever until the user exits or closes it ... As we didn't define any game loop, the window was visible for a moment, but then the program ended, and so the game screen was also gone...
Let's go ahead and implement our game loop

#importing modules
import sys,random
import pygame 

pygame.init()

WIN_X = 800
WIN_Y = 600

WIN = pygame.display.set_mode((WIN_X,WIN_Y))

pygame.display.set_caption('snake game')

#initializing pygame.time.Clock() which controls
#The FPS or frame per second or more explicitly
#For how many times per second our game loop 
#below should run
CLOCK = pygame.time.Clock()

#our infinite game loop
while 1:
   #checking for events in pygame.event.get() which
   #returns the event that occurs like 
   #any key presses or user clicking the
   #exit , maximize , minimize button
   for event in pygame.event.get():
        #here we are checking if user has clicked the exit button
        #by checking if the event.type is pygame.QUIT 
        #which is predefined in pygame as the exit button
        if event.type == pygame.QUIT:
            #if user clicked the exit button , quit the game
            #and exit out of the program
            pygame.quit()
            sys.exit()

        #using CLOCK variable mentioned above and using
        #tick method on it passing in the fps
        #this means that this loop will run 
        #25 times per second, 
        #feel free to change the value
        CLOCK.tick(25)
Enter fullscreen mode Exit fullscreen mode

Alt Text

Now if you run the program, the window is going to stay until you press the exit button, congrats if you could follow up on this ...
In the above, I just wrote a simple infinite while loop, then I wrote another for loop which checks for events like mouse-clicking, hover and others, the method pygame.event.get() returns the events that are occurring currently, there, I am checking if event.type is equal to the event pygame.QUIT or in other words did the user click the cross button, if that's true, quit the program, else continue to run ...
You also might have seen the CLOCK variable I declared, it just initializes pygame.time.Clock() and using it, in the while loop, I used CLOCK.tick(25), which just tells pygame to run the while loop 25 times per second, you are more than welcome to change the number ...

Planning Core Game logic

Now it's time to plan our core game logic, so we will have these 3 functions, main, main_menu, game_over, they represent different scenes of our game, for now, we will only focus on the main function or the main game logic ...
So first of all, how we will render and move our snake, that's the first and most obvious thing to do ...
Well, our snake is going to be a square, I mean a collection of squares, and whenever he can eat any food, the number of squares in the snake's body will increase, now how we will store these squares, and how we will track the position of the square ...
To store the squares of the body of the snake, the data structure that comes into my mind first is a list, so we can create a list of the snake body, and for each element of that list, we will render a square-like shape... Now the squares itself are going to be a list, cause in pygame, to draw a rectangle or square, we will need it's x coordinate,y coordinate and it's the width and it's height, Now for the snake position, we could introduce another list which will contain the snakes starting x and y coordinate, let's say it's [100,50], And then we will increment or decrement it by the snake's speed in the game loop, we would also need a direction variable to track if the snake is moving up, down, left or right... One thing to note is that the snake pos list will be pushed to the snake body list after each round of the loop, and we will pop the first element in the snake body list after every round in the loop, The reason why we do this will be clear once we start implementing it, snake pos will also help us to keep track of where the snake is going so if the snake hit's the boundaries, or eats a fruit, we can know that and also changing the direction of the snake...
Now when the user click's W, the snake will go up, for A snake goes the left, D for right, and S for down, You can change these controls or introduce new controls if you want...
For the fruit, we will create a single square which is going to look like a fruit, You can also use images if you want... We will spawn these fruits randomly on the screen using the random module...
Now for the snake, I could create a snake class and for the fruit, I could create a fruit class, also I could create a game object class from which all game objects are gonna inherit, but for simplicity, I am not going to do that, instead, I will just store them in relevant variables...

Implementing the game logic

We can start implementing the logic discussed above, first, let's start creating our main function, I am going to shift my game loop there

import sys, random
import pygame

pygame.init()

WIN_X = 800
WIN_Y = 600
WIN = pygame.display.set_mode((WIN_X,WIN_Y))
pygame.display.set_caption('snake game')

#main function
def main():
    CLOCK = pygame.time.Clock()
    #variable to store snake position , default when starting is [200,70],
    #Feel free to change
    snake_pos=[200,70]

    #snake body is going to be a 2d list or matrix storing snakes full body
    #here by default, the snake is going to be 3 squares long
    #individual square in this list contains the x and y coordinate
    #of the square itself
    snake_body=[[200,70] , [200-10 , 70] , [200-(2*10),70]]

    #direction of the snake , default is 'right'
    direction = 'right'
    #score of the snake
    score=0

    CLOCK = pygame.time.Clock()
    #game loop
    while 1:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        #filling the window with black color , the (0,0,0)
        #here represents black in rgb , 
        #we are filling the screen for background
        #withouth this your game will act glitchy
        #feel free to change the color
        WIN.fill((0,0,0))

        #for loop that goes through the snake_body
        for square in snake_body:
            #rendering the square of the snake using
            #pygame.draw.rect
            #here the first argument is the surface where
            #YOU wanna draw, in our case, it's the 
            #WIN surface defined above
            #The second argument is the color of the rectangle
            # I chose yellow, feel free to change,
            # color must be RGB
            #third argument is the pos or where to place the rect
            #It takes a tuple with an x coordinate as the first argument
            #AND y coordinate as 2nd argumenT
            #width as 3rd argument and height as 
            #4th argument
            pygame.draw.rect(WIN ,(255, 255, 0), (square[0],square[1],10,10))


        #updating the display with pygame.display.update()
        #withouth this , you wouldn't see anything on the screen
        #You should write all your rendering logic or code
        #that renders things to screen before this line
        pygame.display.update()
        CLOCK.tick(25)

#caliing the main function
main()

Enter fullscreen mode Exit fullscreen mode

Here, I just shifted my code to the main function, made a snake_body and snake_pos variable, in the game loop, I rendered the snake, read the comments if you can't understand, the code should be self-explanatory,
You may be wondering about the coordinate system in pygame, how will pygame draw our objects on the screen, well pygame coordinate system starts from the top leftmost corner, not the bottom leftmost corner like we are used to,
So when we say 20 on the x, 10 on the y in pygame , it means go 20 to the right from the top leftmost corner and 10 to the down from the top ...
If you run the code you should see something like this

Alt Text

Well we can see the snake so that's great news, but the snake is not moving, it's just a static snake on the screen, well that's boring, let's make it move
at the end of the main function before we run pygame.display.update() add the following code

        #remember the direction variable declared at the start
        #of main function , we are checking if direction is
        #up,down,right or left
        if direction == 'right':
            #if direction is right , add 10 to x coordinate
            #of snake_pos , adding to x coordinate makes things
            #move right
            snake_pos[0] += 10
        elif direction == 'left':
            #if direction is left, deduct 10 from x coordinate
            #of snake_pos , deducting from x coordinate 
            #makes things move left
            snake_pos[0] -= 10
        elif direction == 'up':
            #if direction is up, add 10 to y coordinate
            #of snake_pos , adding to y coordinate 
            #makes things move up
            snake_pos[1] -= 10
        elif direction == 'down':
            #if direction is down, deduct 10 from y coordinate
            #of snake_pos , deducting from y coordinate 
            #makes things move down
            snake_pos[1] += 10
Enter fullscreen mode Exit fullscreen mode

If you run the code now, absolutely nothing is going to happen, the snake does not move, why is that?
Because we render squares in snake_body, we don't render the snake_pos, and here we are adjusting snake_pos
So what to do now, well to make the snake move, we can insert our snake_pos to the beginning of the list, let's see what happens, below the code where we check directions and before pygame.display.update() add this

#appending snake_pos to snake_body
#make sure to convert snake_pos
#explicitly to list or else 
#it won't work
snake_body.append(list(snake_pos))
Enter fullscreen mode Exit fullscreen mode

Now if you run the program, you will see something like this,
Alt Text

The snake keeps growing endlessly, but we only want our snake to grow once he eats fruit, we have to fix this...
But why it's behaving this way, well we are appending snake_pos to snake_body, but not removing the old squares from the snake_body, so it's growing endlessly, one thing we can do is remove the oldest square by removing the first item from the list,
Below the code where we append snake_pos to snake_body, add the following

#remove the first element from snake_body
snake_body.pop(0)
Enter fullscreen mode Exit fullscreen mode

Now you should see something like this
Alt Text

So our snake is moving at last, but it's only moving the right, well that's not what we want, let's add some controls

        #The for loop where we check for events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            #here , we are storing pygame.key_pressed() in 
            #keys variable , pygame.key_pressed() returns what key
            #The user has pressed and we can check that below 
            keys= pygame.key.get_pressed()

            #Now in keys , we check if pygame.K_w is true
            #what does that mean
            #well pygame.K_w represents w in pygame
            #and pygame.K_UP represents the up arrow key
            #if keys[pygame.K_w] or keys[pygame.K_UP] 
            #returns true or in other words if the
            #user has clicked the w key or up arrow
            #we change direction to up
            #we then also check clicks on s,a,d,down
            #arrow key,left arrow key and change
            #the direction accordingly
            if (keys[pygame.K_w] or keys[pygame.K_UP]):
                direction = 'up'
            if (keys[pygame.K_s] or keys[pygame.K_DOWN]):
                direction = 'down'
            if (keys[pygame.K_d] or keys[pygame.K_RIGHT]):
                direction = 'right'
            if (keys[pygame.K_a] or keys[pygame.K_LEFT]):
                direction = 'left'
Enter fullscreen mode Exit fullscreen mode

If you run the code now, you should be seeing something like this

Alt Text

So we got our snake moving, now one thing I would like to fix right away is that snake moving instantaneously ...
What do I mean by that, well if the snake is going up, then when we press down arrow key or s, the snake goes over its own body in a very strange way which should not happen, the same thing occurs when the snake is in left and snake goes right or snake is in down and snake goes up, let me show you what I mean

Alt Text

Saw that right, well luckily it has an easy fix, when we are changing the snake's direction, we can check if the snake is already in the opposite direction
Suppose the snake's direction is up, the user wants to go down now, but the snake is going up and we don't want any instantaneous movement, so we'll not go down
The place where we are changing direction in the for loop, change those if check's to this

            #preventing instanteous movement
            #here we are checking if user and wants to
            #to go up , but snake's direction is not
            #'down' which is opposite of up , change
            #the direction
            if (keys[pygame.K_w] or keys[pygame.K_UP]) and direction != 'down':
                direction = 'up'
            if (keys[pygame.K_s] or keys[pygame.K_DOWN]) and direction != 'up':
                direction = 'down'
            if (keys[pygame.K_d] or keys[pygame.K_RIGHT]) and direction != 'left':
                direction = 'right'
            if (keys[pygame.K_a] or keys[pygame.K_LEFT]) and direction != 'right':
                direction = 'left'
Enter fullscreen mode Exit fullscreen mode

With that, the bug gets fixed.
But now, the snake can go through the boundaries of our screen, and keep going endlessly
Well we can exit our application if the snake hit's the boundaries of the window, later on, we will introduce a game_over and main_menu scene

At the end of the game loop , add this

        #checking if x coordinate of snaake is smaller than 0,
        #if that's true , than snake has crossed left side
        #of screen , we also check if snakes x coordinate is 
        #greater than our screen , if that happens , snake
        #has crossed right side of the screen ,
        if snake_pos[0] <=0 or snake_pos[0] >= WIN_X:
            sys.exit()

        #checking if y coordinate of snake is smaller than 0,
        #if that's true than the snake has crossed the upper side
        #of the screen, we also check if snakes y coordinate is 
        #greater than our screens y coordinate,
        #if that happens, snake
        #has crossed the lower side of the screen,
        if snake_pos[1] <=0 or snake_pos[1] >= WIN_Y:
            sys.exit()
Enter fullscreen mode Exit fullscreen mode

This should work however it might be a bit off, might not look pixel-perfect, we can make it better by adding some pixels, worked for me

        if snake_pos[0]+10 <=0 or snake_pos[0] >= WIN_X:
            sys.exit()
        if snake_pos[1]+10 <=0 or snake_pos[1] >= WIN_Y:
            sys.exit()
Enter fullscreen mode Exit fullscreen mode

much better,

Bringing the fruits in

Now let's add some grapes
for The grapes, we can make two variables
fruit_pos and fruit_spawn
fruit_pos is going to be the pos of the grape or fruit, it's going to be positioned randomly on the screen, next we have fruit_spawn which checks if new fruit is to be generated, it's going to be true by default
At the start of the main function, before the game loop, add this

    fruit_pos = [0,0]
    fruit_spawn = True
Enter fullscreen mode Exit fullscreen mode

Then in the game loop, before pygame.display.update(), add this line

#generating a random position for the grape
#for the x coordinate its going to be from
#range 40 to screen_width-40 
#i am not starting from 0 as the grape  can
#be very close to the boundaries then
#same logic for y coordinate
fruit_pos = [random.randrange(40,WIN_X-40),random.randrange(40,WIN_Y-40)]

#Drawing the fruit to the screen using 
#pygame.draw.rect()
pygame.draw.rect(WIN ,(138,43,226),(fruit_pos[0],fruit_pos[1],10,10))
Enter fullscreen mode Exit fullscreen mode

Here, we are generating a random position for the grape and drawing a 10x10 Rect.
If you run the code now, it should look something like this

Alt Text

Well, its kind of keep on generating grapes and not stopping at all, The reason this occurs is that in every round in the while loop, we are changing the fruit_pos and rendering it,
To fix this we can use our fruit_spawn variable, which was set to true initially

The place where we wrote our grape spawning logic, cut that and use this

        #if fruit spawn is true , change fruit_pos
        #and make fruit_spawn false
        if fruit_spawn:
            fruit_pos = [random.randrange(40,WIN_X-40),random.randrange(40,WIN_Y-40)]
            fruit_spawn = False
        pygame.draw.rect(WIN ,(138,43,226),(fruit_pos[0],fruit_pos[1],10,10))
Enter fullscreen mode Exit fullscreen mode

Now, only one fruit should be visible,
If you notice, you will see that we only have one fruit, we are
just changing its position
But the snake still can't eat any fruit, let's add that

At the game loop , before pygame.display.update() , add this

        if pygame.Rect(snake_pos[0],snake_pos[1],10,10).colliderect(pygame.Rect(fruit_pos[0],fruit_pos[1],10,10)):
            fruit_spawn=True
            score += 5
Enter fullscreen mode Exit fullscreen mode

Let me just directly describe what I did instead of commenting,
well I am checking if snake_pos collides with fruit_pos
Now the way I did it looks strange, So let me explain that
in pygame, we have this thing called Rect, More Info here, rects make collision detection easy, You can also use rect on images, any rectangular object works, It can also be circular, But collision detection will not be that great.
Now to make a rect, we have to use Pygame.Rect()method
We have to pass in the x coordinate as the first argument, y coordinate as the 2nd argument, and width and height as the 3rd and 4th argument respectively
In our case, we first pass the snakes x coordinate, y coordinate, it's width and height
Now rects have a lot of methods available, one is colliderect(), This method takes another Pygame.Rect() as argument and checks if they two collide with each other
In our case, we pass fruit_pos transforming as Rect to the colliderect method.
If this returned true, fruit_spawn will be true, so new fruits will be generated and the score will be increased
With that out of the way you should see something like this

Alt Text

But our snake doesn't grow, why is that, how we can make him grow?
Well, remember that we were always removing the first item from snake_body after we append snake_pos to snake_body
What if we only remove the first item in the list when the snake could not eat any grape, if the snake collides with the rect, we don't remove the first element from snake_body
The place where we remove the first element from snake_body, cut that part, then got the part where we check collision between fruit and snake, change that to this

        if pygame.Rect(snake_pos[0],snake_pos[1],10,10).colliderect(pygame.Rect(fruit_pos[0],fruit_pos[1],10,10)):
            fruit_spawn=True
            score += 5
        else:
            snake_body.pop(0)
Enter fullscreen mode Exit fullscreen mode

And now the snake will start to grow,
But why is this working,
When we first start our game, snake_body should be like this

snake_body=[[200,70] , [190 , 70] , [180,70]]
Enter fullscreen mode Exit fullscreen mode

in the game loop, we add snake_pos to it and remove first element
Then it should be something like this

snake_body=[[190, 70], [180, 70], [210, 70]]
Enter fullscreen mode Exit fullscreen mode

Here the snake is 3 squares long, what happens if we don't remove the first element next time

snake_body=[[190, 70], [180, 70], [210, 70], [220, 70]]
Enter fullscreen mode Exit fullscreen mode

The snake is 4 squares long

Now we face another problem when the snake collides with his own body, We have to stop the game if that happens

In the game loop, add this line

        for square in snake_body[1:]:
            if pygame.Rect(square[0],square[1],10,10).colliderect(pygame.Rect(snake_pos[0],snake_pos[1],10,10)):
                sys.exit()
Enter fullscreen mode Exit fullscreen mode

Now, what did I do here?
First of I am looping through the snake_body and checking if snake_pos collides with the other squares.
Now why I excluded the last element in snake_body using snake_body[:-1]
It's a bit confusing but think of the last element as the head of the snake, here we are checking if the head or snake_pos collides with other parts of the body, But if we include the last element in the list, we will be checking if head collides with the head when the for loop reaches there, cause the last element in the snake_body is going to be snake_pos and we would be checking if snake_pos collides with snake_pos or if head collides with head, which is always true.

With that our game is almost ready

Displaying score

We now have to display our score,
In pygame, to show text, first, we have to load a font
at the start of the file, after importing pygame and before the main function, add this

font = pygame.font.SysFont('comicsans',40)
Enter fullscreen mode Exit fullscreen mode

Here we are loading a system font using pygame.font.Sysfont() which takes font as first argument and size as 2nd
I chose comicsans, u can choose other system fonts like timesnewroman

Next, we also have to use this font now ...
At the game loop, before pygame.display.update() add these two lines

        #setting the font up
        score_font = font.render(f'{score}' , True , (255,255,255))
        #declaring position of the font to be in center
        #in x coordinate and 30 in y
        font_pos = score_font.get_rect(center=(WIN_X//2-40 , 30))
        WIN.blit(score_font , font_pos)
Enter fullscreen mode Exit fullscreen mode

So what did I do here?
first I made a score_font variable, and you can name this anything you want by the way, and set it equal to font.render
Now the font is the variable we declared above, so change that to whatever you named it
Now in this font, we have a render method, this takes the string we want to show as 1st argument, in our case its the score, but the score is a number and 1st argument has to be a string and so I changed it to a string using f strings in python. The next argument can be set to either true or false, your wish, 3rd argument is the color in RGB, I chose white.
Now only this won't show text on the screen, we have to Blit it.
So that's why, I used WIN.blit() which takes the thing to blit or display as 1st argument which in our case was a font, and a tuple or list which takes the x and y coordinates, I already declared a font_pos above which is a center position at the top of the screen. If you wanna know how to center text in pygame, there are a lot of ways. You can check this StackOverflow thread for more details.

Let's run the game now ..................

Alt Text

That's a fully functioning snake game we have here.

The main menu and game over scene

And Before I end this post, let's implement the main menu and game over scene...
Let's start with the main menu

In our game code, before the main function, put this code

#The main menu
def main_menu():
    #creating seperate game loop for the main_menu
    while 1:
        #listening for events
        for event in pygame.event.get():
            #you know what this does
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

            #IF THE user clicks anywhere in the screen
            #call the main function , start the game
            if event.type == pygame.MOUSEBUTTONDOWN:
                main()
        #nothing new here
        WIN.fill((0,0,0))

        #showing the message 
        #'Press anywhere to start'
        # at the center of the screen
        main_menu_message = font.render('Press anywhere to start the game' , True , (255,255,255))
        font_pos = main_menu_message.get_rect(center=(WIN_X//2, WIN_Y//2))
        WIN.blit(main_menu_message , font_pos)
        pygame.display.update()
Enter fullscreen mode Exit fullscreen mode

Ok, Let's go through this quick. In this function, we wrote another game loop and another check for pygame.event.get()
If the user clicks anywhere, the main function will be called Which means the game will start, We also display a message 'Press anywhere to start at the center of the screen

Now at the end of our code, where we call main(), instead

main_menu()
Enter fullscreen mode Exit fullscreen mode

We are almost done, The thing which is left is the game_over scene
Let's quickly go through that

#game over scene, this takes the score to display as 
#the argument
def game_over(score):
        #I won't explain these again
        while 1:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
            WIN.fill((0,0,0))

            #showing 'You lost' in red color
            game_over_message = font.render('You Lost' , True , (255,0,0))
            #showing 'You score was SCORE'
            game_over_score = font.render(f'Your Score was {score}' , True , (255,255,255))

            font_pos_message = game_over_message.get_rect(center=(WIN_X//2, WIN_Y//2))
            font_pos_score = game_over_score.get_rect(center=(WIN_X//2, WIN_Y//2+40))
            WIN.blit(game_over_message , font_pos_message)
            WIN.blit(game_over_score , font_pos_score)
            pygame.display.update()

            #here what we are doing is we
            #use time.sleep() to stop our 
            #program for 3 seconds ,
            #don't forget to import time module at
            #the top ...
            #after 3 seconds, we call our main_menu()
            time.sleep(3)
            main_menu()
Enter fullscreen mode Exit fullscreen mode

What the game_over() does is show that you have lost and your score. Pauses the program for 3 seconds and then just calls main_menu()

Now in the main function, whenever we die, we are calling sys.exit(), instead of that, call game_over() passing in the score
Your main function should be close to this

def main():
    CLOCK = pygame.time.Clock()
    snake_pos=[200,70]
    snake_body=[[200,70] , [200-10 , 70] , [200-(2*10),70]]
    fruit_pos = [0,0]
    fruit_spawn = True
    direction = 'right'
    score=0
    CLOCK = pygame.time.Clock()
    while 1:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

            keys= pygame.key.get_pressed()
            if (keys[pygame.K_w] or keys[pygame.K_UP]) and direction != 'down':
                direction = 'up'
            if (keys[pygame.K_s] or keys[pygame.K_DOWN]) and direction != 'up':
                direction = 'down'
            if (keys[pygame.K_d] or keys[pygame.K_RIGHT]) and direction != 'left':
                direction = 'right'
            if (keys[pygame.K_a] or keys[pygame.K_LEFT]) and direction != 'right':
                direction = 'left'

        WIN.fill((0,0,0))
        for square in snake_body:
            pygame.draw.rect(WIN ,(255, 255, 0), (square[0],square[1],10,10))
        if direction == 'right':
            snake_pos[0] += 10
        elif direction == 'left':
            snake_pos[0] -= 10
        elif direction == 'up':
            snake_pos[1] -= 10
        elif direction == 'down':
            snake_pos[1] += 10

        snake_body.append(list(snake_pos))

        if fruit_spawn:
            fruit_pos = [random.randrange(40,WIN_X-40),random.randrange(40,WIN_Y-40)]
            fruit_spawn = False

        pygame.draw.rect(WIN ,(138,43,226),(fruit_pos[0],fruit_pos[1],10,10))

        score_font = font.render(f'{score}' , True , (255,255,255))
        font_pos = score_font.get_rect(center=(WIN_X//2-40 , 30))
        WIN.blit(score_font , font_pos)

        if pygame.Rect(snake_pos[0],snake_pos[1],10,10).colliderect(pygame.Rect(fruit_pos[0],fruit_pos[1],10,10)):
            fruit_spawn=True
            score += 5
        else:
            snake_body.pop(0)

        for square in snake_body[:-1]:
            if pygame.Rect(square[0],square[1],10,10).colliderect(pygame.Rect(snake_pos[0],snake_pos[1],10,10)):
                game_over(score)

        if snake_pos[0]+10 <=0 or snake_pos[0] >= WIN_X:
            game_over(score)
        if snake_pos[1]+10 <=0 or snake_pos[1] >= WIN_Y:
            game_over(score)
        pygame.display.update()

        CLOCK.tick(25)
Enter fullscreen mode Exit fullscreen mode

With this, We should have a complete snake game...

Alt Text

The complete code can be found at my GitHub here

Conclusion

So here you have it... A snake game with pygame, feel free to play with the code and adding features like maybe a high score system, some small animations, add difficulty levels, etc...
Also, it would be better if you create something yourselves.
There are many resources to help you work with
First is the pygame website: https://www.pygame.org/news
2nd will be invent with python: https://inventwithpython.com/
check out this article: https://inventwithpython.com/blog/2012/02/20/i-need-practice-programming-49-ideas-for-game-clones-to-code/

You can also check out games I made with pygame, they might not be the coolest, But you'll get some idea

Fruit Ninja clone: Fruit Frowns
Link: https://github.com/grapeJUICE1/Fruit-Frowns
Breakout
Link: https://github.com/grapeJUICE1/Grape-Bricks
Space Invaders
Link: https://github.com/grapeJUICE1/space-invaders

If you found this useful or helpful, please share a 💓, 🦄, or 🔖.

Discussion (4)

pic
Editor guide
Collapse
abdurrahmaanj profile image
Abdur-Rahmaan Janhangeer • Edited

Thanks for this detailed and easy-to-follow tuto. I would be really grateful if you could have a look at this lib: github.com/Abdur-rahmaanJ/hooman. It is a PyGame wrapper which allows clean codebases with lots of goodies bundled. If ever you make a tuto about this lib, drop me a word, i'll include it in the resources!

Collapse
grapejuice profile image
Grape Juice Author

I just checked your work on hooman.... not bad ... reminds me of pyzero ... Will check it in future ...

Collapse
abdurrahmaanj profile image
Collapse
blacksmithop profile image
ambu

Nice tutorial, I made a couple of changes to your logic such update being called inside a while True loop where it only needs to be called once and streamlining the snake position update logic.
You can find it here: gitlab.com/blacksmithop/snek