I am making a snake game in python with pygame but i am having trouble with getting the program to detect a collision with itself...
I tried to set it up to where it would detect if Morrison.head would detect for Morrison.body, but it's giving me an error
import pygame
import random
import time
import sys
from collections import deque
# Window size
WINDOW_WIDTH=400
WINDOW_HEIGHT=400
pygame.init()
WINDOW = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ),
pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )
SPRITES = pygame.sprite.Group()
pygame.display.set_caption("Picnic!")
background = pygame.image.load("picnic.png")
background_rect = background.get_rect()
HEAD_IMAGE =pygame.image.load('morrison.png').convert_alpha()
#HEAD_IMAGE.fill(pygame.Color('yellow'))
BODY_IMAGE = pygame.image.load('morrison.png').convert_alpha()
#BODY_IMAGE.fill(pygame.Color('orange'))
FOOD1_IMAGE= pygame.image.load("thinMint.png").convert_alpha()
#FOOD1_IMAGE.fill(pygame.Color('green'))
FOOD2_IMAGE =pygame.image.load("cookie.png").convert_alpha()
#FOOD2_IMAGE.fill(pygame.Color('red'))
FOOD3_IMAGE =pygame.image.load("triscuit.png").convert_alpha()
#FOOD3_IMAGE.fill(pygame.Color('blue'))
clock = pygame.time.Clock()
BLACK = (0,0,0)
score = 0
last_positions = deque(maxlen=10)
def generateFoodPosition():
pos = random.randrange(1, 40, 1) * 10, random.randrange(1, 40,
1) * 10
if pos in last_positions:
return generateFoodPosition()
last_positions.append(pos)
return pos
def draw_text(surf, text, size, x, y):
font = pygame.font.SysFont("SquidgySweets.otf", 72)
text_surface = font.render(text, True, BLACK)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
surf.blit(text_surface, text_rect)
class Food(pygame.sprite.Sprite):
def __init__(self, image, *grps):
pygame.sprite.Sprite.__init__(self, *grps)
self.image = image
self.rect = self.image.get_rect()
self.rect.center = generateFoodPosition()
def update(self, dt, snakehead, events):
global score
m=1
if self.rect.colliderect(snakehead):
print("yeet yah")
score += 1
self.rect.center = generateFoodPosition()
if Morrison.head:
Morrison.grow(m)
Morrison.last_grow=Morrison.t
class MorrisonSprite(pygame.sprite.Sprite):
def __init__( self, part_image, position, head, *grps):
pygame.sprite.Sprite.__init__(self, *grps)
self.image = part_image
self.rect = self.image.get_rect()
self.rect.center = position
self.move_step = self.image.get_rect().width # this is how
we know how many pixels per move step
self.t = 0
self.last_move = 0
self.last_grow = 0
self.head = head
self.direction = 'left';
self.body = []
if self.head:
self.body.append(self)
#self.grow(m)
def wrapAroundScreen(self):
# Stay on the screen, and wrap around
if (self.rect.left >= WINDOW_WIDTH ):
self.rect.right = 0
elif (self.rect.right <= 0 ):
self.rect.left = WINDOW_WIDTH
if (self.rect.top >= WINDOW_HEIGHT ):
self.rect.bottom = 0
elif (self.rect.bottom <= 0):
self.rect.top = WINDOW_HEIGHT
def move( self ):
if ( self.direction == 'up' ):
self.rect.y -= self.move_step
elif ( self.direction == 'down' ):
self.rect.y += self.move_step
elif ( self.direction == 'left' ):
self.rect.x -= self.move_step
elif ( self.direction == 'right' ):
self.rect.x += self.move_step
else:
print(" MOVE ERROR - "+direction)
self.wrapAroundScreen()
def update(self, dt, snakehead, events):
if not self.head:
return
keys = pygame.key.get_pressed()
if ( keys[pygame.K_UP] ):
self.direction = "up"
elif ( keys[pygame.K_DOWN] ):
self.direction = "down"
elif ( keys[pygame.K_LEFT] ):
self.direction = "left"
elif ( keys[pygame.K_RIGHT] ):
self.direction = "right"
self.t += dt
if any(e for e in events if e.type == pygame.KEYDOWN) or (
self.t > 3000 and self.t - self.last_move >= 100 ):
self.slither()
self.last_move = self.t
# Grow every 2 seonds
#This is a past test function that is no longer used:
#if (self.t > 3000 and self.t - self.last_grow >= 10000):
#print("Growing...")
#self.grow( 1 )
#self.last_grow = self.t
def slither( self ):
# Move each body part to the location of the previous part
# So we iterate over the tail-parts in reverse
for i in range( len( self.body )-1, 0, -1 ):
self.body[i].rect.center = self.body[i-1].rect.center
# Move the head
self.move()
def getGrowPosition( self ):
# we grow against self.direction
# so if we're moving up, the tail grows down
x,y = self.body[ -1 ].rect.center
if ( self.direction == 'up' ):
y += self.move_step
elif ( self.direction == 'down' ):
y -= self.move_step
elif ( self.direction == 'left' ):
x += self.move_step
elif ( self.direction == 'right' ):
x -= self.move_step
return (x,y)
def grow( self, by_size=1 ):
for i in range( by_size ):
# new body part needs to be added at the tail-position
new_body_part = MorrisonSprite( BODY_IMAGE,
self.getGrowPosition(), False, *self.groups())
self.body.append( new_body_part )
### MAIN
Morrison = MorrisonSprite(HEAD_IMAGE, ((WINDOW_WIDTH//2)-64,
WINDOW_HEIGHT//2 ), True, SPRITES)
clock = pygame.time.Clock()
done = False
Food(FOOD1_IMAGE, SPRITES)
Food(FOOD2_IMAGE, SPRITES)
Food(FOOD3_IMAGE, SPRITES)
dt = 0
while not done:
#this allows you to close the window
events = pygame.event.get()
for event in events:
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == pygame.VIDEORESIZE ):
# this allows you to resize-window:
WINDOW_WIDTH = event.w
WINDOW_HEIGHT = event.h
WINDOW = pygame.display.set_mode(( WINDOW_WIDTH,
WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE)
#Morrison.head = head
#Morrison.rect = Morrison.head.get_rect()
if Morrison.rect.head.colliderect(Morrison.rect.body):
pygame.quit()
#this basically displays everything on the screen (background,
sprites)...
WINDOW.fill(BLACK)
WINDOW.blit(background, background_rect)
draw_text(WINDOW, str(score), 18, WINDOW_WIDTH / 2, 10)
SPRITES.update(dt, Morrison.rect, events)
SPRITES.draw(WINDOW)
pygame.display.flip()
dt = clock.tick_busy_loop(60)
pygame.quit()
i expected the program to detect a collison if it runs into its own body, but instead it just returns an error
How about using the pygame spritecollide
function?
# In MorrisonSprite class
def hitMyself( self ):
hit_by = pygame.sprite.spritecollide( self, SPRITES, False )
# hit_by includes the head too (always), so just check the count
if ( len( hit_by ) > 1 ):
print("HIT") # debug feedback
return True
else:
return False
EDIT: Incorporating this function into the main loop seems to track the hits OK.
# Main loop
while not done:
...
# Did the snake hit it's tail
if ( Morrison.hitMyself() == True ):
done = True
Why don't you keep it simple? Let's say
class Snake:
self.snake = [(200, 200), (210, 200), (220, 200), (230, 200), (240, 200)]
def self_collision(self):
"""
Return True if snake's head is in itself
"""
return self.snake[0] in self.snake[1:]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.