Input.py
This is a wrapper class to simplify control handling in pygame when using multiple controller methods, as developed for Sheep Snaggers 2 and originally described here.
Rather than your game logic having to deal with all the controllers directly, it allows you to define named controls which can map to multiple controllers at once.
Simple example:
input = Input() while running: input.Poll() ship.x += input.GetControl('THRUST') ship.y += input.GetControl('CLIMB') if input.GetControl('FIRE') ship.fire() if input.GetControl('QUIT'): running = False render()
Full source code:
#Beercave Gamews Beerware license v1.0 #This code is free to use for anything you want. #If you find it useful, I'd appreciate it if you buy me a beer :) #See http://www.beercave.co.uk/content/beerware for details import pygame from pygame.locals import * #wrapper class for all input methods #maps controllers to named controls, eg 'FIRE', 'JUMP' etc #keeps all that nasty control handling code out of the game classes #call GetControl(CONTROL_NAME) to check the control state in game #clamp value to a min/max range def clamp(sourceval, minval, maxval): return max(min(sourceval, maxval), minval) class Input(): def __init__(self, **kwargs): #mappings for keydowns, key states and axis positions #format is controlname : [list of [source, (arguments...)]] #multiple inputs can all map to the same control #key/button press events (1,0) #identifies if a key/button was pressed this frame #['keyevent'], (KEY_ID,)] where KEY_ID is the pygame key id #['buttonevent', (STICK_ID, BUTTON_ID)] #['mouseevent', (BUTTON_ID)] #stickevent tracks whether the joystick was moved in a given #direction this frame. Threshold parameter allows for #filtering out of small movements #['stickevent', (STICK_ID,AXIS_ID,DIRECTION,THRESHOLD)] #key/button states (1,0) #identifies if a key/button is currently pressed #['keystate'], (KEY_ID,)] #['buttonstate', (STICK_ID, BUTTON_ID)] #axis mappings #track a joystick axis #can also map keyboard controls to a virtual axis so #the game code doesn't need to know whether it's #dealing with keys or a controller #['keyaxis'], (+KEY_ID, -KEY_ID)] #+KEY_ID is mapped to the positive axis direction #-KEY_ID is mapped to the negative axis direction #['stickaxis', (STICK_ID, AXIS_ID,)] #use '-stickaxis' to invert axis values self.controlMap = { 'QUIT' : [['keyevent', (K_ESCAPE,)]], 'PAUSE' : [['keyevent', (K_p,)]], 'START' : [ ['keyevent', (K_SPACE,)], ['keyevent', (K_RETURN,)], ['buttonevent', (0,0)] ], 'TEST' : [['keyevent', (K_SPACE,)]], 'OPTIONS' : [['keyevent', (K_o,)]], 'SCREENSHOT' : [['keyevent', (K_RCTRL,)]], 'THRUST' : [ ['keyaxis', (K_RIGHT, K_LEFT)], ['stickaxis', (0,0)] ], 'FIRE' : [['keyevent', (K_LCTRL,)], ['keyevent', (K_z,)], ['mouseevent', (1,)], ['mouseevent', (2,)], ['mouseevent', (3,)], ['buttonevent', (0,0)] ], 'FIRE2' : [ ['keyevent', (K_LSHIFT,)], ['keyevent', (K_c,)], ['buttonevent', (0,1)] ], 'TURN' : [ ['keyevent', (K_x,)], ['buttonevent', (0,2)] ], 'CLIMB' : [ ['keyaxis', (K_UP, K_DOWN)], ['-stickaxis', (0,1,)] ], 'STICK1' : [ ['-stickaxis', (0,0,)] ], 'STICK2' : [ ['-stickaxis', (0,1,)] ], 'UP' : [ ['keyevent', (K_UP, )], ['stickevent', (0,1,-1,.5)] ], 'DOWN' : [ ['keyevent', (K_DOWN, )], ['stickevent', (0,1,1,.5)] ], 'RIGHT' : [ ['keyevent', (K_RIGHT, )], ['stickevent', (0,0,1,.5)] ], 'LEFT' : [ ['keyevent', (K_LEFT, )], ['stickevent', (0,0,-1,.5)] ], 'FLINGX' : [['mousedir', (0,)]], 'FLINGY' : [['mousedir', (1,)]] } self.Mouse = [0,0] #state for all keys self.keys = pygame.key.get_pressed() #did event happen this frame for all keys self.keyevents = [0] *323 #all key events this frame self.keyspressed = [] #all mouse events this frame self.mouseevents = [0] * 4 #button press events this frame self.buttonevents = [[0]*20] #state of all joysticks in the system self.stickStates = [] self.InitSticks() self.gesturetime = 0 self.gesturex = 0 self.gesturey = 0 #update the input states #your game engine should call this every frame #for mouse control to work pass time elapsed this frame as dt def Poll(self, dt=0): keyevents = pygame.event.get(pygame.KEYDOWN) mouseevents = pygame.event.get(pygame.MOUSEBUTTONDOWN) stickevents = pygame.event.get(pygame.JOYAXISMOTION) buttonevents = pygame.event.get(pygame.JOYBUTTONDOWN) #clear the events list from the previous frame self.keyevents = [0] *323 self.mouseevents = [0] * 4 sticks = self.stickCount for i in range(0,sticks): state = self.stickStates[i] axes = state['numaxes'] buttons = state['numbuttons'] state['axisevents'] = [0] * axes state['buttonevents'] = [0] * buttons state['axes'] = [0.] * axes #move all pressed keys to an array - will save #iterating over the list every time we check #for a keypress for event in keyevents: self.keyevents[event.key] = True #all keyevents this frame #exposing this is useful for situations where we want to know #all the keys that were pressed - ie for handling text input self.keyspressed = keyevents self.keys = pygame.key.get_pressed() for event in mouseevents: self.mouseevents[event.button] = True for event in stickevents: self.stickStates[event.joy]['axisevents'][event.axis] = event.value for event in buttonevents: self.stickStates[event.joy]['buttonevents'][event.button] = True self.CheckMouse(dt) self.CheckSticks() pygame.event.clear() #check the system for attached joysticks and initialise def InitSticks(self): sticks = pygame.joystick.get_count() self.stickCount = sticks self.stickStates = [] for i in range(0,sticks): state = {} state['stick'] = stick =pygame.joystick.Joystick(i) stick.init() state['name'] = stick.get_name() state['numbuttons'] = buttons = stick.get_numbuttons() state['numaxes'] = axes = stick.get_numaxes() state['buttonstates'] = [0] * buttons state['axes'] = [0.] * axes state['axisevents'] = [0] * axes state['buttonevents'] = [0] * buttons self.stickStates.append(state) #poll all joysticks for state def CheckSticks(self): sticks = self.stickCount for i in range(0,sticks): state = self.stickStates[i] stick = state['stick'] buttons = state['numbuttons'] axes = state['numaxes'] for k in range(0, axes): state['axes'][k] = stick.get_axis(k) #poll mouse for state def CheckMouse(self,dt): #basic mouse gestures relx,rely = pygame.mouse.get_rel() #clear the axes so we don't report a movement every frame self.Mouse = [0,0] #check we've moved a decent amount if relx*relx+rely*rely > 10: self.gesturetime += dt self.gesturex += relx self.gesturey += rely elif self.gesturetime > 0: #a gesture just ended #change the velocity self.Mouse = [self.gesturex, -self.gesturey] self.gesturetime = 0 self.gesturex = 0 self.gesturey = 0 return #call this to get the state of a defined control def GetControl(self, name): mappings = self.controlMap.get(name, None) result = 0. for map in mappings: source, args = map if source == 'keyevent': key, = args result += self.GetKeyEvent(key) if source == 'buttonevent': stick,button = args result += self.GetButtonEvent(stick, button) if source == 'mouseevent': button, = args result += self.GetMouseEvent(button) if (source == 'keystate'): key, = args result += self.GetKeyState(key) if (source == 'keyaxis'): key1,key2 = args result += self.GetKeyAxis(key1, key2) if (source == 'stickaxis'): stick, axis = args val = self.GetStickAxis(stick, axis) result += val if (source == '-stickaxis'): stick, axis = args val = -self.GetStickAxis(stick, axis) result += val if (source == 'stickevent'): stick, axis, direction, threshold = args result += self.GetStickEvent(stick, axis, direction, threshold) if (source == 'mousedir'): axis, = args #mouse axis overrules alternatives if in use return self.GetMouseAxis(axis) return clamp(result, -1, 1) #functions to handle different control mappings def GetKeyEvent(self, key): return self.keyevents[key] def GetButtonEvent(self, stick, button): if self.stickCount <= stick: return False return self.stickStates[stick]['buttonevents'][button] def GetStickEvent(self, stick, axis, direction, threshold): if self.stickCount <= stick: return False value = self.stickStates[stick]['axisevents'][axis] return value * direction > threshold def GetMouseEvent(self, button): return self.mouseevents[button] def GetKeyState(self, key): return self.keys[key] def GetKeyAxis(self, key1, key2): keys = self.keys val1 = 1. if self.keys[key1] else 0. val2 = -1. if self.keys[key2] else 0. return val1 + val2 def GetStickAxis(self, stick, axis): if self.stickCount <= stick: return 0. state = self.stickStates[stick] pos = state['axes'][axis] return pos def GetMouseAxis(self, axis): return self.Mouse[axis]/1000.
This code is released under the Beerware license. It's completely free to use, but if you like it, why not buy me a beer? :)
*All donations are covered by the Beercave Games Beerware Pledge.
Other Stuff
Input.py
Input wrapper class for handling multiple controllers in pygame.
SpritePacker.py
Quick and dirty spritesheet generator
Beerware
The Beercave Games Beerware license. Like my games? Buy me a pint!
