无法创建 class 的两个实例(使用 pygame,试图用两条蛇制作贪吃蛇游戏)

Unable to create two instances of a class (using pygame, trying to make snake game with two snakes)

我创建了一个 class,它使用 pygame 定义和创建了一条蛇。当我创建一个蛇实例 class 时,将正确创建一条蛇,但是当我尝试创建两个实例时,我的代码不会创建第二条蛇。我不确定这是因为我错误地使用了 pygame,还是因为我在 classes 中做错了什么。

这是我的代码;它有点长,但我已经把它全部包括在内,因为我不知道问题出在哪里。我很确定我正在正确处理所有变量和参数,并且处理按键也是正确的。如果您将此代码复制并粘贴到解释器中,如果您说您想要一条蛇,它可以正常工作,但如果您想要创建两条蛇,它就不起作用。

import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"; import pygame
import random
import time
import copy
black = pygame.Color(0, 0, 0)

class Snake():
    def __init__(self, game_window, controls, color, position, speed, window_specs,can_touch=True,respawn=True):

        self.game_window = game_window
        self.color = color
        self.window_x = window_specs[0]
        self.window_y = window_specs[1]

        self.can_touch = can_touch
        self.respawn = respawn

        self.snake_position = position.copy()
        self.snake_body = [position.copy()]
        self.speed = speed
        self.controls = controls.copy()
        
        self.direction = 'RIGHT'
        self.change_to = self.direction
        
        self.moving = True
        self.fps = pygame.time.Clock()
        self.start_moving()

    def start_moving(self):
        #Handles the controls and movement. Control first snake with 'wasd'. Control second snake with arrow keys.
        while self.moving == True:
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == self.controls[0]:
                        self.change_to = 'UP'
                    if event.key == self.controls[1]:
                        self.change_to = 'LEFT'
                    if event.key == self.controls[2]:
                        self.change_to = 'DOWN'
                    if event.key == self.controls[3]:
                        self.change_to = 'RIGHT'
            if self.change_to == 'UP' and self.direction != 'DOWN':
                self.direction = 'UP'
            if self.change_to == 'DOWN' and self.direction != 'UP':
                self.direction = 'DOWN'
            if self.change_to == 'LEFT' and self.direction != 'RIGHT':
                self.direction = 'LEFT'
            if self.change_to == 'RIGHT' and self.direction != 'LEFT':
                self.direction = 'RIGHT'

            if self.direction == 'UP':
                self.snake_position[1] -= 10
            if self.direction == 'LEFT':
                self.snake_position[0] -= 10
            if self.direction == 'DOWN':
                self.snake_position[1] += 10
            if self.direction == 'RIGHT':
                self.snake_position[0] += 10
            self.snake_body.insert(0, list(self.snake_position))
            self.snake_body.pop()
            
            self.game_window.fill(black)
            for pos in self.snake_body:
                pygame.draw.rect(self.game_window,self.color,pygame.Rect(pos[0],pos[1],10,10))

            #Toggles whether snake will die if touching itself
            if self.can_touch == False:
                for block in self.snake_body[1:]:
                    if self.snake_position[0] == block[0] and self.snake_position[1] == block[1]:
                        self.die()

            #Kills snake if snake touches the edge of the window
            if self.snake_position[0] < 0 or self.snake_position[0] > (self.window_x - 10):
                self.die()
            if self.snake_position[1] < 0 or self.snake_position[1] > (self.window_y - 10):
                self.die()
            pygame.display.update()
            self.fps.tick(self.speed)

    def die(self):
        
        #Reset the snake
        self.moving = False
        self.game_window.fill(black)
        self.snake_body = []
        self.position = []
        pygame.display.update()
        time.sleep(1)
        
        #Makes the snake respawn in a random location
        if self.respawn == True:
            self.direction = 'RIGHT'
            self.change_to = self.direction
            self.snake_position = [random.randrange(1, (self.window_x//100)) * 10,
                     random.randrange(1, (self.window_y//100)) * 10]
            self.snake_body = [self.snake_position.copy()]
            self.moving = True
            self.start_moving()

class App():
    def __init__(self,num_players,can_touch=True,respawn=True):
        self.controls = [[pygame.K_w, pygame.K_a, pygame.K_s, pygame.K_d],
                                [pygame.K_UP,pygame.K_LEFT,pygame.K_DOWN,pygame.K_RIGHT]]
        self.starting_positions = [[100,50], [200, 100]]
        self.speed = 15
        self.window_specs = [720,480]
        self.num_players = num_players
        self.can_touch = can_touch
        self.respawn = respawn
        self.players = {}

        #Start up pygame
        pygame.init()
        pygame.display.set_caption('Snake Game')
        self.game_window = pygame.display.set_mode((self.window_specs[0], self.window_specs[1]))

        self.get_players()

    def get_players(self):
        #Let the players choose what color they want
        player_color = []
        for i in range(self.num_players):
            player_color.append(input(f'Player {i+1}, what color do you want your snake to be? '))
            if player_color[i] == 'red':
                player_color[i] = pygame.Color(255,0,0)
            elif player_color[i] == 'green':
                player_color[i] = pygame.Color(0,255,0)
            elif player_color[i] == 'blue':
                player_color[i] = pygame.Color(0,0,255)
            else:
                player_color[i] = pygame.Color(255,255,255)

        #Create the Snakes, this is the bit that does not work.
        for i in range(self.num_players):
            self.players[f'player{i+1}'] = Snake(self.game_window,self.controls[i],player_color[i],self.starting_positions[i],self.speed,self.window_specs)

if __name__ == "__main__":
    num_players = int(input('1 or 2 players: '))
    App(num_players)

这里的问题是您没有为该游戏设置专用的游戏循环。您的游戏的“游戏循环”现在位于单个蛇对象中。你的 Snake.start_moving 函数是保持在 运行ning 上的东西,你看到你的蛇移动的地方等。在你创建一条蛇的情况下,你是 运行ning 那是蛇的 start_moving 函数,所以表面上一切看起来都很好。

如果你是运行 2条蛇,第一条蛇的start_moving函数被调用,你被困在那里。第二条蛇还没有被创造出来。为了说服自己,尝试在函数外添加一个 if event.key is q, return 。然后你会看到当你要两条蛇时,第一条蛇会运行,然后你按q,第一条蛇消失,第二条蛇出现

解决您的问题的方法是在您的项目中创建一个专用的游戏循环,并将所有游戏更新委托给那里。有几种不同的方法可以做到这一点,所以我会把它留给你来设计。但总的来说,这是我在伪代码中推荐的:

snakes = [Snake() for _ in range(numplayers)]

while True:
    for event in pg.events():
        if event is keypress:
            for snake in snakes:
                snake.handle_keys(event.key)
            
    for snake in snakes:
        snake.update(dt)

    for snake in snakes:
        snake.draw(display)
    display.flip()
    tick() # or calculate dt

整个应用程序需要一个应用程序循环,而不是每条蛇都需要一个单独的应用程序。从 Snake class:

中删除应用程序循环
class Snake():
    def __init__(self, game_window, controls, color, position, window_specs,can_touch=True,respawn=True):

        self.game_window = game_window
        self.color = color
        self.window_x = window_specs[0]
        self.window_y = window_specs[1]

        self.can_touch = can_touch
        self.respawn = respawn

        self.snake_position = position.copy()
        self.snake_body = [position.copy()]
        self.controls = controls.copy()
        self.direction = (1, 0)
        
    def move(self, event_list):
        #Handles the controls and movement. Control first snake with 'wasd'. Control second snake with arrow keys.
        for event in event_list:
            if event.type == pygame.QUIT:
                pygame.quit()
            if event.type == pygame.KEYDOWN:
                if event.key == self.controls[0] and self.direction[1] <= 0:
                    self.direction = (0, -1)
                if event.key == self.controls[1] and self.direction[0] <= 0:
                    self.direction = (-1, 0)
                if event.key == self.controls[2] and self.direction[1] >= 0:
                    self.direction = (0, 1)
                if event.key == self.controls[3] and self.direction[0] >= 0:
                    self.direction = (1, 0)

        self.snake_position[0] += self.direction[0] * 10
        self.snake_position[1] += self.direction[1] * 10
        self.snake_body.insert(0, list(self.snake_position))
        self.snake_body.pop()
        
        for pos in self.snake_body:
            pygame.draw.rect(self.game_window,self.color,pygame.Rect(pos[0],pos[1],10,10))

        #Toggles whether snake will die if touching itself
        if self.can_touch == False:
            for block in self.snake_body[1:]:
                if self.snake_position[0] == block[0] and self.snake_position[1] == block[1]:
                    self.die()

        #Kills snake if snake touches the edge of the window
        if self.snake_position[0] < 0 or self.snake_position[0] > (self.window_x - 10):
            self.die()
        if self.snake_position[1] < 0 or self.snake_position[1] > (self.window_y - 10):
            self.die()

    def die(self):
        self.snake_body = []
        self.position = []
        
        #Makes the snake respawn in a random location
        if self.respawn == True:
            self.direction = (1, 0)
            self.snake_position = [random.randrange(1, (self.window_x//100)) * 10,
                     random.randrange(1, (self.window_y//100)) * 10]
            self.snake_body = [self.snake_position.copy()]

但是在App中添加一个应用程序循环:

class App():
    # [...]

    def get_players(self):
        #Let the players choose what color they want
        player_color = []
        color_dict = {'red': (255, 0, 0), 'green': (0, 255, 0), 'blue': (0, 0, 255)}
        for i in range(self.num_players):
            color = input(f'Player {i+1}, what color do you want your snake to be? ')
            player_color.append(color_dict[color] if color in color_dict else (255, 255, 255))

        #Create the Snakes, this is the bit that does not work.
        for i in range(self.num_players):
            self.players[f'player{i+1}'] = Snake(self.game_window,self.controls[i],player_color[i],self.starting_positions[i],self.window_specs)

        clock = pygame.time.Clock()
        run = True
        while run: 
            clock.tick(self.speed)  
            event_list = pygame.event.get()
            for event in event_list:
                if event.type == pygame.QUIT:
                    run = False

            self.game_window.fill(black)  
            for key in self.players:
                self.players[key].move(event_list)
            pygame.display.update()     
                   
        pygame.quit()