问题
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:
- Snake
- 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