- Install PyGame
Pygame is a cross-platform set of Python modules which is used to create video games. It includes computer graphics and sound libraries designed to be used with the Python programming language.
You can install it by using following command,
pip install pygame
For more information, check official documentation here.
- Introduction to the Game
Building a snake and apple game in pygame and python. Snake and apple is a simple game where when a snake eats an apple it gets longer and your score increases. If a snake hits any part of its body it will die.
- Resources
Project complete media resources can be downloaded from master branch of my github repo here:
Project complete source code is available on my Github repo, you can check it here.
Note: Resources include all photos, videos, audio tracks and background music used in this project.
- Classes
Now we have talked about complete game setup, so it’s time to crack the main code into three major classes, that will help us understand and manage the code well and make it readable.
So we will have these three class:
- Snake Class:
In snake class we will have an initial functional call __init__ that will let the screen ready, will do all other initialization for us like sound, screen size, snake length, snake initial direction etc. This class will also contain functions that will ensure the functionality of snake like, drawing snake, snake length increment, snake move, snake moving directions (left, right, up and down) and snake walk function.
Code:
class Snake:
def __init__(self, parent_screen, length):
self.parent_screen = parent_screen
self.length = length
self.x = [SIZE]*length
self.y = [SIZE]*length
self.direction = 'down'
self.block = pygame.image.load("resources/block.jpg").convert()
# drawing snake with blocks
def draw(self):
for i in range(self.length):
self.parent_screen.blit(self.block, (self.x[i], self.y[i]))
pygame.display.flip()
# increases the length as snake eats an apple
def increase_length(self):
self.length += 1
self.x.append(-1)
self.y.append(-1)
# snake movement towards left
def move_left(self):
self.direction = 'left'
# snake movement towards right
def move_right(self):
self.direction = 'right'
# snake movement towards up
def move_up(self):
self.direction = 'up'
# snake movement towards down
def move_down(self):
self.direction = 'down'
# snake to continue walking unless movement direction is changed by arrows
def walk(self):
# update body
for i in range(self.length-1, 0, -1):
self.x[i] = self.x[i-1]
self.y[i] = self.y[i-1]
# update head
if self.direction == 'left':
self.x[0] -= SIZE
if self.direction == 'right':
self.x[0] += SIZE
if self.direction == 'up':
self.y[0] -= SIZE
if self.direction == 'down':
self.y[0] += SIZE
self.draw()
- Apple Class:
In the context of the game, Apple is food that a snake eats. Now Apple class has some initial look (photo) and Apple size declared in __init__ function. It has draw and move functions that support its functionality in the game.
Code:
class Apple:
def __init__(self, parent_screen):
self.image = pygame.image.load("resources/apple.jpg").convert()
self.parent_screen = parent_screen
self.x = SIZE*3
self.y = SIZE*3
# drawing apple on screen
def draw(self):
self.parent_screen.blit(self.image, (self.x, self.y))
pygame.display.flip()
# regenerating apple else where once snake eats an apple
def move(self):
self.x = random.randint(0, 24) * SIZE
self.y = random.randint(0, 19) * SIZE
- Game Class:
This is the main controller class of this game. It’s __init__ function will set up all the things required to start the game like all kinds of music/sounds, background image, snake and apple object, ready the surface to play game upon, and draw snake and apple on the surface. Some major functions are calculate and rate the collisions (Snake-snake collision, snake-apple collision), rendering background function, play background music and sounds upon collision functions, play game function that is supposed to be a main handler, run the snake in all directions function, reset score, display score, show game over function.
Code:
class Game:
def __init__(self):
pygame.init()
# pygame.display.set_caption("Snake asnd Apple Game by .bullbat")
pygame.mixer.init()
self.play_bg_music()
self.surface = pygame.display.set_mode((1000, 800))
self.surface.fill((91, 180, 181))
self.snake = Snake(self.surface, 1)
self.snake.draw()
self.apple = Apple(self.surface)
self.apple.draw()
# basic collision fun between two objects
def is_collision(self, x1, y1, x2, y2):
if x1 >= x2 and x1 < x2 + SIZE:
if y1 >= y2 and y1 < y2 + SIZE:
return True
return False
# provide backgroud music for game
def play_bg_music(self):
pygame.mixer.music.load("resources/bg_music_1.wav")
pygame.mixer.music.play()
# output sound on event occurance
def play_sound(self, sound):
sound = pygame.mixer.Sound(f"resources/{sound}.wav")
pygame.mixer.Sound.play(sound)
# background picture/ surface for the game
def render_bg(self):
bg = pygame.image.load("resources/background.jpg")
self.surface.blit(bg, (0, 0))
# @main game Handler/ Controller
def play(self):
self.render_bg()
self.snake.walk()
self.apple.draw()
self.display_score()
pygame.display.flip()
# Snake eating Apple scenario
if self.is_collision(self.snake.x[0], self.snake.y[0], self.apple.x, self.apple.y):
self.play_sound("ding")
self.snake.increase_length()
self.apple.move()
# Snake colliding its own body
for i in range(3, self.snake.length):
if self.is_collision(self.snake.x[0], self.snake.y[0], self.snake.x[i], self.snake.y[i]):
self.play_sound("crash")
raise "Collision Occured"
# snake colliding with the boundries of the window
if not (0 <= self.snake.x[0] <= 1000 and 0 <= self.snake.y[0] <= 800):
self.play_sound('crash')
raise "Hit the boundry error"
# display score on screen
def display_score(self):
font = pygame.font.SysFont('arial', 30)
score = font.render(
f"Score: {self.snake.length}", True, (255, 255, 255))
self.surface.blit(score, (800, 10))
# show a scorecard and replay option once game is over
def show_game_over(self):
self.render_bg()
font = pygame.font.SysFont('arial', 30)
line1 = font.render(
f"Game is over! Your Score is: {self.snake.length}", True, (255, 255, 255))
self.surface.blit(line1, (200, 300))
line2 = font.render(
"To play again press Enter. To exit press Escape!", True, (255, 255, 255))
self.surface.blit(line2, (200, 350))
pygame.display.flip()
pygame.mixer.music.pause()
# reset the game state to initial state as user opt to replay the game
def reset(self):
self.snake = Snake(self.surface, 1)
self.apple = Apple(self.surface)
# @main Game Logic
def run(self):
running = True
pause = False
while running:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
if event.key == K_RETURN:
pygame.mixer.music.unpause()
pause = False
if not pause:
if event.key == K_UP:
self.snake.move_up()
if event.key == K_DOWN:
self.snake.move_down()
if event.key == K_LEFT:
self.snake.move_left()
if event.key == K_RIGHT:
self.snake.move_right()
elif event.type == QUIT:
running = False
try:
if not pause:
self.play()
except Exception as e:
self.show_game_over()
pause = True
self.reset()
time.sleep(0.3)
- Main Class:
Code:
# place this code in the start of your project
import pygame
from pygame.locals import *
import time
import random
SIZE = 40
BACKGROUND_COLOR = (91, 180, 181)
# end here
# main functioin (may place at the end of the project)
if __name__ == "__main__":
game = Game()
game.run()
- Run-time Functionality:
This includes:
- Score Count
- Increment in snake length upon eating apple
- Sounds on eating apple, hitting boundary and game over
- Background music throughout the game
- Replay option once game is over
- Quit the game any time option
- Conclusion:
So this is supposed to be an easy to implement game if and only if you break the logic into small modules that are manageable easily and also easily replicate the same logic into clean and well looking code. I hope you like this blog, and if you want to get complete source code, you can download it here.