Python 3非阻塞同步行为

前端之家收集整理的这篇文章主要介绍了Python 3非阻塞同步行为 前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

我正在使用Pygame在python3中制作经典的atari蛇游戏.我想产生一个子进程来监听按键,以便每当玩家输入键(上,下,左或右)时,该子进程就会发送父进程钥匙.但是该管道不应阻塞,以便蛇可以沿其行进的方向行进,直到收到钥匙为止.

我在multi-processes上找到了Python的官方文档,但是它没有描述我想要的行为,或者至少没有记录示例用法是否受阻.有人可以举一个例子说明如何实现吗?

最佳答案
你说:

I want to create an interface for an AI to take control of the snake. It wouldn’t be fair if the state of the game is simply passed to the AI on each iteration b/c it could then just take as long as it want to compute the next move. Hence why it should be synchronous and non-blocking.

因此,要获得所需的内容,您需要一个抽象.在下面的示例中,我创建了一个做到这一点的Controller类. KeyboardController处理键盘输入,而AsyncController启动线程并使用Queue类传递游戏状态和“ AI”的决定.请注意,您必须在主线程上获取pygame事件,因此我在主循环中执行此操作,并将事件简单地传递给控制器​​.

您的AI必须由worker函数调用.如您所见,当前,辅助函数中的“ AI”仅每0.5秒起作用一次,而帧速率为120.对于游戏来说,AI花费这么长时间做出决定并不重要.

这是代码

import pygame
import time
import random
from queue import Queue,Empty
from threading import Thread

class Controller():
    def __init__(self,color,message,actor):
        self.color = color
        self.message = message
        if actor: self.attach(actor)

    def attach(self,actor):
        self.actor = actor
        self.actor.controller = self
        self.actor.image.fill(self.color)

class AsyncController(Controller):
    def __init__(self,actor=None):
        super().__init__(pygame.Color('orange'),"AI is in control.",actor)
        self.out_queue = Queue()
        self.in_queue  = Queue()
        t = Thread(target=self.worker)
        t.daemon = True
        t.start()

    def update(self,events,dt):
        for e in events:
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_SPACE: self.actor.controller = KeyboardController(self.actor)

        self.out_queue.put_nowait((self.actor,dt))
        try: return self.in_queue.get_nowait()
        except Empty: pass

    def worker(self):
        while True:
            try:
                actor,dt = self.out_queue.get_nowait()
                if actor.rect.x < 100: self.in_queue.put_nowait(pygame.Vector2(1,0))
                if actor.rect.x > 600: self.in_queue.put_nowait(pygame.Vector2(-1,0))
                if actor.rect.y < 100: self.in_queue.put_nowait(pygame.Vector2(0,1))
                if actor.rect.y > 400: self.in_queue.put_nowait(pygame.Vector2(0,-1))
                if random.randrange(1,100) < 15:
                    self.in_queue.put_nowait(random.choice([
                        pygame.Vector2(1,0),pygame.Vector2(-1,pygame.Vector2(0,-1),1)]))

                time.sleep(0.5)
            except Empty:
                pass

class KeyboardController(Controller):
    def __init__(self,actor=None):
        super().__init__(pygame.Color('dodgerblue'),"You're in control.",actor)

    def update(self,dt):
        for e in events:
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_SPACE: self.actor.controller = AsyncController(self.actor)
                if e.key == pygame.K_UP: return pygame.Vector2(0,-1)
                if e.key == pygame.K_DOWN: return pygame.Vector2(0,1)
                if e.key == pygame.K_LEFT: return pygame.Vector2(-1,0)
                if e.key == pygame.K_RIGHT: return pygame.Vector2(1,0)

class Actor(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((32,32))
        self.image.fill(pygame.Color('dodgerblue'))
        self.rect = self.image.get_rect(center=(100,100))
        self.direction = pygame.Vector2(1,0)
        self.pos = self.rect.center

    def update(self,dt):
        new_direction = self.controller.update(events,dt)
        if new_direction:
            self.direction = new_direction
        self.pos += (self.direction * dt * 0.2)
        self.rect.center = self.pos

def main():
    pygame.init()

    actor   = Actor()
    sprites = pygame.sprite.Group(actor)
    screen  = pygame.display.set_mode([800,600])
    clock   = pygame.time.Clock()
    font    = pygame.font.SysFont("consolas",20,True)
    dt      = 0
    KeyboardController(actor)

    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        sprites.update(events,dt)
        screen.fill(pygame.Color('grey12'))
        screen.blit(font.render(actor.controller.message + ' [SPACE] to change to keyboard control.',True,pygame.Color('white')),(10,10))
        sprites.draw(screen)
        dt = clock.tick(120)
        pygame.display.update()

if __name__ == '__main__':
    main()

enter image description here

请注意,此实现使用无限队列.您想添加一些逻辑来清除队列,以免您的游戏占用大量内存.

原文链接:https://www.f2er.com/python/533054.html

猜你在找的Python相关文章