The McCree controller is so close to being done! There’s one last change to make: swapping out the DDR dance pad for a faster controller. That ‘faster controller’ is going to be a Wii Nunchuk, the one-handed extension controller for Nintendo’s 7th generation console.
This post is part of a series on creating a custom controller for Overwatch using a Nerf revolver. Check out the project page here.
What is this, Footloose?
So why switch from the dance pad to the Nunchuk? Basically, while the dance pad does work it’s sloooooow – switching between directions can take up to a half second. This is abysmal for a fast-paced game like Overwatch, where being able to sidestep a projectile is often the difference between life and death.
Not only is the dance pad slow, but combinations of inputs are cumbersome. For single inputs I can tap my foot on a pad, switching my weight between sides for left/right inputs. For multiple inputs (e.g. ‘move forward’ and ‘jump’) I need to physically step to two pads on the board and then step back. This makes it difficult to do something like jump onto a moving platform.
‘Proper’ DDR setups in arcades and the like have a support bar behind the player which allows them to take their weight off of their feet and ‘tap’ pads to activate them. But I need to have both of my hands free to manipulate the Nerf gun.
Lastly, both the Nerf gun and my headphones are wired which creates a tripping hazard. I need to always be conscious of where the wires are to not trip over them, which takes my attention away from the game.
In short – the dancepad is convenient, but it makes for a pretty mediocre movement controller for my McCree Nerf gun.
Enter the Nunchuk
Say hello to the Wii Nunchuk! This is what I’m going to replace my dance pad with. It’s a small one-handed controller with an XY joystick, two buttons, and a three-axis accelerometer.
The Nunchuk is actually Nintendo’s answer for how to do movement controls if the player has a one-handed aiming controller. It was their accessory controller for the Wii remote, which the player aimed using an infrared sensor. This makes it perfect for my purposes.
This is a genuine Nunchuk I picked up from a local used gaming store for $12. I’m going to connect it to an Arduino to handle McCree’s movement.
‘Mounting’ the Nunchuk
I explicitly designed the McCree controller for two-handed control, using a dance pad for movement. Although I knew a Nunchuk would probably be the controller of choice from the beginning, I also knew that having another controller in the player’s off-hand would ruin some of the animations – notably reloading and fan-the-hammer. This is why I initially went with a dance pad.
Rather than re-designing the Nerf gun, I’m going to make it possible to ‘drop’ the Nunchuk if the player needs to use a two-handed control. This way the player can still use the the Nunchuk and the two-handed controls on the gun.
For the time being, this ‘mounting’ solution is going to be very Macgyver-esque. I attached a rubber band to the rubber strain-relief molding near the base of the Nunchuk, and tied the other end to a binder clip. This is a low-tech solution that allows the Nunchuk to hang from my belt. I can let go, manipulate the Nerf gun, and then grab it again very quickly. The only downside is that I have to hold the Nunchuk near my waist the whole time. I may end up swapping this out for something like a key reel in the future.
Control Setup
So how am I going to set up the movement controls with my shiny new Nunchuk?
- Joystick XY: Movement (WASD)
- C Button: Combat Roll (LSHIFT)
- Z Button: Jump (Space)
- C+Z Buttons: Communication wheel (C)
- AccelY-: Scoreboard (Tab)
Obviously, the Nunchuk’s joystick is going to be mapped to forward/back and strafing movement (in place of the dance pad’s arrow keys). The Nunchuk’s two trigger buttons are going to be mapped to “jump” and “roll”, which are McCree’s two main movement abilities. Crouch will have to be saved for another controller.
Since jump has no cooldown, I can also do something if I press both trigger buttons at the same time so long as I make sure to press ‘Z’ first and release it last. For the time being I’m going to map this combination to the communication wheel, because sometimes it’s just nice to say hello and thank you. Although I may swap it out later.
Because I need to be able to drop the controller on a whim I can’t do much with the accelerometer. The only accelerometer-triggered ability I’m adding is if the Y axis shows the Nunchuk pointing up due to gravity, which is something that should never happen unless I’m holding it there intentionally (i.e. I can’t point the controller up easily if it’s attached to my belt). This control is going to be mapped to the scoreboard, just in-case I want to check the team compositions or how I’m doing.
The Nunchuk has far fewer buttons than the dance pad, so I’m going to have to lose some of the controls like my voice line shortcut or the menu key (I won’t be able to leave a game manually, the horror!). I’ll still have a keyboard within reach, so this isn’t the end of the world.
Hardware Setup
The Arduino I’m using to emulate keypresses is once again a Pro Micro. The Pro Micro works on 5V from USB and doesn’t have a voltage regulator, so I’m using an Arduino Nano to step the voltage down to +3.3V for the Nunchuk. A standalone regulator would be better, but I don’t have any on-hand.
To step down the logic levels on the I2C lines I’m using a TCA9548A multiplexer in a breakout from Adafruit. I bought this to be able to use 2 Nunchuks simultaneously, but it also conveniently works as a level converter to step the signals down.
To connect to the Nunchuk itself I’m using a “Nunchuky” breakout board, again purchased from Adafruit. This is hooked into the +3.3V power coming off of the Nano and the SDA / SCL 0 lines coming off of the multiplexer. I should have also added pull-up resistors to each breakout, but with only one controller on the bus it seems to work fine as-is.
I have all of this set up on a breadboard, so I can just plug in the USB from the Pro Micro and go. For a more technical overview of the Nunchuk and how it communicates with the Arduino, see this post.
Programming
The last step is to write a short Arduino program to convert the Nunchuk’s controls into keyboard presses. I’m using this library by Kevin Harrington to make the Nunchuk communication easier. It’s lacking a few features I’d like (pull requests incoming!), but the fundamentals work great.
Initialization
#include <Wire.h> #include <WiiChuck.h> #include <Keyboard.h> Nunchuck nunchuk(SDA, SCL); void setup() { // Safety check. Ground pin 9 to stop keyboard. pinMode(9, INPUT_PULLUP); if(digitalRead(9) == 0){ for(;;){} } setMultiplexer(); TWBR = 12; // Gotta go fast I2C nunchuk.begin(); if(nunchuk.checkButtonZ() && nunchuk.checkButtonC()){ // Nunchuk disconnected or both buttons pressed on startup for(;;){} } } void setMultiplexer(){ Wire.begin(); Wire.beginTransmission(0x70); Wire.write(1 << 0); // Switch to port 0 Wire.endTransmission(); }
The program starts as most do by importing the header files necessary – the Wire library to handle I2C, the WiiChuck library to handle the Nunchuk communication, and the Keyboard library to emulate keypresses.
Next I create a global ‘Nunchuk’ object and launch into the setup
function. The setup function starts the I2C communication with the Nunchuk and performs two safety checks to prevent keypress spamming in-case of a problem. The first of these checks is a hardware pin: just ground the pin (#9) to halt the program. The second check is just holding the Nunchuk’s two buttons on startup.
At the moment, the library has no functions to check if the controller is connected or not. I would prefer to use the Nunchuk’s ‘controller detect’ pin for this, but unfortunately it isn’t exposed on my breakout boards. Although through a stroke of luck both of the controller’s buttons are read as ‘pressed’ when they send a ‘0’ bit, so the 0-filled array when it’s not populated via I2C will ‘press’ these buttons if the controller is disconnected. That means my halt method is also triggered if the controller isn’t plugged in. Neat, huh?
I also have a short function in there to set my multiplexer (that I’m using as a level shifter) to its 0 port. If I got a dedicated level shifter I could remove that line, but I only need to set the initial port and it otherwise doesn’t affect the speed.
Button Setup
Here I’m doing something a little differently than the Nerf gun, which only has two actual “buttons” on it.
struct button { const char key; boolean pressed = 0; button(char k) : key(k) {} void press(boolean state){ if(state == pressed){ return; } state ? Keyboard.press(key) : Keyboard.release(key); pressed = state; } } ;
First, I’m setting up a small struct so that I can handle each control as an object. The struct contains two variables for keeping track of the associated keyboard key and whether or not the button is pressed. It also contains a short function that calls the respective ‘press’ or ‘release’ function if the button changes states which avoids input spamming.
Due to the way the Arduino IDE builds prototypes, this needs to be in a separate file which I’m cleverly calling “button.h”. No guards or anything, because I like to live on the edge.
#include "button.h" button moveForward('w'); button moveLeft('a'); button moveBack('s'); button moveRight('d'); button jump(' '); button roll(KEY_LEFT_SHIFT); button commWheel('c'); button scoreboard(KEY_TAB);
I’ve added global button
objects for each control at the top of the sketch. Calling <button>.press(0/1)
will press or release the respective keyboard button.
Getting Loopy
The loop is short and to the point:
void loop() { nunchuk.readData(); joyWASD((uint8_t) nunchuk.getJoyX(), (uint8_t) nunchuk.getJoyY()); handleButtons(nunchuk.checkButtonC(), nunchuk.checkButtonZ()); handleAccel(nunchuk.getAccelY()); }
The Nunchuk is polled for new data, and then each control is processed in turn: first the movement keys, then the dedicated buttons, and finally the accelerometer-based controls.
Since I’m not scaling anything based on time, I can do the polling as fast as possible to keep the controller feeling responsive.
WASD Parsing
void joyWASD(uint8_t x, uint8_t y){ button * movementKeys[4] = {&moveRight, &moveLeft, &moveForward, &moveBack}; // X+, X-, Y+, Y- const uint8_t joyDeadzone = 60; // +/-, centered at 127 uint8_t joyXY[2] = {x, y}; for(int i = 0; i < 2; i++){ movementKeys[i*2]->press(joyXY[i] >= 128 + joyDeadzone); movementKeys[i*2 + 1]->press(joyXY[i] <= 128 - joyDeadzone); } }
The WASD
function is passed the X and Y joystick values and loops through each axis to check if the range is outside of a deadzone set around the center of the joystick range. If it is, the respective key is pressed. If not, the key is released. This gives the joystick 8 degrees of freedom. Not as great as an analog stick, but it’ll work fine.
I wish I could’ve done analog movement, but this isn’t easy to do with Overwatch. See the note at the bottom of the page.
Button Parsing
void handleButtons(boolean bC, boolean bZ){ if(bC && bZ){ commWheel.press(true); } else{ commWheel.press(false); roll.press(bC); jump.press(bZ); } }
This little function is probably the simplest part of the whole code. If both buttons are pressed, do that command. Otherwise, deal with each button separately.
Accelerometer Parsing
void handleAccel(uint16_t aY){ const uint16_t accelThreshold = 360; // Controller pointed up const uint8_t countThreshold = 6; // Arbitrary limit to filter noise static uint8_t ayCount = 0; if(aY <= accelThreshold){ ayCount++; if(ayCount >= 6){ scoreboard.press(true); ayCount = 6; } } else{ ayCount = 0; scoreboard.press(false); } }
The accelerometer setup is also simple. The code checks if the accelerometer is below a given value for a given number of samples, and if so presses the button. If the accelerometer goes above the threshold the number of samples resets and the button is released.
That’s it! Short, sweet, and to the point. Much simpler than the Nerf gun code.
The code’s only weak-spot at the moment is if the Nunchuk is disconnected. The microcontroller will hold the ‘C’ and ‘Z’ button inputs (‘LSHIFT’ and ‘space’ respectively) and will need to be reset before the Nunchuk will work again. Thankfully the breakout boards I’m using take advantage of the retaining clips on the Nunchuk’s connector, so it should only come undone if I accidentally pull the board off of my desk and the breakout comes out of the breadboard. Which, given my clumsy tendencies, may happen at some point!
Playtesting
With everything finished it was time to load up the game and put the complete setup through its paces.
After just one game, it’s clear that this is a huge upgrade over the dance pad. The controls are much faster and more intuitive to use, and combined with the aiming algorithm changes it’s a force to be reckoned with. To be honest it almost feels like I’m cheating compared with the dance pad. Which I kind-of am… it is half of a genuine controller, after-all. Although the point of this project to play with the Nerf gun, not to make it harder to play the game.
To me, this clip looks like anyone else playing Overwatch. Look at how easily I change between walking forward and strafing, and how quickly I can react once I see the Reaper.
One thing I noticed is that I’m a lot more hesitant to take my hand off of the Nunchuk. I think this is probably subconsciously not wanting to drop the controller; I tend to “set it down” to where it’s free hanging rather than just let it go, which means I’m slow when switching my hand to the revolver. Because of this I don’t reload as often and I almost always go for a headshot rather than fanning the hammer on stunned targets.
In the latest playtest I even won a free-for-all deathmatch – and rather handedly, at that. 20 kills, 10 deaths. Playing McCree with a Nerf gun. That’s absolutely crazy to me.
Conclusion
At this point, I consider the controller definitively done! Everything is assembled, programmed, and plays great. All that’s left to do is finish up the last of the documentation.
Next Up: Conclusion!
Overwatch XInput Woes
Before ending this post I wanted to make a brief comment on why I decided on using keyboard emulation rather than joystick emulation for the Nunchuk.
Although the Arduino has a Joystick library available that allows me to emulate an analog joystick with a few buttons, it does not work in Overwatch. Like a whole host of games these days, Overwatch uses XInput for its controller inputs. Xbox 360 and Xbox One controllers work perfectly out of the box, but many 3rd party controllers aren’t detected by the game at all even though they are seen by Windows.
The solution in many cases is to use software like x360CE to emulate an Xbox 360 controller. I haven’t tried it, but according to their documentation Overwatch is not a supported game.
In any case, I’m not going to mess around with getting Overwatch to see the Nunchuk as a controller. There are too many horror stories of players getting banned for using software to emulate keystrokes that I’m going to stay far away from it – at least for the time being.