我正在使用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()
请注意,此实现使用无限队列.您想添加一些逻辑来清除队列,以免您的游戏占用大量内存.