使用 cocos2d-python 和 pyglet 进行密钥轮询

Key polling with cocos2d-python and pyglet

我正在尝试在按下某个键时移动精灵。我可以用 on_key_press() 和 on_key_release() 来做到这一点,但是有了这些我 运行 遇到了在按住左键的同时向右移动的问题,反之亦然。我想使用密钥轮询并从 pyglet 文档中找到它。

from pyglet.window import key

window = pyglet.window.Window()
keys = key.KeyStateHandler()
window.push_handlers(keys)

# Check if the spacebar is currently pressed:
if keys[key.SPACE]:
    pass    

我似乎无法在 cocos2d 中实现这一点。下面是一个简单的例子。它打印 'Key Press!' 就好了,但是如果我可以在按下 space 键时让它重复打印 'Space!' ,它就会解决我的问题。

from cocos.director import director
from cocos.layer import *
from cocos.scene import Scene
from pyglet.window import key
from pyglet.window.key import KeyStateHandler

class MyLayer(Layer):
    is_event_handler = True

    def __init__(self):
        super(MyLayer, self).__init__()
        self.keys = KeyStateHandler()
        director.window.push_handlers(self.keys)

    def on_key_press(self, symbol, modifiers):
        print('Key press!')
        if self.keys[key.SPACE]:
            print('Space!')

def main():
    director.init(resizable=False, width=1024, height=786)
    title_scene = Scene(MyLayer())
    director.run(title_scene)

if __name__ == '__main__':
    main()

为了完整性,这里是我的 on_key_press(), on_key_release() 代码。问题是,如果我按 Right,按 Left,释放 Left,我的 Sprite 将停止,因为 on_key_release() 将 x 速度设置为零。但是,我仍然按向右键,所以我想在按下并松开向左键后继续朝那个方向移动。

def on_key_press(self, symbol, modifiers):
    if symbol == key.LEFT:
        self.player1.velocity = (-self.player1.speed, self.player1.velocity[1])
    if symbol == key.RIGHT:
        self.player1.velocity = (self.player1.speed, self.player1.velocity[1])

def on_key_release(self, symbol, modifiers):
    if symbol == key.LEFT:
        self.player1.velocity = (0, self.player1.velocity[1])
    if symbol == key.RIGHT:
        self.player1.velocity = (0, self.player1.velocity[1])

来自 pyglet guide:

The Window.on_key_press and Window.on_key_release events are fired when any key on the keyboard is pressed or released, respectively. These events are not affected by "key repeat" -- once a key is pressed there are no more events for that key until it is released.

这意味着如果你按下右,按下左,释放左,但不释放右,那么在你释放右并再次按下之前,新的 on_key_press 事件将不会被调度。

如果保留现有结构,我认为您必须检查 self.keys 中是否设置了 key.RIGHT,在 on_key_release 中。但仅此一点是行不通的。使用当前代码,您总是会得到 keys[key.RIGHT]False。接下来我会解释这是为什么。

当您调用 director.window.push_handlers(self.keys) 时,您将 KeyStateHandler 自己的 on_key_presson_key_release 处理程序注册到与注册 MyLayer 的相应处理程序相同的队列中。 KeyStateHandler 的处理程序所做的是它们在 keys dict 中设置和取消设置键,以便您可以查询 keys[key.SPACE] 以查看 space 键是否被按住。

代码的问题在于您在 MyLayer 的处理程序之前注册了 KeyStateHandler 的处理程序,这导致 MyLayer 的处理程序在 KeyStateHandler 的处理程序之前被调用。所以当你按下 space 并检查 MyLayer.on_key_press 是否 keys[key.SPACE]True 时,它不是,因为 KeyStateHandler 还没有设置 keys[key.SPACE]

要在 MyLayer 的处理函数之前调用 KeyStateHandler 的处理函数,您不应该在 MyLayer 的构造函数中注册(=推送)它们,但在 MyLayer 的 on_enter 函数中是这样的:

def on_enter(self):
    super(MyLayer, self).on_enter()
    director.window.push_handlers(self.keys)

如果你这样做,你可能还应该 remove/deregister MyLayer 的 on_exit 函数中的那些处理程序。

如果您对代码片段进行此更改,它应该在您每次按 space 时打印 'Key press!' 和 'Space!',但不会重复,因为 on_key_press 事件是每次按键仍仅发送一次。

您可能已经安排了一些带有 CocosNode.schedule, or defined some Action that uses self.player1.velocity to move your Sprite (inheriting from the Move 操作的功能将是执行此操作的好方法)。获得所需效果的一种方法是不定义自己的 on_key_press 和 on_key_release 处理函数,而是依赖 KeyStateHandler。您将 KeyStateHandler 的处理函数推送到您的代码片段中,并在您从 keys dict 中读取的计划函数或操作步骤的开始,按下哪些键,并根据它更新速度。

例如,要使用时间表重复打印 'Space!':

from cocos.director import director
from cocos.layer import *
from cocos.scene import Scene
from pyglet.window import key
from pyglet.window.key import KeyStateHandler

class MyLayer(Layer):

    def __init__(self):
        super(MyLayer, self).__init__()
        self.keys = KeyStateHandler()
        director.window.push_handlers(self.keys)
        self.schedule(self.act_on_input)

    def act_on_input(self, dt):
        if self.keys[key.SPACE]:
            print('Space!')

def main():
    director.init(resizable=False, width=1024, height=786)
    title_scene = Scene(MyLayer())
    director.run(title_scene)

if __name__ == '__main__':
    main()

请注意,其工作方式是在 KeyStateHandler 的 on_key_press 处理程序中将 self.keys[key.SPACE] 设置为 True,每次按键仅调用一次。当您释放键时,字典条目在 KeyStateHandler 的 on_key_release 处理程序中设置为 False