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])
From pyglet guide:
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
, insideon_key_release
. But that alone wouldn't work. With the current code you would always get thatkeys[key.RIGHT]
isFalse
. I'll explain next why this is.When you call
director.window.push_handlers(self.keys)
, you register KeyStateHandler's ownon_key_press
andon_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 inkeys
dict so that you can for example querykeys[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
whetherkeys[key.SPACE]
isTrue
, it is not, since KeyStateHandler hasn't setkeys[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: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 fromkeys
dict, which keys are being pressed, and update the velocity based on that.For example, to print 'Space!' repeatedly using schedule:
Note that the way this works is that
self.keys[key.SPACE]
is set toTrue
in KeyStateHandler'son_key_press
handler, which is called only once per key press. When you release the key, the dict entry is set toFalse
in KeyStateHandler'son_key_release
handler.