Home | Trees | Indices | Help |
|
---|
|
1 # -*- coding: utf-8 -*- 2 """ 3 Input helper to ease the input setup 4 5 @author: fabius 6 @copyright: fabius@2010 7 @license: GCTA give credit to (the) authors 8 @version: 0.1 9 @date: 2010-06 10 @contact: astelix (U{panda3d forums<http://www.panda3d.org/forums/profile.php?mode=viewprofile&u=752>}) 11 @status: eternal WIP 12 13 @todo: 14 - introdurre bind/unbind dinamico dei controlli o di singoli input 15 """ 16 __all__ = ['easyinput'] 17 18 from direct.showbase.DirectObject import DirectObject 19 from direct.gui.OnscreenText import OnscreenText 20 from direct.task.Task import Task 21 import sys 22 from pandac.PandaModules import WindowProperties 23 24 #================================================================ 25 # 26 try: 27 import pygame as PYG 28 PYG.init() 29 except ImportError: 30 print "[WARNING] Pygame module not present" 31 PYG=None 3234 """ 35 A class to ease the input bind of actions. 36 Setup: 37 - define a config string similar to what we find in a quake config file, where a bind command assign a keyword to an input device event to act as a bridge between the device input and the game action handler 38 - define a dictionary with the config keywords as keys and the handlers as values 39 """ 40 _DEBUG=False 41 #------------------------------------------------------ 42 # 43 # predefined bind names/named-handlers; you may change'em passing an overrider config text overrider 44 DEFAULT_CONFIG=""" 45 //JOY ------ 46 bind joy0-button1 "action" 47 //+- y axis movement : by default will fire forward/back events 48 bind joy0-axis1 "thrust" 49 bind joy0-button2 "jump" 50 //+- x axis movement : by default will fire moveleft/moveright events 51 bind joy0-axis0 "heading" 52 bind joy0-button11 "quit" 53 bind joy0-button6 "run" 54 //KB ------ 55 bind enter "action" 56 bind w "forward" 57 bind s "back" 58 bind a "left" 59 bind d "right" 60 bind arrow_down "down" 61 bind arrow_up "up" 62 bind arrow_left "left" 63 bind arrow_right "right" 64 bind rcontrol "jump" 65 bind page_up "headup" 66 bind page_down "headdown" 67 bind escape "quit" 68 bind rshift "run" 69 bind \ "alwaysrun" 70 // MOUSE ------ 71 //to unbind some event specify an empty string as handler 72 bind mouse1 "mouse1" 73 bind mouse2 "mouse2" 74 bind mouse3 "mouse3" 75 bind mouse-x "heading" 76 //+- z axis movement : by default will fire headup/headdown events 77 bind mouse-y "pitch" 78 bind wheel_up "zoomin" 79 bind wheel_down "zoomout" 80 """381 382 #================================================================ 383 # 384 if __name__ == '__main__': 385 import direct.directbase.DirectStart 386 from direct.gui.DirectGui import * 387 from direct.gui.OnscreenText import OnscreenText 388 389 #============================================================ 390 # 391 text2d=lambda line=0, text='': OnscreenText( 392 text = text, pos = (0, line*.1), scale = 0.05, mayChange=True, fg=(1,1,0,1), bg=(0,0,0,.5) 393 )82 """ 83 @param config: 84 E{\}n sepatated string of 3 element lines: 85 [0]=bind [1]=event name [2]=bridge keyword. 86 @param bridge: dictionary of event keywordE{:}handler elements. 87 """ 88 DirectObject.__init__(self) 89 # 90 self._DEBUG=debug 91 # 92 self.JOY=0 93 94 # self.config={'<command>':{'<event>': <handler>}} 95 self.config={} 96 self.add_config(self.DEFAULT_CONFIG) 97 self.add_config(config) 98 99 # bridge with predefined events connected to handlers to be overrided 100 _bridge={ 101 'action': self.action, 102 'back': self.back, 103 'forward': self.forward, 104 'jump': self.jump, 105 'left': self.left, 106 'right': self.right, 107 'thrust': self.thrust, 108 'pitch': self.pitch, 109 'heading': self.heading, 110 'up': self.headup, 111 'down': self.headdown, 112 'quit': self.quitme, 113 'run': self.run, 114 'alwaysrun': self.alwaysrunToggle, 115 'zoomin': lambda foo: self.zoom(1), 116 'zoomout': lambda foo: self.zoom(-1), 117 } 118 # melt default bridge with user defined 119 for k,v in bridge.iteritems(): _bridge[k]=v 120 121 # returns the number of joysticks found 122 self.JOY=self.pyga_joysetup() 123 124 # bind operations happens here 125 self.readmouse_binds={} 126 readmouse={} 127 for evt, hndrepeat in self.config['bind'].iteritems(): 128 hnd,repeat=hndrepeat 129 if hnd in _bridge: 130 hname=_bridge[hnd] 131 #bind joystick 132 if evt.startswith('joy'): 133 if self.JOY: 134 ###print ">>>JOaccept:%s->%s" % (evt, hname) 135 self.accept(evt, _bridge[hnd]) 136 #bind mouse 137 elif evt.startswith('mouse'): 138 # if there is a bind for mouse movement will be created relative listeners to hear mouse-x or mouse-y events fired by a mouse polling task eventually created below 139 if evt in ["mouse-x", "mouse-y"]: 140 readmouse[evt]=_bridge[hnd] 141 else: 142 #here should bind button listeners, but not necessarily 143 ###print ">>>MOaccept:%s->%s" % (evt, hname) 144 self.accept(evt, _bridge[hnd], [1]) 145 self.accept(evt+"-up", _bridge[hnd], [0]) 146 # qui dovrebbe arrivare solo kbkey 147 else: 148 ###print ">>>KBaccept:%s->%s" % (evt, hname) 149 repeat='-repeat' if repeat else '' 150 self.accept(evt+'-up', _bridge[hnd], [0]) 151 self.accept(evt+repeat, _bridge[hnd], [1]) 152 153 #** spawn the mouse movement tracking task (see above the mouse bind part) 154 self.mouseTask=None 155 if readmouse: self.set_mouse_read(readmouse, True) 156 #taskMgr.popupControls() 157 ###print messenger 158 self.pyga_joytask=None159 160 #------------------------------------------------------ 161 #163 """ Invoke this before this class object will be off duty """ 164 self.ignoreAll() 165 if self.mouseTask: 166 taskMgr.remove(self.mouseTask) 167 self.mouseTask=None 168 if self.pyga_joytask: 169 taskMgr.remove(self.pyga_joytask) 170 self.pyga_joytask=None 171 props = WindowProperties() 172 props.setCursorHidden(False) 173 props.setMouseMode(WindowProperties.MAbsolute) 174 base.win.requestProperties(props)175 176 #------------------------------------------------------ 177 #179 if self.mouseTask: 180 taskMgr.remove(self.mouseTask) 181 self.mouseTask=None 182 183 if activate: 184 if not self.readmouse_binds: 185 for xy in ['mouse-x', 'mouse-y']: 186 self.readmouse_binds[xy]=readmouse.get(xy, self._foo) 187 taskName = "_esnptmo-%s" % id(self) 188 self.mouseTask = taskMgr.add(self._read_mouse, taskName, sort=40) 189 190 props = WindowProperties() 191 props.setCursorHidden(activate) 192 props.setMouseMode( 193 [WindowProperties.MAbsolute, WindowProperties.MRelative][activate]) 194 base.win.requestProperties(props)195 196 #------------------------------------------------------ 197 # 198 # to avoid to flood with useless still mouse events 199 _pre_mox=0 200 _pre_moy=0 201 mouse_speed_factor=5.203 """Read the mouse position and then route mouse position to the relative binded handler 204 """ 205 if base.mouseWatcherNode.hasMouse(): 206 x = base.win.getPointer(0).getX() 207 y = base.win.getPointer(0).getY() 208 deltax=(x-self._pre_mox) 209 deltay=(y-self._pre_moy) 210 if deltax or deltay: 211 self.readmouse_binds["mouse-x"](deltax/self.mouse_speed_factor) 212 self.readmouse_binds["mouse-y"](deltay/self.mouse_speed_factor) 213 self._predelta=True 214 elif self._predelta: 215 self._predelta=False 216 self.readmouse_binds["mouse-x"](0) 217 self.readmouse_binds["mouse-y"](0) 218 self._pre_mox,self._pre_moy=(x, y) 219 else: 220 self.readmouse_binds["mouse-x"](0) 221 self.readmouse_binds["mouse-y"](0) 222 223 return Task.cont224 225 #------------------------------------------------------ 226 # 228 #------------------------------------------------------ 229 #231 """Merge a text file config in the main config data collector where the latter override the former 232 """ 233 clean=lambda n: n.strip().strip('"').lower() 234 for line in config.split('\n'): 235 items=line.strip().split() 236 if items and len(items) >= 3: 237 cmd, evt, hnd=items[:3] 238 """ NOTE 239 - just 'bind' command expected right now 240 - '+' prepended ti the handler means REPEAT (make sense just for keyboard keys actually) 241 """ 242 cmd=clean(cmd) 243 if cmd in ['bind']: 244 evt,hnd=(clean(evt), clean(hnd)) 245 if not cmd in self.config: self.config[cmd]={} 246 repeat=hnd.startswith('+') 247 if repeat: hnd=hnd[1:] 248 self.config[cmd].update([[evt, [hnd, repeat]]])249 250 #------------------------------------------------------ 251 #253 """Pygame joystick(s) startup - returns the number of joysticks found 254 """ 255 jcount=0 256 if PYG: 257 self.dbgprint("pygame starts") 258 jcount=PYG.joystick.get_count() 259 if jcount > 0: 260 for x in range(jcount): 261 j = PYG.joystick.Joystick(x) 262 j.init() 263 self.dbgprint(">>>Enabled joystick: %s" % j.get_name()) 264 taskMgr.add(self.pyga_joytask, 'tsk_pygajoy') 265 else: 266 self.dbgprint("No Joysticks to Initialize!") 267 268 return jcount269 #------------------------------------------------------ 270 #272 """If there is a joy event it will be sent a proper string message and event value to the messenger queue 273 """ 274 for e in PYG.event.get(): 275 # joystick buttons up and down events routing 276 # will send a string like, i.e.: "joy0-button1" for the button 1 of the joy 0 and 0 or 1 as a parameter for button up or down status respectively 277 if e.type in [PYG.JOYBUTTONDOWN, PYG.JOYBUTTONUP]: 278 s="joy%d-button%d" % (e.joy, e.button) 279 messenger.send(s, [1 if e.type == PYG.JOYBUTTONDOWN else 0]) 280 # joistick axis (analog and digital) 281 # will send a string like, i.e.: "joy0-axis1" for the axis 1 of the joy 0 and a number between 0 and 1 or 0 and -1 as the stick or hat status (the digital stick returns 0 OR +-1 but analog sticks floating values from 0.0 and +-1.0) 282 elif e.type == PYG.JOYAXISMOTION: 283 s="joy%d-axis%d" % (e.joy, e.axis) 284 ###print "Jax-%r(%r)" % (e.axis, e.value) 285 if e.axis in [1,2]: 286 messenger.send(s, [-e.value]) 287 else: 288 messenger.send(s, [e.value]) 289 return Task.cont290 #----------------------------------------------------- 291 #293 if self._DEBUG: print msg294 #------------------------------------------------------ 295 # PREDEFINED INPUT EVENT HANDLERS (to override) 296 """ 297 NOTE some events are grouped in one such us thrust collect forward and back events so that you may subclass just thrust and ease your game logic 298 """ 299 #------------------------------------------------------ 300 # 304 #------------------------------------------------------ 305 # 309 #------------------------------------------------------ 310 # 314 #------------------------------------------------------ 315 # 319 #------------------------------------------------------ 320 # 324 #------------------------------------------------------ 325 #327 """evt=-1. to 1. - for x movements 328 to use for steering 329 """ 330 self.dbgprint("heading(%r)"%evt)331 #------------------------------------------------------ 332 # 336 #------------------------------------------------------ 337 # 341 #------------------------------------------------------ 342 # 346 #------------------------------------------------------ 347 # 351 #------------------------------------------------------ 352 #354 """evt=-1. to 1. - for z movements 355 by default the evt collect headup and headdown events 356 override or bind this in your subclass 357 """ 358 self.dbgprint("pitch(%r)"%evt)359 #------------------------------------------------------ 360 # 364 #------------------------------------------------------ 365 # 369 #------------------------------------------------------ 370 #372 """evt=-1. to 1. - for y movements 373 by default the evt collect forward and back events 374 override or bind this in your subclass 375 """ 376 self.dbgprint("thrust(%r)"%evt)377 #------------------------------------------------------ 378 #380 self.dbgprint("z00ming(%r)"%evt)395 """Sample use of easyinput class 396 in brief you define a text very similar to quake .cfg files strings where to issue a bind command followed by a input event name and a keyword that define a bridge between the input event and a handler that will be called when that event will be fired 397 """ 398 #------------------------------------------------------ 399 #447 448 #============================================================ 449 # 460 # 461 # the test menu 462 # 474 475 DO=DirectObject() 476 DO.accept('1', setchoice, ['1']) 477 DO.accept('2', setchoice, ['2']) 478 DO.accept('escape', sys.exit) 479 480 text = """ 481 Sample choice 482 ============= 483 1) Instanced easyInput class 484 2) Derived class from easyInput 485 ESC) exit 486 """ 487 menu = OnscreenText(parent=base.a2dTopLeft, 488 text = text, pos = (.5, -.1), scale = 0.05, mayChange=False, 489 fg=(1,1,0,1), bg=(0,0,0,.5) 490 ) 491 492 run() 493401 DirectObject.__init__(self) 402 # 403 #self.accept('escape-up', self.quitme) 404 # crea ost di debug 405 self.display={} 406 l=0 407 for x in ['joy', 'mouse', 'kb', 'unspecified']: 408 l+=1 409 self.display[x]=text2d(line=l, text=x.upper()+":") 410 ### 411 # bind eventi device di input 412 #file.cfg tipo quake3 413 cfg="""//JOY ------ 414 bind joy0-button9 "quit" // a comment 415 bind joy0-button1 "action" 416 bind joy0-axis1 "axis1" 417 //KB ------ 418 bind arrow_up "forward" 419 bind escape "quit" 420 bind enter "action" 421 // MOUSE ------ 422 bind mouse1 "action" 423 """ 424 #these are the allowed handlers we need 425 hndbridge={ 426 'axis1': self.axis1test, 427 'action': self.allaction, 428 'quit': self.quitme, 429 } 430 # 431 self.xinput=easyinput(cfg, hndbridge)432 #------------------------------------------------------ 433 #435 """qui ci si aspetta un evt click (evt=true)""" 436 if evt: 437 self.dbgprint("too much for testing: so-long") 438 sys.exit()439 #------------------------------------------------------ 440 # 443 #------------------------------------------------------ 444 #
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Tue Sep 21 14:49:51 2010 | http://epydoc.sourceforge.net |