comparison grease/impl/controls.py @ 5:bc88f7d5ca8b

Added base files for grease
author KarstenBock@gmx.net
date Tue, 12 Jul 2011 10:16:48 +0200
parents
children
comparison
equal deleted inserted replaced
4:bf1dd9c24a7e 5:bc88f7d5ca8b
1 #############################################################################
2 #
3 # Copyright (c) 2010 by Casey Duncan
4 # All Rights Reserved.
5 #
6 # This software is subject to the provisions of the MIT License
7 # A copy of the license should accompany this distribution.
8 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11 #
12 #############################################################################
13 """Control systems for binding controls to game logic"""
14
15 import grease
16 from pyglet.window import key
17
18 class KeyControls(grease.System):
19 """System that maps subclass-defined action methods to keys.
20
21 Keys may be mapped in the subclass definition using decorators
22 defined here as class methods or at runtime using the ``bind_key_*``
23 instance methods.
24
25 See :ref:`an example implementation in the tutorial <tut-controls-example>`.
26 """
27 MODIFIER_MASK = ~(key.MOD_NUMLOCK | key.MOD_SCROLLLOCK | key.MOD_CAPSLOCK)
28 """The MODIFIER_MASK allows you to filter out modifier keys that should be
29 ignored by the application. By default, capslock, numlock, and scrolllock
30 are ignored.
31 """
32
33 world = None
34 """:class:`grease.World` object this system is bound to"""
35
36 def __init__(self):
37 self._key_press_map = {}
38 self._key_release_map = {}
39 self._key_hold_map = {}
40 for name in self.__class__.__dict__:
41 member = getattr(self, name)
42 if hasattr(member, '_grease_hold_key_binding'):
43 for binding in member._grease_hold_key_binding:
44 self.bind_key_hold(member, *binding)
45 if hasattr(member, '_grease_press_key_binding'):
46 for binding in member._grease_press_key_binding:
47 self.bind_key_press(member, *binding)
48 if hasattr(member, '_grease_release_key_binding'):
49 for binding in member._grease_release_key_binding:
50 self.bind_key_release(member, *binding)
51 self.held_keys = set()
52
53 ## decorator methods for binding methods to key input events ##
54
55 @classmethod
56 def key_hold(cls, symbol, modifiers=0):
57 """Decorator to bind a method to be executed where a key is held down"""
58 def bind(f):
59 if not hasattr(f, '_grease_hold_key_binding'):
60 f._grease_hold_key_binding = []
61 f._grease_hold_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
62 return f
63 return bind
64
65 @classmethod
66 def key_press(cls, symbol, modifiers=0):
67 """Decorator to bind a method to be executed where a key is initially depressed"""
68 def bind(f):
69 if not hasattr(f, '_grease_press_key_binding'):
70 f._grease_press_key_binding = []
71 f._grease_press_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
72 return f
73 return bind
74
75 @classmethod
76 def key_release(cls, symbol, modifiers=0):
77 """Decorator to bind a method to be executed where a key is released"""
78 def bind(f):
79 if not hasattr(f, '_grease_release_key_binding'):
80 f._grease_release_key_binding = []
81 f._grease_release_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
82 return f
83 return bind
84
85 ## runtime binding methods ##
86
87 def bind_key_hold(self, method, key, modifiers=0):
88 """Bind a method to a key at runtime to be invoked when the key is
89 held down, this replaces any existing key hold binding for this key.
90 To unbind the key entirely, pass ``None`` for method.
91 """
92 if method is not None:
93 self._key_hold_map[key, modifiers & self.MODIFIER_MASK] = method
94 else:
95 try:
96 del self._key_hold_map[key, modifiers & self.MODIFIER_MASK]
97 except KeyError:
98 pass
99
100 def bind_key_press(self, method, key, modifiers=0):
101 """Bind a method to a key at runtime to be invoked when the key is initially
102 pressed, this replaces any existing key hold binding for this key. To unbind
103 the key entirely, pass ``None`` for method.
104 """
105 if method is not None:
106 self._key_press_map[key, modifiers & self.MODIFIER_MASK] = method
107 else:
108 try:
109 del self._key_press_map[key, modifiers & self.MODIFIER_MASK]
110 except KeyError:
111 pass
112
113 def bind_key_release(self, method, key, modifiers=0):
114 """Bind a method to a key at runtime to be invoked when the key is releaseed,
115 this replaces any existing key hold binding for this key. To unbind
116 the key entirely, pass ``None`` for method.
117 """
118 if method is not None:
119 self._key_release_map[key, modifiers & self.MODIFIER_MASK] = method
120 else:
121 try:
122 del self._key_release_map[key, modifiers & self.MODIFIER_MASK]
123 except KeyError:
124 pass
125
126 def step(self, dt):
127 """invoke held key functions"""
128 already_run = set()
129 for key in self.held_keys:
130 func = self._key_hold_map.get(key)
131 if func is not None and func not in already_run:
132 already_run.add(func)
133 func(dt)
134
135 def on_key_press(self, key, modifiers):
136 """Handle pyglet key press. Invoke key press methods and
137 activate key hold functions
138 """
139 key_mod = (key, modifiers & self.MODIFIER_MASK)
140 if key_mod in self._key_press_map:
141 self._key_press_map[key_mod]()
142 self.held_keys.add(key_mod)
143
144 def on_key_release(self, key, modifiers):
145 """Handle pyglet key release. Invoke key release methods and
146 deactivate key hold functions
147 """
148 key_mod = (key, modifiers & self.MODIFIER_MASK)
149 if key_mod in self._key_release_map:
150 self._key_release_map[key_mod]()
151 self.held_keys.discard(key_mod)
152
153
154 if __name__ == '__main__':
155 import pyglet
156
157 class TestKeyControls(KeyControls):
158
159 MODIFIER_MASK = ~(key.MOD_NUMLOCK | key.MOD_SCROLLLOCK | key.MOD_CTRL)
160
161 remapped = False
162
163 @KeyControls.key_hold(key.UP)
164 @KeyControls.key_hold(key.W)
165 def up(self, dt):
166 print 'UP!'
167
168 @KeyControls.key_hold(key.LEFT)
169 @KeyControls.key_hold(key.A)
170 def left(self, dt):
171 print 'LEFT!'
172
173 @KeyControls.key_hold(key.RIGHT)
174 @KeyControls.key_hold(key.D)
175 def right(self, dt):
176 print 'RIGHT!'
177
178 @KeyControls.key_hold(key.DOWN)
179 @KeyControls.key_hold(key.S)
180 def down(self, dt):
181 print 'DOWN!'
182
183 @KeyControls.key_press(key.SPACE)
184 def fire(self):
185 print 'FIRE!'
186
187 @KeyControls.key_press(key.R)
188 def remap_keys(self):
189 if not self.remapped:
190 self.bind_key_hold(None, key.W)
191 self.bind_key_hold(None, key.A)
192 self.bind_key_hold(None, key.S)
193 self.bind_key_hold(None, key.D)
194 self.bind_key_hold(self.up, key.I)
195 self.bind_key_hold(self.left, key.J)
196 self.bind_key_hold(self.right, key.L)
197 self.bind_key_hold(self.down, key.K)
198 else:
199 self.bind_key_hold(None, key.I)
200 self.bind_key_hold(None, key.J)
201 self.bind_key_hold(None, key.K)
202 self.bind_key_hold(None, key.L)
203 self.bind_key_hold(self.up, key.W)
204 self.bind_key_hold(self.left, key.A)
205 self.bind_key_hold(self.right, key.D)
206 self.bind_key_hold(self.down, key.S)
207 self.remapped = not self.remapped
208
209
210 window = pyglet.window.Window()
211 window.clear()
212 controls = TestKeyControls()
213 window.push_handlers(controls)
214 pyglet.clock.schedule_interval(controls.step, 0.5)
215 pyglet.app.run()
216