Skip to main content
 首页 » 编程设计

Python + PyGame之处理同时发生的鼠标和键盘事件

2025年05月04日13oomusou

我完全愿意承认我的硬件是这里问题的原因,但老实说我不这么认为,因为我确实看到计算机使用其他软件同时处理两种输入/游戏/等等,所以我猜这里的错误是我对 PyGame 事件处理程序的方法。

我正在随意使用 Python 和 PyGame,并且只是试图一次积累我的知识,并通过在我学习的过程中构建“游戏”来表达这些知识。这在很大程度上是一项正在进行的工作,没有实现碰撞检测或记分之类的任何东西,我认为可以稍后实现。

这里的相关难题是游戏将执行 MOUSEMOTION 事件和 KEYDOWN 事件,它似乎不想同时处理它们。 “玩家”对象在移动时不能射击,在射击时不能移动。由于大多数游戏玩家都喜欢边射击边移动的奢侈享受,我认为这是一个障碍。

import pygame, random, sys 
from pygame.locals import * 
 
pygame.init() 
 
width = 640 
height = 480 
 
 
DISPLAYSURF = pygame.display.set_mode((width, height)) 
pygame.display.set_caption('It moves!') 
pygame.mouse.set_visible(0) 
 
 
 
class Player(pygame.sprite.Sprite): 
 
    def __init__(self, x, y): 
        pygame.sprite.Sprite.__init__(self) 
 
        self.x = x 
        self.y = y 
        self.width = 50 
        self.height = 25 
        self.playerRect = None 
 
 
 
    def update(self, event): 
        if event.type == MOUSEMOTION: 
            self.x, self.y = event.pos 
 
 
        #get a new playerRect and draw it 
        self.playerRect = pygame.Rect(self.x, self.y, self.width, self.height) 
        pygame.draw.ellipse(DISPLAYSURF, RED, (self.playerRect), 3) 
 
 
    def shotcheck(self, event): 
        if event.type == KEYDOWN: 
            if event.key == K_KP8: 
                return (True, 'up') 
            elif event.key == K_KP2: 
                return (True, 'down') 
            elif event.key == K_KP4: 
                return (True, 'left') 
            elif event.key == K_KP6: 
                return (True, 'right') 
            elif event.key == K_KP7: 
                return (True, 'upleft') 
            elif event.key == K_KP1: 
                return (True, 'downleft') 
            elif event.key == K_KP9: 
                return (True, 'upright') 
            elif event.key == K_KP3: 
                return (True, 'downright') 
            else: 
                return (0, 0) 
 
 
 
class Enemy(pygame.sprite.Sprite): 
    def __init__(self, x, y): 
        pygame.sprite.Sprite.__init__(self) 
        self.x = x 
        self.y = y 
        #self.body = pygame.rect.Rect(self.x, self.y, 15, 15) 
        self.speed = 5 
        self.xmove = 0 
        self.ymove = 0 
 
 
 
    def update(self, event): 
        self.x += self.speed 
        if self.x > 350: 
            self.speed *= -1 
        elif self.x < 25: 
            self.speed *= -1 
 
        pygame.draw.rect(DISPLAYSURF, BLUE, (self.x, self.y, 15, 15), 4) 
 
 
 
#pass it a directional value when fired based on the key 
#may have to divide speed / 2 if moving diagonally 
class Bullet(pygame.sprite.Sprite): 
    def __init__(self, x, y, direction): 
        pygame.sprite.Sprite.__init__(self) 
        self.x = x 
        self.y = y 
        self.direction = direction 
        self.width = 4 
        self.height = 4 
        self.bulletRect = None 
        self.speed = 8 
 
 
 
    def update(self, event): 
 
        if self.direction == 'up': 
            self.y -= self.speed 
 
        elif self.direction == 'down': 
            self.y += self.speed 
 
        elif self.direction == 'left': 
            self.x -= self.speed 
 
        elif self.direction == 'right': 
            self.x += self.speed 
 
        elif self.direction == 'upleft': 
            self.x -= (self.speed/2) 
            self.y -= (self.speed/2) 
 
        elif self.direction == 'downleft': 
            self.x -= (self.speed/2) 
            self.y += (self.speed/2) 
 
        elif self.direction == 'upright': 
            self.x += (self.speed/2) 
                self.y -= (self.speed/2) 
 
        elif self.direction == 'downright': 
            self.x += (self.speed/2) 
            self.y += (self.speed/2) 
 
 
        self.bulletRect = pygame.Rect(self.x, self.y, 4, 4) 
        pygame.draw.ellipse(DISPLAYSURF, GREEN, (self.bulletRect), 2) 
 
 
 
 
 
FPS = 30 
fpsClock = pygame.time.Clock() 
 
 
RED = (255, 0, 0) 
GREEN = (0, 255, 0) 
BLUE = (0, 0, 255) 
BLACK = (0, 0, 0) 
 
 
 
ship = Player(width / 2, height / 2) 
bads = Enemy(width / 2, height / 2) 
 
 
 
queue = pygame.sprite.Group() 
queue.add(ship) 
queue.add(bads) 
 
 
while True: 
    DISPLAYSURF.fill(BLACK) 
    for event in pygame.event.get(): 
        if event.type == QUIT: 
            sys.exit() 
 
 
        #passes 'event' to everything in the queue and calls 
        #their obj.update().  in this way the gameloop  
        #is a bit more readable 
    for thing in queue: 
        thing.update(event) 
 
    try: #i'm not married to this bit of code :/ 
        checkForShot, shotDirection = ship.shotcheck(event) 
        if checkForShot: 
            shotx, shoty = ship.playerRect.center 
            shot = Bullet(shotx, shoty, shotDirection) 
            queue.add(shot) 
    except TypeError: 
        pass 
 
    pygame.display.flip() 
    fpsClock.tick(FPS) 

我知道这基本上会产生一个真正没有灵感的 Robotron 克隆,但正如我所说,这是我的一个蹒跚学步的项目,我在运行在线教程时将其放在一起。是的,现在有一个不必要的“随机导入”,稍后会很重要。

我猜有几个问题;对于初学者来说,我不喜欢处理子弹创建的方式(在我看来,玩家对象应该将它们添加到游戏队列本身而不是返回 True/False 元组,但这似乎有点不直观让播放器对象直接提及队列。用 try/except 处理它感觉很懒惰,但也许我很挑剔)。然而,我也感觉到这个问题等同于弄清楚如何处理让事件处理程序正确地处理 thing.update() 以同时移动 (MOUSEMOTION) 和射击 (KEYDOWN)。

我还猜测,为了让它表现得更“像人们期望的那样”,我需要告诉它也处理 KEYUP 事件。但是,我仍然对为什么事件处理程序似乎选择一个 event.type 而忽略另一个感到困惑(根据我的经验,无论哪个先出现)。

请您参考如下方法:

检查一下!

import pygame, random, sys 
from pygame.locals import * 
 
pygame.init() 
 
width = 640 
height = 480 
 
 
DISPLAYSURF = pygame.display.set_mode((width, height)) 
pygame.display.set_caption('It moves!') 
pygame.mouse.set_visible(0) 
 
 
 
class Player(pygame.sprite.Sprite): 
 
    def __init__(self, x, y): 
        pygame.sprite.Sprite.__init__(self) 
 
        self.x = x 
        self.y = y 
        self.width = 50 
        self.height = 25 
        self.playerRect = None 
 
 
 
    def update(self, event): 
        if event.type == MOUSEMOTION: 
            self.x, self.y = event.pos 
 
 
        #get a new playerRect and draw it 
        self.playerRect = pygame.Rect(self.x, self.y, self.width, self.height) 
        pygame.draw.ellipse(DISPLAYSURF, RED, (self.playerRect), 3) 
 
 
    def shotcheck(self, event): 
        if event.type == KEYDOWN: 
            if event.key == K_KP8: 
                return (True, 'up') 
            elif event.key == K_KP2: 
                return (True, 'down') 
            elif event.key == K_KP4: 
                return (True, 'left') 
            elif event.key == K_KP6: 
                return (True, 'right') 
            elif event.key == K_KP7: 
                return (True, 'upleft') 
            elif event.key == K_KP1: 
                return (True, 'downleft') 
            elif event.key == K_KP9: 
                return (True, 'upright') 
            elif event.key == K_KP3: 
                return (True, 'downright') 
            else: 
                return (0, 0) 
 
 
 
class Enemy(pygame.sprite.Sprite): 
    def __init__(self, x, y): 
        pygame.sprite.Sprite.__init__(self) 
        self.x = x 
        self.y = y 
        #self.body = pygame.rect.Rect(self.x, self.y, 15, 15) 
        self.speed = 5 
        self.xmove = 0 
        self.ymove = 0 
 
 
 
    def update(self, event): 
        self.x += self.speed 
        if self.x > 350: 
            self.speed *= -1 
        elif self.x < 25: 
            self.speed *= -1 
 
        pygame.draw.rect(DISPLAYSURF, BLUE, (self.x, self.y, 15, 15), 4) 
 
 
 
#pass it a directional value when fired based on the key 
#may have to divide speed / 2 if moving diagonally 
class Bullet(pygame.sprite.Sprite): 
    def __init__(self, x, y, direction): 
        pygame.sprite.Sprite.__init__(self) 
        self.x = x 
        self.y = y 
        self.direction = direction 
        self.width = 4 
        self.height = 4 
        self.bulletRect = None 
        self.speed = 8 
 
 
 
    def update(self, event): 
 
        if self.direction == 'up': 
            self.y -= self.speed 
 
        elif self.direction == 'down': 
            self.y += self.speed 
 
        elif self.direction == 'left': 
            self.x -= self.speed 
 
        elif self.direction == 'right': 
            self.x += self.speed 
 
        elif self.direction == 'upleft': 
            self.x -= (self.speed/2) 
            self.y -= (self.speed/2) 
 
        elif self.direction == 'downleft': 
            self.x -= (self.speed/2) 
            self.y += (self.speed/2) 
 
        elif self.direction == 'upright': 
            self.x += (self.speed/2) 
                self.y -= (self.speed/2) 
 
        elif self.direction == 'downright': 
            self.x += (self.speed/2) 
            self.y += (self.speed/2) 
 
 
        self.bulletRect = pygame.Rect(self.x, self.y, 4, 4) 
        pygame.draw.ellipse(DISPLAYSURF, GREEN, (self.bulletRect), 2) 
 
 
 
 
 
FPS = 30 
fpsClock = pygame.time.Clock() 
 
 
RED = (255, 0, 0) 
GREEN = (0, 255, 0) 
BLUE = (0, 0, 255) 
BLACK = (0, 0, 0) 
 
 
 
ship = Player(width / 2, height / 2) 
bads = Enemy(width / 2, height / 2) 
 
 
 
queue = pygame.sprite.Group() 
queue.add(ship) 
queue.add(bads) 
 
 
while True: 
    DISPLAYSURF.fill(BLACK) 
    for event in pygame.event.get(): 
        if event.type == QUIT: 
            sys.exit() 
 
 
                #passes 'event' to everything in the queue and calls 
                #their obj.update().  in this way the gameloop  
                #is a bit more readable 
            for thing in queue: 
                thing.update(event) 
 
    try: #i'm not married to this bit of code :/ 
        checkForShot, shotDirection = ship.shotcheck(event) 
        if checkForShot: 
            shotx, shoty = ship.playerRect.center 
            shot = Bullet(shotx, shoty, shotDirection) 
            queue.add(shot) 
    except TypeError: 
        pass 
 
    pygame.display.flip() 
    fpsClock.tick(FPS) 

问题出在处理拉取(从 pygame 中检索它们)的代码中。您制作了获取每个事件的循环。但随后您“更新”了您的游戏状态,而不是每一个,而是最后一个!我认为纠正缩进就足够了。