What was originally called the Knight Motion Controller™ is now called the Gauntlet™ - a wireless motion controller. Instead of having the original design of being a handheld motion controller, I'm now utilizing a glove with compartments that stores the CPB and the battery pack. I came to the realization that this would be much more comfortable and intuitive to control. I have to preface the rest of the description by acknowledging why it's called the Gauntlet. The game I chose to design the controller around is one I made called Knightborn, it's set in medieval times and plated armor pieces for your hands are called gauntlets. The Gauntlet utilizes the accelerometer on the CPB to control everything from keyboard to mouse controls. Because I had to map 4 inputs and 8 directions of the mouse, I had to be a little creative for it to work adequately enough. The Gauntlet has 2 control options, one controls the character and left mouse button (LMB), the other controls mouse movement, I'll discuss how that works later. Knightborn uses the A/D keys for left/right character movement. Simply titling the glove (your hand) left/right will control the character. LMB is triggered when the controller is tilted forward and the escape key, which is used to open the menu, is triggered when the controller is tilted far enough backwards (towards you). The trickiest part about this controller was mouse motion inputs. After hours of tinkering, I decided that a toggle between character and mouse control was the best option. To switch from key input to mouse control, the glove must be rotated until your palm is face up and rotated back to switch between the two. When you are in the mouse control state, you have 8 directions to control the mouse cursor. In other words, I mapped the X and Y axes to a radian (a unit of angle) variable which divided a 360 degree radius into 8 segments, each segment mapped to their specific mouse direction. When the state of your controller is switched, a lower octave sound cue plays to signify character control, higher octave for mouse control. The name of the controller and the reason behind it connects to the game and some of the motions are similar to in-game actions, attacking is a downward controller movement and pausing the game requires a stop hand gesture.
/* PROJECT: GAME CONTROLLER** KNIGHTBORN MOTION GLOVE* BY MATTHEW POWELL** TO BE USED WITH THE GAME KNIGHTBORN*///**********************************************************************************// PRE-INITIALIZE//**********************************************************************************// LIBRARIES#include <Adafruit_CircuitPlayground.h>#include <Adafruit_Circuit_Playground.h>#include <bluefruit.h>#include "notes.h"// BLUEFRUIT BLUETOOTHBLEDis bledis;BLEHidAdafruit blehid;// HID (MOUSE)uint8_t const report_id = 0;int8_t const VERT = -5; // MOUSE MOVEMENTint8_t const HORZ = 5; // MOUSE MOVEMENT// TOGGLE CHARACTER/MOUSE CONTROLfloat M = 1;// BLUETOOTH CONNECTIONfloat B = 0;//**********************************************************************************// SETUP//**********************************************************************************void setup() {// BEGIN BLUEFRUIT/BLUETOOTHBluefruit.begin();Bluefruit.setTxPower(4);bledis.setManufacturer("Adafruit Industries");bledis.setModel("Bluefruit Feather 52");bledis.begin();blehid.begin();startAdv(); // STARTS BLUETOOTH FUNCTION// INPUTS/OUTPUTSpinMode(7, INPUT_PULLDOWN);// TESTING/*pinMode(4, INPUT_PULLDOWN);pinMode(5, INPUT_PULLDOWN);*/// DEBUGGING SETUP/*Serial.begin(9600);delay(1000);*/// BEGIN CIRCUIT PLAYGROUNDCircuitPlayground.begin();// SET SPEAKER VOLUMEanalogWrite (A0, 75);// BLUETOOTH IS SEARCHING & CONTROLLER IS ON AUDIO CUECircuitPlayground.playTone(C5, 80);CircuitPlayground.playTone(E5, 80);CircuitPlayground.playTone(G5, 80);}//**********************************************************************************// BLUETOOTH FUNCTION//**********************************************************************************void startAdv(void){// ADVERTISING PACKETBluefruit.Advertising.addTxPower();Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_MOUSE);// ADD BLE HID SERVICESBluefruit.Advertising.addService(blehid);// ADD NAME TO ADVERTISING PACKETBluefruit.Advertising.addName();// ADVERTISE IF DISCONNECTEDBluefruit.Advertising.restartOnDisconnect(true);// ADVERTISING INTERVALBluefruit.Advertising.setInterval(32, 244);// ADVERTISING FAST MODE TIMEOUTBluefruit.Advertising.setFastTimeout(30);// START ADVERTISINGBluefruit.Advertising.start(0);}//**********************************************************************************// RUNTIME FUNCTION//**********************************************************************************void loop() {// IF BLUEOOTH IS CONNECTED, PLAY AUDIO CUEif(Bluefruit.connected() && B == 0 ) {CircuitPlayground.playTone(G5, 80);CircuitPlayground.playTone(G5, 80);B = 1;}// X, Y, Zfloat x = CircuitPlayground.motionX();float y = CircuitPlayground.motionY();float z = CircuitPlayground.motionZ();float rad = atan2(y, x);// DEBUGGING/*Serial.print(x);Serial.print(" ");Serial.print(y);Serial.print(" ");Serial.print(z);Serial.print(" ");Serial.print(M);Serial.print(" ");Serial.print(rad);Serial.println(" ");*/// SWITCH BETWEEN KEYBOARD AND MOUSE CONTROLif(digitalRead(7) && M == 0) {keyboard();}if(digitalRead(7) && M == 1) {mouse();blehid.keyRelease();}// CONTROLLER FUNCTIONALITY IS OFF AND NULL KEYBOARD/MOUSE INPUT// PREVENTS FEEDBACK LOOPS AND ACTS AS AN EMERGENCY SHUTOFFif(!digitalRead(7)) {blehid.keyRelease();blehid.mouseButtonRelease();}}//----------------------------------------------------------------------------------// KEYBOARD CONTROLS//----------------------------------------------------------------------------------void keyboard() {// X, Y, Z, RAD FLOATSfloat x = CircuitPlayground.motionX();float y = CircuitPlayground.motionY();float z = CircuitPlayground.motionZ();// IF Z IS LESS THAN -9 & MOUSE VARIABLE IS SET TO 0, SWITCH TO MOUSE CONTROLSif(z < -9 && M == 0) {M = 1;CircuitPlayground.playTone(G4, 80);CircuitPlayground.playTone(G4, 80);blehid.keyRelease();blehid.mouseButtonRelease();delay(100);}// IF X AXIS VALUE IS GREATER THAN 6.5, INPUT KEYif(x > 4.5) {blehid.keyPress('a');}// IF X AXIS VALUE IS LESS THAN -6.5, INPUT KEY Dif(x < -7.5) {blehid.keyPress('d');}// IF X AXIS VALUE IS GREATER THAN 6.5, INPUT KEY ESCAPEif(y > 9) {uint8_t const modifier = 0;uint8_t keycode[6] = {0};keycode[0] = HID_KEY_ESCAPE;blehid.keyboardReport(report_id, modifier, keycode);delay(100);blehid.keyRelease();}// IF Y IS LESS THAN -8, TRIGGER LEFT MOUSE BUTTONif(y < -8) {blehid.mouseButtonPress(MOUSE_BUTTON_LEFT);delay(100);blehid.mouseButtonRelease();}// RELEASE A,D KEYS IF X AXIS IS BETWEEN 6.5,-6.5if(x < 4.5 && x > -7.5) {blehid.keyRelease();}}//----------------------------------------------------------------------------------// MOUSE CONTROLS//----------------------------------------------------------------------------------// WONKY MOUSE CONTROLS, REQUIRES FURTHER OPTIMIZATIONvoid mouse() {// X, Y, Z, RAD FLOATSfloat x = CircuitPlayground.motionX();float y = CircuitPlayground.motionY();float z = CircuitPlayground.motionZ();float rad = atan2(y, x);// IF Z AXIS IS LESS THAN -9 & MOUSE VARIABLES IS SET TO 1, CHANGE TO KEYBOARD CONTROLSif(z < -9 && M == 1) {M = 0;CircuitPlayground.playTone(C4, 80);CircuitPlayground.playTone(C4, 80);delay(100);}// IF Z AXIS IS LESS THAN 0, STOP MOUSE MOVEMENT, THAT WAY IF YOU'RE TRYING TO SWITCH TO// KEYBOARD CONTROLS TO USE LMB, THE MOUSE WON'T CHANGE POSITIONS.if(z < 0) {blehid.mouseMove(0, 0);}// UPif(rad < -1 && rad > -2) {blehid.mouseMove(0, VERT);}// UP/RIGHTif(rad < -2 && rad > -2.75) {blehid.mouseMove(HORZ, VERT);}// RIGHTif((rad < -2.75 && rad > -3) || (rad < 3 && rad > 2)) {blehid.mouseMove(HORZ, 0);}// DOWN/RIGHTif(rad < 2.75 && rad > 2) {blehid.mouseMove(HORZ, -VERT);}// DOWNif(rad < 2 && rad > 1) {blehid.mouseMove(0, -VERT);}// DOWN/LEFTif(rad < 1 && rad > .25) {blehid.mouseMove(-HORZ, -VERT);}// LEFTif(rad < .25 && rad > -.25) {blehid.mouseMove(-HORZ, 0);}// UP/LEFTif(rad < -.25 && rad > -1) {blehid.mouseMove(-HORZ, VERT);}}
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.