代码之家  ›  专栏  ›  技术社区  ›  K. Russell Smith

Pygame事件循环在没有用户输入的情况下阻止游戏

  •  1
  • K. Russell Smith  · 技术社区  · 2 年前

    我用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样本进行了比较,但一切看起来都很好,那么发生了什么?

    1 回复  |  直到 2 年前
        1
  •  3
  •   Rabbid76    2 年前

    您必须在应用程序循环中运行游戏逻辑并绘制场景,但不能在事件循环中运行。事件循环仅在事件发生时执行,但应用程序循环每帧执行一次,帧数由 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()