【问题标题】:Python + PyGame - handling simultaneous mouse and keyboard eventsPython + PyGame - 处理同时发生的鼠标和键盘事件
【发布时间】:2012-04-07 19:03:00
【问题描述】:

我完全愿意接受我的硬件是这里问题的原因的想法,但是我真的不这么认为,因为我确实看到计算机使用其他软件同时处理这两种输入/游戏/等,所以我猜这里的错误是我对 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 而忽略另一个感到困惑(根据我的经验,无论哪个先出现)。

【问题讨论】:

  • 处理事件的代码在哪里???你只检查它是否是EXIT,然后忽略其他!
  • 每个对象或多或少地处理自己的事件,方法是将它们放入队列中,然后将事件传递给“for thing in queue: thing.update(event)”。也许这是问题的一部分?我愿意接受。但它使实际的游戏循环看起来漂亮而整洁,而不是将每个对象对事件的个人响应放入游戏循环中

标签: python event-handling pygame


【解决方案1】:

看看这个!

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 中检索它们)。您制作了获取每个事件的循环。但是后来你“更新”了你的游戏状态,而不是每一个,而是最后一个!我认为纠正缩进就足够了。

【讨论】:

  • 如果我按照你建议的方式缩进,我会得到一个漂亮的空白屏幕,直到我晃动鼠标。 :)
  • 在每个对象中分开 update() 和 draw()!!然后在队列中添加 for thing:thing.draw() 代替原来的 for。因此,您绘制每一帧,并在每个事件中更新对象!
  • 然后你会意识到你没有更新子弹的运动!
  • 然后您发明解决方案!这是主游戏循环的 3 个组成部分的分离:处理每个用户事件的部分、处理与用户事件无关的游戏状态更新的部分以及在屏幕上绘制的部分。
  • 虽然您的缩进建议没有帮助,但您关于正确分离我的游戏循环的 cmets 确实有效。通过将 Player 对象的 update() 拆分为 event()、update() 和 draw(),我能够解决问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-06
相关资源
最近更新 更多