如果没有用户输入,Pygame 事件循环会阻止游戏

Pygame event loop blocks game if there's no user input

提问人:K. Russell Smith 提问时间:9/2/2023 最后编辑:Rabbid76K. Russell Smith 更新时间:9/2/2023 访问量:26

问:

我摸索了一个我正在用 Python 编写的贪吃蛇游戏,它出现的主循环有问题

对于任何草率,提前道歉,我仍在原型设计中:

import sys
import pygame
from pygame.locals import *

from libs.Touch import Touch

def sortPairs(*pairs):
    result = {}
    for pair in pairs:
        result[pair[0]] = pair[1]
        result[pair[1]] = pair[0]
    return result

DIR = {
    'LEFT':  (-1, +0),
    'RIGHT': (+1, +0),
    'UP':    (+0, -1),
    'DOWN':  (+0, +1)
}
DIR['INVERSE'] = sortPairs(
    (DIR['LEFT'], DIR['RIGHT']),
    (DIR['UP'],   DIR['DOWN']))

class Snake:
    def __init__(self,x, y):
        self.dir   = DIR['RIGHT']
        self.nodes = [[x, y]]
    
    def move(self):
        for i in range(len(self.nodes) - 1, 0, -1):
            self.nodes[i][0] = self.nodes[i - 1][0]
            self.nodes[i][1] = self.nodes[i - 1][1]
            
        self.nodes[0][0] += self.dir[0]
        self.nodes[0][1] += self.dir[1]
    
    def turn(self, turn):
        dir = DIR[turn]
        if len(self.nodes) == 1 or dir != DIR['INVERSE'][turn]:
            self.dir = dir
    
    def grow(self):
        self.nodes.appendLeft([
            self.nodes[0][0] + self.dir[0],
            self.nodes[0][1] + self.dir[1]])
    
    def draw(self, surface,x, y, node_size):
        rect = pygame.Rect(
            0, 0, node_size, node_size)
        
        for node in self.nodes:
            rect.left   = x + node[0] * node_size
            rect.top    = y + node[1] * node_size
            rect.right  = rect.left + node_size     
            rect.bottom = rect.top + node_size
            
            
            pygame.draw.rect(
                surface,
                pygame.Color("#FF0000"),
                rect)

class Game:
    def __init__(self, size, tick):
        self.size = size
        self.tick = tick
        self.snake = Snake(0, 0)
        
    def draw(self, surface):
        surface.fill(pygame.Color('#161616'))
        
        cell_size = min(
            surface.get_width(),
            surface.get_height()) / self.size
        
        bounds_stroke = int(cell_size / 10)
        
        bounds = pygame.Rect(
            surface.get_width() / 2,
            surface.get_height() / 2,
            cell_size * self.size - bounds_stroke,
            cell_size * self.size - bounds_stroke)
        bounds.center = (bounds.x, bounds.y)
        
        self.snake.draw(
            surface,
            int(bounds.left),
            int(bounds.top),
            int(cell_size))
        
        pygame.draw.rect(
            surface,
            pygame.Color('#6f91b3'),
            bounds,
            bounds_stroke)
        
    def update(self):
        self.snake.move()
    
    def handle_event(self, event):
        if event.type == MOUSEBUTTONDOWN:
            pygame.mouse.get_rel()
        if event.type == MOUSEBUTTONUP:
            swipe = Touch.reg_swipe(*pygame.mouse.get_rel())
            if swipe != -1:
                self.snake.turn(swipe)
            
        if event.type == QUIT:
            pygame.quit()

def main():
    game = Game(30, 100)
    
    pygame.init()
    
    # Resolution is ignored on Android
    surface = pygame.display.set_mode((640, 480))
    
    clock = pygame.time.Clock()
    
    delta = 0
    
    while True:
        for ev in pygame.event.get():
            game.handle_event(ev)
            
            delta += clock.tick(60)
            
            if delta >= game.tick:
                game.update()
                delta = 0
            
            game.draw(surface)
            pygame.display.flip()

if __name__ == '__main__':
    main()

./libs/Touch.py:

class Touch:
    max_touch_dist = 15
    min_swipe_dist = 50
    
    @staticmethod
    def reg_swipe(rel_x, rel_y):
        min = Touch.min_swipe_dist
        if abs(rel_y) < min:
            if rel_x <= -min:
                return 'LEFT'
            elif rel_x >= min:
                return 'RIGHT'
        if rel_y <= -min:
            return 'UP'
        elif rel_y >= min:
            return 'DOWN'
            
        return -1

我在手机上使用 Pydroid,但在在线解释器上测试它得到了相同的结果:游戏运行 2 次迭代,然后冻结,然后仅在我触摸屏幕(或触发我想象的事件)时运行),我已经将我的代码(尤其是事件循环)与 Pygame 示例进行了比较,但一切看起来都很好,那么这是怎么回事?

蟒蛇 皮游戏

评论


答:

3赞 Rabbid76 9/2/2023 #1

您必须在应用程序循环中运行游戏逻辑并绘制场景,但不能在事件循环中绘制场景。仅当事件发生时才执行事件循环,但应用程序循环每帧执行一次,帧数由 控制。clock.tick(60)

def main():
    # [...]

    while True:
        for ev in pygame.event.get():
            game.handle_event(ev)
            
        delta += clock.tick(60)    
        if delta >= game.tick:
            game.update()
            delta = 0
            
        game.draw(surface)
        pygame.display.flip()

评论

0赞 K. Russell Smith 9/2/2023
哎呀,我没有注意到我的缩进......多么尴尬。谢谢