Reputation: 15
I'm trying to move a sprite while a key is pressed. I can do it with on_key_press() and on_key_release(), but with these I run into problems with moving right while holding left and vice versa. I'd like to use key polling and found this from the pyglet documentation.
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
I don't seem to be able to implement this in cocos2d. A simple example is below. It prints 'Key Press!' just fine, but if I could get it to print 'Space!' repeatedly when the space key is pressed it would solve my problem.
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()
For completeness here is my on_key_press(), on_key_release() code. The problem is that if I press Right, press Left, release Left my Sprite will be stopped since the on_key_release() sets the x velocity to zero. But, I'm still pressing Right so I want to continue moving that directions after pressing and releasing Left.
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])
Upvotes: 0
Views: 1256
Reputation:
From 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.
This means that if you press right, press left, release left, but don't release right, then new on_key_press events won't be dispatched before you release right and press it again.
If you keep the structure you have, I think you have to check if key.RIGHT is set in self.keys
, inside on_key_release
. But that alone wouldn't work. With the current code you would always get that keys[key.RIGHT]
is False
. I'll explain next why this is.
When you call director.window.push_handlers(self.keys)
, you register KeyStateHandler's own on_key_press
and on_key_release
handlers to the same queue you register MyLayer's corresponding handlers. What the KeyStateHandler's handlers do is that they set and unset keys in keys
dict so that you can for example query keys[key.SPACE]
to see if space key is being held down.
The problem with the code is that you register KeyStateHandler's handlers before MyLayer's handlers, which causes MyLayer's handlers to be called before those of KeyStateHandler. So when you press space and check in MyLayer.on_key_press
whether keys[key.SPACE]
is True
, it is not, since KeyStateHandler hasn't set keys[key.SPACE]
yet.
To call KeyStateHandler's handler functions before those of MyLayer, you shouldn't register (=push) them in MyLayer's constructor, but in MyLayer's on_enter
function like this:
def on_enter(self):
super(MyLayer, self).on_enter()
director.window.push_handlers(self.keys)
If you do this, you should probably also remove/deregister those handler's in MyLayer's on_exit
function.
If you make this change to your code snippet, it should print 'Key press!' and 'Space!' every time you press space, but not repeatedly since on_key_press event is still dispatched only once per key press.
You probably have scheduled some function with CocosNode.schedule, or defined some Action that uses self.player1.velocity
to move your Sprite (inheriting from the Move action would be a good way to do this). One way to get the effect you want is to not define your own on_key_press and and on_key_release handler functions, but to rely on KeyStateHandler. You push the KeyStateHandler's handler functions as in your code snippet, and in the beginning of your scheduled function or Action step you read from keys
dict, which keys are being pressed, and update the velocity based on that.
For example, to print 'Space!' repeatedly using schedule:
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()
Note that the way this works is that self.keys[key.SPACE]
is set to True
in KeyStateHandler's on_key_press
handler, which is called only once per key press. When you release the key, the dict entry is set to False
in KeyStateHandler's on_key_release
handler.
Upvotes: 2