pygame.sprite.groupcollide() does not work when trying to implement collision in pygame

爷,独闯天下 提交于 2021-02-05 06:15:47

问题


I'm working on a Snake Game project, and I can't make the snake and the foods collide. After reading some Pygame documentation, I decided to use pygame.sprite.groupcollide for collisions. I have two types of sprites in my game:

  1. Snake
  2. Food(s)

pygame.sprite.groupcollide has worked in my previous game (Alien Invasion game from a Python book); however, for some reason it doesn't work in my current game. What do you think might be the reason? Here is the higlight of the collisions part (snake_game.py):

def _check_snake_food_collisions(self):
        '''Check the collisions between the snake and foods.'''
        collisions = pygame.sprite.groupcollide(self.foods, self.snake_parts, True, False)

Here is my code:

snake_game.py

import pygame
import sys

from random import randint

from settings import Settings 
from snake import Snake
from food import Food 

class SnakeGame:
    '''The main class of the game.'''

    def __init__(self):
        '''Initialize the game assets, screen, etc.'''
        pygame.init()
        self.settings = Settings()
        
        self.screen = pygame.display.set_mode((self.settings.screen_width,
                            self.settings.screen_height))
        pygame.display.set_caption("Snake Game")

        self.snake_parts = pygame.sprite.Group() #Will do some refactoring here.
        self.snake_part = Snake(self)  # Will move this part elsewhere.
        self.snake_parts.add(self.snake_part)
        
        self.foods = pygame.sprite.Group()
        self.food = Food(self)          # Will do some refactoring here as well.
        self.foods.add(self.food)       # Will move this part elsewhere.
       
    def run_game(self):
        '''The main loop of the game.'''
        while True:
            self._check_events()
            self._update_snake_parts()
            self._update_screen()

    def _check_events(self):
        '''Check all the events.'''
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    def _check_keydown_events(self, event):
        '''Check keydown events.'''
        if event.key == pygame.K_UP or event.key == pygame.K_w:
            self.snake_part.m_up = True
        elif event.key == pygame.K_DOWN or event.key == pygame.K_s:
            self.snake_part.m_down = True
        elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
            self.snake_part.m_left = True
        elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
            self.snake_part.m_right = True
        elif event.key == pygame.K_q:
            sys.exit()

    def _check_keyup_events(self, event):
        '''Check keyup events.'''
        if event.key == pygame.K_UP or event.key == pygame.K_w:
            self.snake_part.m_up = False
        elif event.key == pygame.K_DOWN or event.key == pygame.K_s:
            self.snake_part.m_down = False
        elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
            self.snake_part.m_left = False
        elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
            self.snake_part.m_right = False

    def _check_snake_food_collisions(self):
        '''Check the collisions between the snake and foods.'''
        collisions = pygame.sprite.groupcollide(self.foods, self.snake_parts, True, False)
      
    def _update_snake_parts(self):
        '''Update all the parts of the snake.'''
        self.snake_parts.update()
        
    def _update_screen(self):
        '''Update the screen.'''
        self.screen.fill(self.settings.bg_color)

        # Draw snake parts to the screen.
        for snake_part in self.snake_parts.sprites():
            snake_part.draw_part()

        # Draw foods to the screen.
        self.food.draw_food()

        pygame.display.flip()

if __name__ == '__main__':
    sg = SnakeGame()
    sg.run_game()

snake.py

import pygame
import sys

from pygame.sprite import Sprite 

class Snake(Sprite):
    '''A class to manage the snake.'''

    def __init__(self, sg):
        '''Initialize the snake's location, size, etc.'''
        super().__init__()
        self.screen = sg.screen
        self.settings = sg.settings
        self.color = self.settings.snake_color
        self.screen_rect = self.screen.get_rect()
        
        # Create the snake's rect object and position it.
        self.rect = pygame.Rect(0,0, self.settings.snake_width,
                            self.settings.snake_height)
        self.rect.center = self.screen_rect.center

        # Get the precise coordinates of the snake.
        self.x = float(self.rect.x)
        self.y = float(self.rect.y)

        # Set movement flags.
        self.m_right = False
        self.m_left = False
        self.m_up = False
        self.m_down = False

    def update(self):
        '''Update the position of the snake.'''
        if self.m_right and self.rect.right < self.screen_rect.right:                    
            self.x += self.settings.snake_speed
        if self.m_left and self.rect.left > self.screen_rect.left:
            self.x -= self.settings.snake_speed
        if self.m_down and self.rect.bottom < self.screen_rect.bottom:
            self.y += self.settings.snake_speed
        if self.m_up and self.rect.top > self.screen_rect.top:
            self.y -= self.settings.snake_speed

        self.rect.x = self.x
        self.rect.y = self.y
        
    def draw_part(self):
        '''Draw the snake to the screen.'''
        pygame.draw.rect(self.screen, self.color, self.rect)

food.py

import pygame

from random import randint
from pygame.sprite import Sprite 

class Food(Sprite):
    '''A class to manage the foods.'''

    def __init__(self, sg):
        '''Initialize the food rect, food color, and other assets.'''
        super().__init__()
        self.screen = sg.screen
        self.settings = sg.settings
        self.color = self.settings.food_color
        
        self.rect = pygame.Rect(0, 0, self.settings.food_width,
                        self.settings.food_height)

        # Initialize the food at a random position.
        self.spawn_food()
    
    def spawn_food(self):
        '''Position the food at a random position.'''
        self.rect.x = randint(0, self.settings.screen_width - 
                        self.settings.food_width) 
        self.rect.y = randint(0, self.settings.screen_height - 
                        self.settings.food_height) 

    def draw_food(self):
        '''Draw the food to the screen.'''
        pygame.draw.rect(self.screen, self.color, self.rect)

settings.py

class Settings:
    '''Class for game settings.'''

    def __init__(self):
        '''Initialize the game settings.'''

        # Screen settings
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (119, 181, 254)  # Blue color

        # Snake settings
        self.snake_width = 20
        self.snake_height = 20
        self.snake_color = (255, 255, 0)  # Yellow color
        self.snake_speed = 1

        # Food settings
        self.food_width = 10
        self.food_height = 10
        self.food_color = (139, 69, 19)  # Brown color

回答1:


Read the documentation of pygame.sprite.groupcollide:

[...] If either dokill argument is True, the colliding Sprites will be removed from their respective Group.

When you do

collisions = pygame.sprite.groupcollide(self.foods, self.snake_parts, True, False)

and a collision is detected and the food is removed from the Group self.foods. However, the self.food attribute still refers to the Food object.

The food is in the Group until a collision is detected. Hence you have to draw the objects in the Group self.foods rather than the Sprite object self.food. You don't even need the self.food attribute. It is sufficient if all Food objects are contained in self.foods:

self.food.draw_food()

for food in self.foods.sprites():
    food.draw_food()

Please note, that _check_snake_food_collisions is actually not called in your code.



来源:https://stackoverflow.com/questions/63997798/pygame-sprite-groupcollide-does-not-work-when-trying-to-implement-collision-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!