Tutorial #4
Subscribe to Tech with Tim
Collision With Snack
Now that we’ve created the snack we need to draw it and see if the head of our snake collides with it.
Our redrawWindow() function should now look like this:
def redrawWindow(surface): global rows, width, s, snack # ADD snack to this line surface.fill((0,0,0)) s.draw(surface) snack.draw(surface) # NEW drawGrid(width,rows, surface) pygame.display.update()
To check for collision we will add the following lines into our game loop within the main() function.
if s.body[0] == snake.pos: # Checks if the head collides with the snack s.addCube() # Adds a new cube to the snake snack = cube(randomSnack(rows, s), color=(0,255,0)) # creates a new snack object
Snake Class – addCube() Method
The last step to adding a cube is to add it to the end of our snake object. We will do this from the addCube() method.
class snake(): ... def addCube(self): tail = self.body[-1] dx, dy = tail.dirnx, tail.dirny # We need to know which side of the snake to add the cube to. # So we check what direction we are currently moving in to determine if we # need to add the cube to the left, right, above or below. if dx == 1 and dy == 0: self.body.append(cube((tail.pos[0]-1,tail.pos[1]))) elif dx == -1 and dy == 0: self.body.append(cube((tail.pos[0]+1,tail.pos[1]))) elif dx == 0 and dy == 1: self.body.append(cube((tail.pos[0],tail.pos[1]-1))) elif dx == 0 and dy == -1: self.body.append(cube((tail.pos[0],tail.pos[1]+1))) # We then set the cubes direction to the direction of the snake. self.body[-1].dirnx = dx self.body[-1].dirny = dy
Ending the Game
We lose the game when our snake object collides with itself. To check this we add the following line to our main() function inside the game loop.
for x in range(len(s.body)): if s.body[x].pos in list(map(lambda z:z.pos,s.body[x+1:])): # This will check if any of the positions in our body list overlap print('Score: ', len(s.body)) message_box('You Lost!', 'Play again...') s.reset((10,10)) break
Snake Class – reset() Method
Now we will code the reset() method from within the snake class. All this will do is reset the snake so we can play again afterwards.
class snake(): ... def reset(self, pos): self.head = cube(pos) self.body = [] self.body.append(self.head) self.turns = {} self.dirnx = 0 self.dirny = 1
Displaying a Message Box
When the user losses we want to display a message box telling them that they lost.
We are going to do this using tkinter and inside of our message_box() function.
import tkinter as tk from tkinter import messagebox def message_box(subject, content): root = tk.Tk() root.attributes("-topmost", True) root.withdraw() messagebox.showinfo(subject, content) try: root.destroy() except: pass
And now our game is finished!
Full Code
#Snake Tutorial Python import math import random import pygame import tkinter as tk from tkinter import messagebox class cube(object): rows = 20 w = 500 def __init__(self,start,dirnx=1,dirny=0,color=(255,0,0)): self.pos = start self.dirnx = 1 self.dirny = 0 self.color = color def move(self, dirnx, dirny): self.dirnx = dirnx self.dirny = dirny self.pos = (self.pos[0] + self.dirnx, self.pos[1] + self.dirny) def draw(self, surface, eyes=False): dis = self.w // self.rows i = self.pos[0] j = self.pos[1] pygame.draw.rect(surface, self.color, (i*dis+1,j*dis+1, dis-2, dis-2)) if eyes: centre = dis//2 radius = 3 circleMiddle = (i*dis+centre-radius,j*dis+8) circleMiddle2 = (i*dis + dis -radius*2, j*dis+8) pygame.draw.circle(surface, (0,0,0), circleMiddle, radius) pygame.draw.circle(surface, (0,0,0), circleMiddle2, radius) class snake(object): body = [] turns = {} def __init__(self, color, pos): self.color = color self.head = cube(pos) self.body.append(self.head) self.dirnx = 0 self.dirny = 1 def move(self): for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() keys = pygame.key.get_pressed() for key in keys: if keys[pygame.K_LEFT]: self.dirnx = -1 self.dirny = 0 self.turns[self.head.pos[:]] = [self.dirnx, self.dirny] elif keys[pygame.K_RIGHT]: self.dirnx = 1 self.dirny = 0 self.turns[self.head.pos[:]] = [self.dirnx, self.dirny] elif keys[pygame.K_UP]: self.dirnx = 0 self.dirny = -1 self.turns[self.head.pos[:]] = [self.dirnx, self.dirny] elif keys[pygame.K_DOWN]: self.dirnx = 0 self.dirny = 1 self.turns[self.head.pos[:]] = [self.dirnx, self.dirny] for i, c in enumerate(self.body): p = c.pos[:] if p in self.turns: turn = self.turns[p] c.move(turn[0],turn[1]) if i == len(self.body)-1: self.turns.pop(p) else: if c.dirnx == -1 and c.pos[0] <= 0: c.pos = (c.rows-1, c.pos[1]) elif c.dirnx == 1 and c.pos[0] >= c.rows-1: c.pos = (0,c.pos[1]) elif c.dirny == 1 and c.pos[1] >= c.rows-1: c.pos = (c.pos[0], 0) elif c.dirny == -1 and c.pos[1] <= 0: c.pos = (c.pos[0],c.rows-1) else: c.move(c.dirnx,c.dirny) def reset(self, pos): self.head = cube(pos) self.body = [] self.body.append(self.head) self.turns = {} self.dirnx = 0 self.dirny = 1 def addCube(self): tail = self.body[-1] dx, dy = tail.dirnx, tail.dirny if dx == 1 and dy == 0: self.body.append(cube((tail.pos[0]-1,tail.pos[1]))) elif dx == -1 and dy == 0: self.body.append(cube((tail.pos[0]+1,tail.pos[1]))) elif dx == 0 and dy == 1: self.body.append(cube((tail.pos[0],tail.pos[1]-1))) elif dx == 0 and dy == -1: self.body.append(cube((tail.pos[0],tail.pos[1]+1))) self.body[-1].dirnx = dx self.body[-1].dirny = dy def draw(self, surface): for i, c in enumerate(self.body): if i ==0: c.draw(surface, True) else: c.draw(surface) def drawGrid(w, rows, surface): sizeBtwn = w // rows x = 0 y = 0 for l in range(rows): x = x + sizeBtwn y = y + sizeBtwn pygame.draw.line(surface, (255,255,255), (x,0),(x,w)) pygame.draw.line(surface, (255,255,255), (0,y),(w,y)) def redrawWindow(surface): global rows, width, s, snack surface.fill((0,0,0)) s.draw(surface) snack.draw(surface) drawGrid(width,rows, surface) pygame.display.update() def randomSnack(rows, item): positions = item.body while True: x = random.randrange(rows) y = random.randrange(rows) if len(list(filter(lambda z:z.pos == (x,y), positions))) > 0: continue else: break return (x,y) def message_box(subject, content): root = tk.Tk() root.attributes("-topmost", True) root.withdraw() messagebox.showinfo(subject, content) try: root.destroy() except: pass def main(): global width, rows, s, snack width = 500 rows = 20 win = pygame.display.set_mode((width, width)) s = snake((255,0,0), (10,10)) snack = cube(randomSnack(rows, s), color=(0,255,0)) flag = True clock = pygame.time.Clock() while flag: pygame.time.delay(50) clock.tick(10) s.move() if s.body[0].pos == snack.pos: s.addCube() snack = cube(randomSnack(rows, s), color=(0,255,0)) for x in range(len(s.body)): if s.body[x].pos in list(map(lambda z:z.pos,s.body[x+1:])): print('Score: ', len(s.body)) message_box('You Lost!', 'Play again...') s.reset((10,10)) break redrawWindow(win) main()
If this tutorial helped you please consider supporting myself and website by heading to the Support/Donate page. Any donation helps and is greatly appreciated.