18 April 2021

Thumper: Space Beetle


The space beetle controller is designed after its namesake; made to resemble the player’s character in Thumper, it aims to mirror the physicality of the game. 3D modeled and printed in the game avatar’s image; a distinct design effectively conveys a connection to its specialized purpose. Its controls are mapped with this in mind; squeezing both wings presses space and allows the player to speed up or jump when appropriate. This is handled by force-sensitive resistors held between the wing flaps. Turning the beetle left or right while the wings are down lets them slide in the corresponding direction while tilting the controller upwards opens the wings or allows the player to fly. All of these are inputs from the accelerometer mapped to a range that lets the CPE determine which direction the player intends to go. The signifiers rely on the player taking cues from their character, though given the expertise I would have liked to add lights in the eyes that denote which direction is currently being registered by the accelerometer. The feedback is a one-to-one response to their inputs; closing the wings causes the character to do the same, as with turning and flying. Its lack of external or visible buttons with a focus on the metallic wings tells the user that they need to interact with it in a more creative way than a traditional device. Allowing the player to consider their movements in a 3D space and experience the visceral rhythm of Thumper in a different light. 

Besides the LED idea, what other signifiers would compliment this design? I feel like physical feedback like a physical rumble and a buzzing sound for different movements would have contributed a lot to the legibility of the control scheme, but I also wonder if there was a method to incorporate printed instructions that wouldn’t have looked out of place.  

/////////////////////////////////
//  Space Beetle    //
//  By Julia Houha  //
////////////////////////////////

///////////////////////////////////////////////////////////////////
//          Instructions:                   //
// Jump: Space / Both Wings                 //
// Speed: W / Tilt Up                        //
// Left Slide: A / Tilt Left + Left Wing    //
// Right Slide: D / Tilt Right + Right Wing //
///////////////////////////////////////////////////////////////////

///////////////////////
//  Libraries  //
//////////////////////

#include <Keyboard.h>
#include <Adafruit_CircuitPlayground.h>
#include <Adafruit_Circuit_Playground.h>

///////////////////////
//  Variables  //
///////////////////////
// Create turn int
const int debounce = 100;
const int threshold = 500;

bool jumping = false;
bool sliding = false;
bool zooming = false;

float Lforce = 0.0;
float Rforce = 0.0;
bool leanL = false;
bool leanR = false;

const int lFSR_PIN = A2; // left FSR pin
const int rFSR_PIN = A3; // right FSR pin
const float VCC = 3.27;  // input voltage
const float R_DIV = 9930.0;  // resistor

////////////////////////////////
//  Keyboard Map  //
///////////////////////////////

void setup() {
  Serial.begin(9600);
  CircuitPlayground.begin();
  //Keyboard Inputs
  pinMode(CPLAY_SLIDESWITCHPIN, INPUT_PULLUP);  // failsafe for infinite loop
  pinMode(CPLAY_LEFTBUTTON, INPUT_PULLDOWN);    // placeholder left force sensor
  pinMode(CPLAY_RIGHTBUTTON, INPUT_PULLDOWN);   // placeholder right force sensor
  pinMode(lFSR_PIN, INPUT);                     // left FSR
  pinMode(rFSR_PIN, INPUT);                     // right FSR
  Keyboard.begin();
}

// Loop
void loop() {
  // Failsafe Switch
  if (!digitalRead(CPLAY_SLIDESWITCHPIN)) {
    Keyboard.releaseAll();
    //Serial.println("released all keys");
  }
  if (sliding == false) {
    Keyboard.release('A');
    Keyboard.release('D');
  }

  // Space Inputs
  if (jumping == false && leanL == true && leanR == true) {
    Keyboard.press(' ');
    jumping = true;
  }
  if (jumping == true && leanL == false || leanR == false) {
    Keyboard.release(' ');
    jumping = false;
  }
  slideX();
  zoomY();

  // LEFT FSR reading
  int LfsrADC = analogRead(lFSR_PIN);

  if (LfsrADC != 0) {
    float LfsrV = LfsrADC * VCC / 1023.0;
    float LfsrR = R_DIV * (VCC / LfsrV - 1.0);
    //Serial.println("Left Resistance: " + String(LfsrR) + "ohms");
    Lforce;
    float LfsrG = 1.0 / LfsrR;
    if (LfsrR <= 600)
      Lforce = (LfsrG - 0.00075) / 0.00000032639;
    else
      Lforce = LfsrG / 0.000000642857;
    Serial.println("Left Force: " + String(Lforce) + " g");
    Serial.println();

    //delay(500);
  }

  // RIGHT FSR reading
  int RfsrADC = analogRead(rFSR_PIN);

  if (RfsrADC != 0) {
    float RfsrV = RfsrADC * VCC / 1023.0;
    float RfsrR = R_DIV * (VCC / RfsrV - 1.0);
    //Serial.println("Right Resistance: " + String(RfsrR) + "ohms");
    Rforce;
    float RfsrG = 1.0 / RfsrR;
    if (RfsrR <= 600)
      Rforce = (RfsrG - 0.00075) / 0.00000032639;
    else
      Rforce = RfsrG / 0.000000642857;
    Serial.println("Right Force: " + String(Rforce) + " g");
    Serial.println();

    //delay(500);
  }

  // Setting left and right leans
  if (Lforce > 300) {
    leanL = true;
  } else
    leanL = false;

  if (Rforce > 300) {
    leanR = true;
  } else
    leanR = false;
}

// Slide
void slideX() {
  float x = CircuitPlayground.motionX();

  // map to accelerometer for left or right slide
  int slide = map(x, -10, 10, -3, 3);

  // left slide when rotated left with left button pressed
  if (sliding == false && slide == 2 && leanL == true) {
    Keyboard.press(' ');
    Keyboard.press('A');
    //delay(debounce);
    Serial.println("sliding left");
    //Serial.println(slide);
    sliding = true;
  }
  if (sliding == true && leanL == false) {
    Keyboard.release(' ');
    Keyboard.release('A');
    //delay(debounce);
    //Serial.println("stopped sliding");
    //Serial.println(slide);
    sliding = false;
  }

  // right slide when rotated right with right button pressed
  if (sliding == false && slide == -3 && leanR == true) {
    Keyboard.press(' ');
    Keyboard.press('D');
    //delay(debounce);
    Serial.println("sliding right");
    //Serial.println(slide);
    sliding = true;
  }
  if (sliding == true && leanR == false) {
    Keyboard.release(' ');
    Keyboard.release('D');
    //Serial.println("stopped sliding");
    sliding = false;
  }
  if (slide == 1 && leanL == false) {
    Keyboard.press('A');
  }
  if (slide == -1 && leanR == false) {
    Keyboard.press('D');
  }

}

// Flap
void zoomY() {
  float y = CircuitPlayground.motionY();

  // map to accelerometer for zoom
  int zoom = map(y, -10, 10, -3, 3);

  // zoom when pointed down
  if (zooming == false && zoom >= 2) {
    Keyboard.press('W');
    zooming = true;
  }
  if (zooming == true && zoom <= 1 || leanL == true || leanR == true) {
    Keyboard.release('W');
    zooming = false;
  }
}

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.