Handling input from an Xbox 360 controller

Having been sold together with millions of Xbox 360 game consoles, the Xbox 360 controller is one of the most widespread and well-known types of input devices for gamers. But it is not only console gamers who are able to use this kind of controller as it can easily be plugged into a Windows PC too. Apart from the device being recognized by the operating system, many PC games are officially supporting the Xbox 360 controller as a possible input device.

In case you want to create a game with support for this gamepad, this recipe is for you. But also if you intend to support any other type of joystick or game controller you will find interesting resources ahead because the API you are going to use is not bound to any device in particular. Nonetheless, this recipe will show you how to read data from the Xbox 360 controller's analog sticks and buttons, and will provide you with a minimal class that maps the raw button and axis indices to more meaningfully named variables.

Getting ready

This recipe builds upon the code and knowledge presented in the recipe Implementing an abstraction layer for supporting multiple input methods. Before going on, you are required to follow and understand that recipe!

Additionally, as Panda3D does not have built-in support for analog input devices like joysticks and gamepads, you need to add the pygame programming library to your installation of the Panda3D engine:

  1. Start your web browser and go to www.pygame.org/download.shtml.
  2. Scroll down the page until you find the following list of download links:
    Getting ready
  3. Download the file pygame-1.9.1.win32-py2.6.msi. The version number might not match. In that case watch out for the -py2.6 postfix in the filename.
  4. Launch the installer and click Next until you reach the following step of the install wizard:
    Getting ready
  5. Make sure the directory actually is the python subdirectory of your Panda3D installation. The screenshot shows the default installation path.
  6. Finish the installation and you are ready to go.

How to do it...

Let's write some code for handling gamepad input:

  1. Add a new source file called XboxControllerHandler.py and insert the following code:
    from panda3d.core import *
    import pygame
    import math
    class XboxControllerState:
    A = 0
    B = 1
    X = 2
    Y = 3
    LB = 4
    RB = 5
    BACK = 6
    START = 7
    LS = 8
    RS = 9
    def __init__(self, joy):
    self.joy = joy
    self.leftStick = Vec2()
    self.rightStick = Vec2()
    self.dpad = Vec2()
    self.triggers = 0.0
    self.buttons = [False] * self.joy.get_numbuttons()
    def update(self):
    self.leftStick.setX(self.joy.get_axis(0))
    self.leftStick.setY(self.joy.get_axis(1))
    self.rightStick.setX(self.joy.get_axis(4))
    self.rightStick.setY(self.joy.get_axis(3))
    self.triggers = self.joy.get_axis(2)
    for i in range(self.joy.get_numbuttons()):
    self.buttons[i] = self.joy.get_button(i)
    
  2. Add the XboxControllerHandler class below the code of XboxControllerState:
    class XboxControllerHandler(InputHandler):
    def __init__(self):
    InputHandler.__init__(self)
    self.wasWalking = False
    self.wasReversing = False
    self.controller = None
    pygame.init()
    pygame.joystick.init()
    for i in range(pygame.joystick.get_count()):
    joy = pygame.joystick.Joystick(i)
    name = joy.get_name()
    if "Xbox 360" in name or "XBOX 360" in name:
    joy.init()
    self.controller = joy
    self.state = XboxControllerState(joy)
    taskMgr.add(self.updateInput, "update input")
    def updateInput(self, task):
    pygame.event.pump()
    if self.controller:
    self.state.update()
    x = self.state.rightStick.getX()
    y = self.state.leftStick.getY()
    if y < -0.5 and not self.wasWalking:
    self.wasWalking = True
    self.beginWalk()
    elif not y < -0.5 and self.wasWalking:
    self.wasWalking = False
    self.endWalk()
    elif y > 0.5 and not self.wasReversing:
    self.wasReversing = True
    self.beginReverse()
    elif not y > 0.5 and self.wasReversing:
    self.wasReversing = False
    self.endReverse()
    if math.fabs(x) > 0.2:
    messenger.send("turn", [-x])
    self.dispatchMessages()
    return task.cont
    
  3. Open Application.py and add the highlighted line to the constructor of the Application class:
    self.keyInput = KeyboardMouseHandler()
    self.xboxInput = XboxControllerHandler()
    
  4. Start the application and control the panda using the left analog stick for moving forward and backward and the right stick for turning left and right.

How it works...

In the constructor of the XboxControllerHandler class we can see the pygame library and its joystick module being initialized before we iterate over all connected devices to see if we can find an Xbox 360 controller. If this routine is successful, a new instance of XboxControllerState is created.

This class is a container for storing the state of an Xbox 360 controller and provides easier access to the controller data than using numeric indices. The class' leftStick and rightStick variables store the state of the two analog sticks, while dpad and triggers store the states of the cross-shaped directional pad and the analog triggers on the back of the controller. Data about the various buttons on the controller being up or down can be accessed using the buttons list. To make accessing these buttons easier, the A, B, X, and other class variables, found right under the class declaration, can be used to address buttons by name rather than by a numeric index.

This leaves the updateInput() method of the XboxControllerHandler class open for discussion. Here, we keep the internal message loop of pygame running by calling pygame.event.pump(). Handling the input for walking forwards and backwards requires special care as we are degrading the left analog stick to a binary control scheme. We do not care how far the stick was pushed forward. Instead, we just set a flag based on whether the stick has been moved forward or backward.

Because we are not receiving any events for when an analog stick has become active, we need to take care of detecting this case ourselves. Therefore we need to store and check if we were not walking or reversing before any of the -start events are triggered. The same applies for the -stop events, where we need to determine if we were in a walking or reversing state, respectively.

The data read from the analog sticks on the Xbox 360 controller does not just simply go back to zero if they are centered. Instead, we receive a lot of noise from the controller. This is the reason why the turn event is starting to be triggered after the controller was moved more than 20% of the way towards one direction, or else the panda would be twitching uncontrollably and never stand still. We apply this low-cut filter in the code using the conditional expression if math.fabs(x) > 0.2.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset