Grease logo

Source code for grease.controls

#############################################################################
#
# Copyright (c) 2010 by Casey Duncan
# All Rights Reserved.
#
# This software is subject to the provisions of the MIT License
# A copy of the license should accompany this distribution.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#
#############################################################################
"""Control systems for binding controls to game logic"""

import grease
from pyglet.window import key

[docs]class KeyControls(grease.System): """System that maps subclass-defined action methods to keys. Keys may be mapped in the subclass definition using decorators defined here as class methods or at runtime using the ``bind_key_*`` instance methods. See :ref:`an example implementation in the tutorial <tut-controls-example>`. """ MODIFIER_MASK = ~(key.MOD_NUMLOCK | key.MOD_SCROLLLOCK | key.MOD_CAPSLOCK) """The MODIFIER_MASK allows you to filter out modifier keys that should be ignored by the application. By default, numlock and scrolllock are ignored. """ world = None """:class:`grease.World` object this system is bound to""" def __init__(self): self._key_press_map = {} self._key_release_map = {} self._key_hold_map = {} for name in self.__class__.__dict__: member = getattr(self, name) if hasattr(member, '_grease_hold_key_binding'): for binding in member._grease_hold_key_binding: self.bind_key_hold(member, *binding) if hasattr(member, '_grease_press_key_binding'): for binding in member._grease_press_key_binding: self.bind_key_press(member, *binding) if hasattr(member, '_grease_release_key_binding'): for binding in member._grease_release_key_binding: self.bind_key_release(member, *binding) self.held_keys = set() ## decorator methods for binding methods to key input events ## @classmethod
[docs] def key_hold(cls, symbol, modifiers=0): """Decorator to bind a method to be executed where a key is held down""" def bind(f): if not hasattr(f, '_grease_hold_key_binding'): f._grease_hold_key_binding = [] f._grease_hold_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK)) return f return bind
@classmethod
[docs] def key_press(cls, symbol, modifiers=0): """Decorator to bind a method to be executed where a key is initially depressed""" def bind(f): if not hasattr(f, '_grease_press_key_binding'): f._grease_press_key_binding = [] f._grease_press_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK)) return f return bind
@classmethod
[docs] def key_release(cls, symbol, modifiers=0): """Decorator to bind a method to be executed where a key is released""" def bind(f): if not hasattr(f, '_grease_release_key_binding'): f._grease_release_key_binding = [] f._grease_release_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK)) return f return bind ## runtime binding methods ##
[docs] def bind_key_hold(self, method, key, modifiers=0): """Bind a method to a key at runtime to be invoked when the key is held down, this replaces any existing key hold binding for this key. To unbind the key entirely, pass ``None`` for method. """ if method is not None: self._key_hold_map[key, modifiers & self.MODIFIER_MASK] = method else: try: del self._key_hold_map[key, modifiers & self.MODIFIER_MASK] except KeyError: pass
[docs] def bind_key_press(self, method, key, modifiers=0): """Bind a method to a key at runtime to be invoked when the key is initially pressed, this replaces any existing key hold binding for this key. To unbind the key entirely, pass ``None`` for method. """ if method is not None: self._key_press_map[key, modifiers & self.MODIFIER_MASK] = method else: try: del self._key_press_map[key, modifiers & self.MODIFIER_MASK] except KeyError: pass
[docs] def bind_key_release(self, method, key, modifiers=0): """Bind a method to a key at runtime to be invoked when the key is releaseed, this replaces any existing key hold binding for this key. To unbind the key entirely, pass ``None`` for method. """ if method is not None: self._key_release_map[key, modifiers & self.MODIFIER_MASK] = method else: try: del self._key_release_map[key, modifiers & self.MODIFIER_MASK] except KeyError: pass
[docs] def step(self, dt): """invoke held key functions""" already_run = set() for key in self.held_keys: func = self._key_hold_map.get(key) if func is not None and func not in already_run: already_run.add(func) func(dt)
[docs] def on_key_press(self, key, modifiers): """Handle pyglet key press. Invoke key press methods and activate key hold functions """ key_mod = (key, modifiers & self.MODIFIER_MASK) if key_mod in self._key_press_map: self._key_press_map[key_mod]() self.held_keys.add(key_mod)
[docs] def on_key_release(self, key, modifiers): """Handle pyglet key release. Invoke key release methods and deactivate key hold functions """ key_mod = (key, modifiers & self.MODIFIER_MASK) if key_mod in self._key_release_map: self._key_release_map[key_mod]() self.held_keys.discard(key_mod)
if __name__ == '__main__': import pyglet class TestKeyControls(KeyControls): MODIFIER_MASK = ~(key.MOD_NUMLOCK | key.MOD_SCROLLLOCK | key.MOD_CTRL) remapped = False @KeyControls.key_hold(key.UP) @KeyControls.key_hold(key.W) def up(self, dt): print 'UP!' @KeyControls.key_hold(key.LEFT) @KeyControls.key_hold(key.A) def left(self, dt): print 'LEFT!' @KeyControls.key_hold(key.RIGHT) @KeyControls.key_hold(key.D) def right(self, dt): print 'RIGHT!' @KeyControls.key_hold(key.DOWN) @KeyControls.key_hold(key.S) def down(self, dt): print 'DOWN!' @KeyControls.key_press(key.SPACE) def fire(self): print 'FIRE!' @KeyControls.key_press(key.R) def remap_keys(self): if not self.remapped: self.bind_key_hold(None, key.W) self.bind_key_hold(None, key.A) self.bind_key_hold(None, key.S) self.bind_key_hold(None, key.D) self.bind_key_hold(self.up, key.I) self.bind_key_hold(self.left, key.J) self.bind_key_hold(self.right, key.L) self.bind_key_hold(self.down, key.K) else: self.bind_key_hold(None, key.I) self.bind_key_hold(None, key.J) self.bind_key_hold(None, key.K) self.bind_key_hold(None, key.L) self.bind_key_hold(self.up, key.W) self.bind_key_hold(self.left, key.A) self.bind_key_hold(self.right, key.D) self.bind_key_hold(self.down, key.S) self.remapped = not self.remapped window = pyglet.window.Window() window.clear() controls = TestKeyControls() window.push_handlers(controls) pyglet.clock.schedule_interval(controls.step, 0.5) pyglet.app.run()