简体   繁体   中英

How to detect a collision of a snake with itself in a snake game made with pygame

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM