26 April 2026

Team 12- Game Controller

 


Our controller’s shape and overall design is based on the Type-A fighter which acts as a boss in ZeroRanger. This ship is the same type as those used by the player. To control their up and down movement the player tilts the controller forwards and back, this movement is tracked by the accelerometer on the Circuit Playground. To move left and right the player turns the potentiometer in the direction they want to move. The player’s three fire inputs are controlled by light sensors. When the sliding panel above the sensor is moved, it is revealed to the light and inputs the corresponding fire command in game. Fire 1 acts as the select button in menus while fire 2 is the back button. Lastly by moving the switch the player can transform their ship into melee mode then flip the switch back to return to the standard ship. The sections of the controller used to input are colored in orange, allowing players to find them easily and know which parts of the controller will move. The three light sensors are also placed roughly where that weapon will fire from in the game. Alongside the game’s audio and visual feedback the controller provides physical feedback with each input resulting in a motion on the controller. The controller also provides additional visual feedback with the player able to see the revealed light sensor or use the marks on the potentiometer to tell its current position.

Question: How could the design better allow the player to input different actions in quick succession?


Schematic- 


Code-

 #include <Adafruit_CircuitPlayground.h>

#include <Adafruit_Circuit_Playground.h>

#include "Keyboard.h"

 

//define variables for initial light values

int ZeroRead;

int TwoRead;

int ThreeRead;

 

void setup() {

Serial.begin(9600);

CircuitPlayground.begin();

Keyboard.begin();

delay(1000);

//set initial light values

int ZeroRead = analogRead(A0);

int TwoRead = analogRead(A2);

int ThreeRead = analogRead(A3);

}

 

//define variables for controlling release of fire buttons

bool fire1 = 0;

bool fire2 = 0;

bool fire3 = 0;

 

//define variables for controlling transform macro

bool oldform = 0;

bool newform = 1;

 

void loop() {

//Only read inputs if CircuitPlayground switch is set to on position

if (CircuitPlayground.slideSwitch() < 1) {

//Press Z(fire1) while light sensor 1 is uncovered and release it when it is covered

  if (analogRead(A2) > TwoRead + 20) {

    Keyboard.press('z');

    fire1 = 1;

  }

  if (fire1 == 1) {

  if (analogRead(A2) <= TwoRead + 20) {

     Keyboard.release('z');

     fire1 = 0;

     }

  }

//Press X(fire2) while light sensor 2 is uncovered and release it when it is covered

  if (analogRead(A0) < (ZeroRead + 20)) {

    Keyboard.press('x');

    fire2 = 1;

  }

  if (fire2 == 1) {

  if (analogRead(A0) >= (ZeroRead + 20)) {

     Keyboard.release('x');

     fire2 = 0;

     }

  }

//Press C(fire3) while light sensor 3 is uncovered and release it when it is covered

  if (analogRead(A3) < (ThreeRead + 20)) {

    Keyboard.press('c');

    fire3 = 1;

  }

  if (fire3 == 1) {

   if (analogRead(A3) > (ThreeRead + 20)) {

     Keyboard.release('c');

     fire3 = 0;

     }

  }

//Write Left Ctrl(fire1+2+3 macro) when the switch changes position

  if (oldform != newform) {

   if (analogRead(A7) > 1000 ) {

     Keyboard.write(KEY_LEFT_CTRL);

     newform = 0;

  }

  }

  if (oldform == newform) {

    if (analogRead(A7) < 50) {

      Keyboard.write(KEY_LEFT_CTRL);

      newform = 1;

    }

  }

//Move up and down based on accelerometer value

 if (CircuitPlayground.motionY() <= -2) {

  Keyboard.press(KEY_UP_ARROW);

 }

if (CircuitPlayground.motionY() > -2) {

  Keyboard.release(KEY_UP_ARROW);

  }

 if (CircuitPlayground.motionY() >= 2) {

  Keyboard.press(KEY_DOWN_ARROW);

 }

if (CircuitPlayground.motionY() < 2) {

  Keyboard.release(KEY_DOWN_ARROW);

  }

//Move left or right based on potentiometer value

 if (analogRead(A6) <= 400) {

  Keyboard.press(KEY_RIGHT_ARROW);

 }

if (analogRead(A6) > 400) {

  Keyboard.release(KEY_RIGHT_ARROW);

  }

 if (analogRead(A6) >= 700) {

  Keyboard.press(KEY_LEFT_ARROW);

 }

if (analogRead(A6) < 700) {

  Keyboard.release(KEY_LEFT_ARROW);

  }

}

}

Video-

(Due to sudden mechanical failures our controller was not functional to record the video)

Below is a video showing the two sensors that are still working:





 



Final Project Team 8: Hidden Folks Game Controller


Our game controller is made to reflect the theme of the game Hidden Folks. In the game, players must search through mini landscapes to find certain people, things, or objects. The game, which consists of a black and white color palette, has simple inputs, consisting of zoom in/zoom out buttons, a select button, and a pan button to move across the landscape.Due to the overall simplistic nature of the game, we chose to carry the theme into the controller by creating a treasure map controller. The controller consists of two cylinders and a middle portion, which simultaneously give the player somewhere to grab onto and also visually represent the two scroll portions of an ancient map. The middle portion is decorated overtop with a hand-drawn map, integrating both a light sensor and a touchpad into the layout.


There are a total of 4 inputs: a light sensor, a potentiometer, a touchpad sensor, and the circuit's tilt mechanism. The light sensor replaces the "click" option for the game, which allows players to select objects within the landscape. Because the player is searching for objects/people, the "X marks the spot" on the controller signifies clicking to find/search. The potentiometer replaces the zoom in/zoom out function, which allows players a closer look at the landscape. The touchpad sensor replaces the click and hold that allows players to move around the level as needed. It is shown as a square within the map to signify the search, having to press to apply pressure and look around. The tilt gives motion to move the mouse around the screen. It signifies how you have to carefully search a map such as needing to move it around to find what you need.


Peer Review Feedback: Based on the inputs we chose and the theme of the controller, are there alternative inputs that would have worked better or should have been added to have a more cohesive design?

Controller Image:




Video:


Schematic:


Code:

#include <Mouse.h>
#include <Adafruit_CircuitPlayground.h>

// ---------------- PINS ------------------
// making variables for each pin and what it connects to on the CPE
const int fsrPin = A1;
const int lightSensorPin = A2;
const int potPin = A3;

// ---------------- STATES ----------------
// variables used for keeping info to reference later
bool leftHeld = false;
int lastPotValue = 0;

// ---------------- SETUP -----------------
void setup() {
  // circuit and mouse tracking, along with the 3 analog inputs
  Serial.begin(9600);
  CircuitPlayground.begin();
  Mouse.begin();
  pinMode(fsrPin, INPUT);
  pinMode(potPin, INPUT);
  pinMode(lightSensorPin, INPUT);
  lastPotValue = analogRead(potPin);
}

// ---------------- LOOP FUNCTIONS ------------------
void loop() {
  // repeated functions
  handleSensors();
  handleTilt();
  handlePot();
  //lightCheck();
  //fsrCheck;
 
  delay(10);
}

/*void lightCheck() {
  // checking light values
  int lightVal = analogRead(lightSensorPin);
  Serial.print("Light Sensor Value: ");
  Serial.println(lightVal);

  delay(200);
}
*/

/*void fsrCheck() {
  // checking pressure values
  int fsrReading;
  fsrReading = analogRead(fsrPin);
  Serial.print("Analog Reading = ");
  Serial.print(fsrReading);

  delay(200);
}
*/

// ---------------- SENSOR HANDLING ----------------
void handleSensors() {
  // variables to get readings on each sensor / how it activates
  int fsrVal = analogRead(fsrPin);
  int lightVal = analogRead(lightSensorPin);
  bool fsrPressed = fsrVal > 500;              // pressure pad threshold
  bool lightTriggered = lightVal < 600;        // darkness threshold

  // ---------- PRESSURE PAD = HOLD ------------
  // hold down mouse when fsr pressure increases
  if (fsrPressed && !leftHeld) {
    Mouse.press(MOUSE_LEFT);
    leftHeld = true;
  }
  else if (!fsrPressed && leftHeld) {
    Mouse.release(MOUSE_LEFT);
    leftHeld = false;
  }

  // ---------- LIGHT SENSOR = PRESS ----------
  // light sensor activating in darkness presses left click
  static bool lastLightState = false;
  if (lightTriggered && !lastLightState) {
    Mouse.click(MOUSE_LEFT);
  }
  lastLightState = lightTriggered;
}

// ---------------- TILT ----------------
// tilt movement around the screen, adjusting speed as needed
void handleTilt() {
  float x = CircuitPlayground.motionX();
  float y = CircuitPlayground.motionY();
  int moveX = -x * 2;
  int moveY = y * 2;
  // dead zone
  if (abs(moveX) < 2) moveX = 0;
  if (abs(moveY) < 2) moveY = 0;
  Mouse.move(moveX,moveY);
}

// ---------------- POTENTIOMETER ----------------
void handlePot() {
  int potValue = analogRead(potPin);

  static int potFiltered = 0;
  potFiltered = (potFiltered * 3 + potValue) / 4;

  int diff = potFiltered - lastPotValue;

  if (abs(diff) > 5) {
    int scrollAmount = diff / 10;

    if (!leftHeld) {
      Mouse.move(0, 0, scrollAmount);
    }

    lastPotValue = potFiltered;
  }
}

Final Controller Team 23: golf it

 



Our project is a custom physical controller designed specifically for the game Golf It, with the goal of making the gameplay feel more intuitive, consistent, and thematically connected to real golf. The conceptual model behind our design is based on translating the game’s mouse‑based swing mechanic into a larger, more physical motion that resembles pulling back and swinging a golf club. Instead of relying on small, inconsistent mouse movements, our controller uses a large joystick connected to a potentiometer, allowing players to physically pull back and push forward to control swing strength. This creates a clearer mental model: the farther and faster you move the joystick, the stronger the hit—just like in real golf.

To reinforce the theme, we incorporated a real golf ball and designed the button as a golf tee. The top surface of the controller uses a textured material similar to golf turf, helping players immediately understand the purpose of the device through visual and tactile signifiers. When the player interacts with the joystick or presses the tee‑button, the controller provides direct feedback through on‑screen movement in the game. The potentiometer sends analog values through A2, mapping physical motion to vertical mouse movement, while the button on A1 tells the game that you are ready to swing. These mappings create clarity, pull back to aim power, tilt to aim direction, and press the tee to start swing.

Our design evolved from a small square base into a larger rectangular platform to give players more space for movement and to make the interaction more fun and immersive.

Team Contributions:  
Mathieu: Wiring, coding, input mapping, and controller mechanics
Kai: Physical design, theming, materials, and assembly

Peer Review Question:  
How effectively does our controller communicate its purpose and golf‑themed interactions to a first‑time player without explanation?





Code 


#include <Mouse.h>
#include <Adafruit_CircuitPlayground.h>

int potPin = A2;     // potentiometer
int buttonPin = A1;  // button on A1
int centerValue = 512;
int deadzone = 40;

void setup() {
  CircuitPlayground.begin();
  Mouse.begin();
  Serial.begin(9600);

  pinMode(buttonPin, INPUT_PULLUP);  // button to GND
}

void loop() {

  //  POTENTIOMETER  UP/DOWN WITH SAFE ZONE
  int potValue = analogRead(potPin);
  Serial.println(potValue);
  int diff = potValue - centerValue;

  if (abs(diff) > deadzone) {
    int moveY = diff / 5;
    Mouse.move(0, moveY, 0);
  }

  //  BOARD ROTATED SIDEWAYS  USE motionY FOR LEFT/RIGHT
  float tiltY = CircuitPlayground.motionY();  // up/down tilt
  int moveX = tiltY * 2;                      // convert to left/right
  Mouse.move(moveX, 0, 0);

  // BUTTON  LEFT CLICK 
  static bool buttonWasPressed = false;
  bool buttonPressed = (digitalRead(buttonPin) == LOW);

  if (buttonPressed && !buttonWasPressed) {
    Mouse.click();
    delay(150);
  }

  buttonWasPressed = buttonPressed;

  delay(10);
}








Final Team 1 - Mario Bros. controller

    For this project, our team decided to create a controller for the Game Boy Advance port of Mario Bros., the arcade game. A primary goal our team was connecting controller aesthetics to the aesthetic of the game, and attempting to bridge functionality and believability. To achieve this, we chose plumbing pipes for the base of our design, as plumbing as a job and levels in the sewers are a primary focus of the game. Continuing with the pipe design, we included a valve wheel and wrench as inputs. To connect to the whimsical aspect of the game, we used a POW block, an item from the game, as a button on top of the pipe. Using these aspects to lay everything out, we connected each item to an input to bring the player into the job of the plumber characters. To signify each aspect as a potential input, we displayed the valve wheel and wrench in red to contrast with the mainly green design. The pow block stands as an additional blue object, and the top is lightly lifted, hinting again to the user how it can be interacted with. To connect each input with functionality, the valve wheel can be turned to control the direction of the game character, like one might control water. The wrench on the side is a built-in lever, and moving it up and down can control the player’s jumping and crouching. In-game, hitting the pow block can damage all enemies on screen. In our controller designer, players can hit the POW block, and trigger their ability to run. As the later sequels, the Super Mario Bros. games are known for vocally emoting as the move, loudly speaking at the controller(which the CPE is inside) will additionally allow the player to pause the game.

    Our team’s open ended question is: What size of the controller do you think would best suite the immersion and target audience of the game?

Virgil West:

  • Controller modeling
  • Blog posting
  • Schematics
  • Painting

Finn Albritton:

  • Coding
  • Bug testing
  • Writing
  • Soldering
#include <Keyboard.h>
#include <KeyboardLayout.h>
#include <Adafruit_CircuitPlayground.h>
#include <Adafruit_Circuit_Playground.h>

// Code for Project:Game Controller, Super Mario Bros.
// Final Team 1, Finnegan Albritton and Virgil West

// Global Variables

// Establishes A4 as the first potentiometer
const int potPin = A4;
int potValue = 0;

// Establishes A2 as the second potentiometer
const int secondpotPin = A2;
int secondpotValue = 0;

// Establishes A1 as the button
const int buttonPin = A1;

// Variables to check for inputs that need to be held down
bool crouchToggle = false;
bool buttonState = false;
bool buttonPrevState = false;
bool runToggle = false;

void setup() {
  // Sets up the CPE and Keyboard Controls
  CircuitPlayground.begin();
  Keyboard.begin();

  // Establishes the button mode
  pinMode(buttonPin, INPUT_PULLDOWN);
}


void loop() {

  // Controls function while the CPE switch is on
  if(CircuitPlayground.slideSwitch() == true) {

    //Reads the potentiometers and button
    potValue = analogRead(potPin);
    secondpotValue = analogRead(secondpotPin);
    buttonState = digitalRead(buttonPin);

    // Local variable activates the microphone and checks for sound
    int micCheck = CircuitPlayground.mic.soundPressureLevel(10);


    // Checks the first potentiometer for left and right movement

    // If the potentiometer is turned to the left, the keyboard input makes the character move left
    if(potValue <= 400) {
      Keyboard.press(KEY_LEFT_ARROW);
      delay(100);
      Keyboard.release(KEY_LEFT_ARROW);
    }

    // If the potentiometer is in the middle, the character does not move
    else if (potValue >= 401 && potValue <= 599){
      Keyboard.release(KEY_RIGHT_ARROW);
      Keyboard.release(KEY_LEFT_ARROW);
    }
// If the potentiometer is turned to the right, the keyboard input makes the character move right
    if(potValue >= 600){
      Keyboard.press(KEY_RIGHT_ARROW);
      delay(100);
      Keyboard.release(KEY_RIGHT_ARROW);
    }

    //Checks for the sound level of the user
    // If the user is loud enough, the keyboard input pauses the game
    if(micCheck >= 50){
      Keyboard.press(KEY_RETURN);
      delay(100);
      Keyboard.release(KEY_RETURN);
    }


    // Checks the second potentiometer for crouching and jumping movement

    // If the potentiometer is turned up, the keyboard input makes the character jump
    if(secondpotValue < 300){
      Keyboard.press('z');
    }

    // If the potentiometer is in the middle, the character does not take either action
    if (secondpotValue >= 300 && secondpotValue <= 400){
      // Resets the bool to false if input is not being held down
      crouchToggle = false;
      Keyboard.release(KEY_DOWN_ARROW);
      Keyboard.release('z');
    }

    // If the potentiometer is turned down, the keyboard input makes the character crouch
    if(secondpotValue > 400 && crouchToggle == false){
      // Sets the bool to true to hold down input
      crouchToggle = true;
      Keyboard.press(KEY_DOWN_ARROW);
    }


     //Checks to see if the button is being pressed
    if(buttonState == true){

      //If it is and the bool has not been triggered, the input will be held
      if(runToggle == false){
        Keyboard.press('x');
        runToggle = true;
      }
    }

    //If the button is not being pressed, the bool will reset and input will be released
    if(buttonState == false && runToggle == true){
      Keyboard.release('x');
      runToggle = false;
    }

  }
 
}


Finals Controller Team 20 - Sonic

Final Controller



Video Link: https://www.youtube.com/watch?v=z_nKT3N6RUU

For our project, we chose to create a controller for classic Sonic due to its simplistic inputs and heavy flow-based gameplay. We wanted to develop a controller that would allow players to physically immerse themselves in the fast paced experience in some way. For our controls, we wanted to create two white gloves, inspired by Silver's gloves and powers from the Sonic franchise, that would allow the player to interact with the game world. One hand uses a color sensor, having players grab different-colored gems, paying homage to the chaos emeralds from within the game, with each tied to an input. Players quickly grab and swap different gems to help them control Sonic's movement within the level. But this is not the only form of input we have. On the alternate glove, we have two “rings,” a reference to Silver's power displayed through white or Silver’s rings  in the Sonic franchise. The two rings can come together to complete a circuit, causing Sonic to jump in-game. Through these two forms, players will rapidly move their hands across the surface in front of them to grab at different emeralds, connect rings, and soar through the various fast-paced levels found within the game. From each glove come wires connected to its internal circuits, which go back to a central housing unit that houses the circuit playground. We hoped the gloves would serve as a strong signifier when developing the project, being associated with grabbing and holding, which naturally led to the idea and relationship of grabbing the emeralds. During the primary test, we felt that the motion of bringing the thumb and pointer finger felt natural, and with the two “rings” providing a visual indication, we thought they would work well. Players get the physical feel of grabbing and dropping the emeralds with one glove and the sensation of bringing their fingers together creates tactical senses in the other. Through our controller, we wanted to create a fast-paced, tactile experience that would engage the player in varied ways through their hand, hoping to generate a sense of flow not only in the game but also through the controls. Do you feel that alternate controls in fast-paced games, with more involved elements, would enhance or detract from the experience?

Circuit


Programming

#include <Keyboard.h>

#define S2 A1
#define S3 A2
#define OUT_PIN A3
#define TOUCH_PIN A4
#define SLIDE_SWITCH 7

bool upHeld = false;
bool rightHeld = false;
bool leftHeld = false;

void setup() {
  Serial.begin(9600);

  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(OUT_PIN, INPUT);

  pinMode(TOUCH_PIN, INPUT_PULLUP);
  pinMode(SLIDE_SWITCH, INPUT_PULLUP);

  Keyboard.begin();
}

unsigned long readColor(bool s2, bool s3) {
  digitalWrite(S2, s2);
  digitalWrite(S3, s3);
  delay(50);
  return pulseIn(OUT_PIN, LOW, 200000);
}

void loop() {

  // Slide switch OFF = everything disabled
  if (digitalRead(SLIDE_SWITCH) == HIGH) {
    Keyboard.releaseAll();
    upHeld = rightHeld = leftHeld = false;
    return;
  }

  // Jump input
  bool jumping = (digitalRead(TOUCH_PIN) == LOW);

  // Read colors
  unsigned long red = readColor(LOW, LOW);
  unsigned long green = readColor(HIGH, HIGH);
  unsigned long blue = readColor(LOW, HIGH);

  // DEBUG 
  Serial.print("R: "); Serial.print(red);
  Serial.print(" G: "); Serial.print(green);
  Serial.print(" B: "); Serial.println(blue);

  // yellow detec
bool isYellow =
  (green > red + 20) &&
  (blue > red + 20) &&
  (green > 130 && green < 200);

  bool isRed = (red < green && red < blue);
  bool isBlue = (blue < red && blue < green);
  bool isGreen = (green < red && green < blue);

  // PRIORITY: JUMP FIRST
  if (jumping) {

    Keyboard.releaseAll();
    upHeld = rightHeld = leftHeld = false;

    Keyboard.press('S');

  } else {

    Keyboard.release('S');

    // YELLOW = ENTER
    if (isYellow) {
      Keyboard.releaseAll();
      upHeld = rightHeld = leftHeld = false;

      Keyboard.write(KEY_RETURN);
      delay(300); // prevent spam
      return;
    }

    // UP (RED)
    if (isRed) {
      if (!upHeld) {
        Keyboard.press(KEY_DOWN_ARROW);
        upHeld = true;
      }
    } else if (upHeld) {
      Keyboard.release(KEY_DOWN_ARROW);
      upHeld = false;
    }

    // RIGHT (BLUE)
    if (isBlue) {
      if (!rightHeld) {
        Keyboard.press(KEY_RIGHT_ARROW);
        rightHeld = true;
      }
    } else if (rightHeld) {
      Keyboard.release(KEY_RIGHT_ARROW);
      rightHeld = false;
    }

    // LEFT (GREEN)
    if (isGreen) {
      if (!leftHeld) {
        Keyboard.press(KEY_LEFT_ARROW);
        leftHeld = true;
      }
    } else if (leftHeld) {
      Keyboard.release(KEY_LEFT_ARROW);
      leftHeld = false;
    }
  }

  delay(50);
}

Credits

Building of the Gloves: John and Charlotte

Creation of Emeralds: Charlot

Programming: Charlot

Writing: John

Video/Photo: Charlotte

Circuit Schematics: John



Finals Team 10 Controller - Undertale

 

Controller Photo




Controller Description

This project is for the game Undertale. It focuses on adapting the main villain of the game- flowey as a full controller. We used a lot of the flower theme with our photo resistors functioning as petals that absorb light. We wanted to take the theme of the game into account too so we decided to go more into the soul angle. A soul or a person feels something in one way or another. So we added both touch and sound into the controller. The pressure sensor was our confirm key and the CPE's mic was our cancel key. Whenever someone clapped it got registered. Our controller itself reacts to everything you do in a physical setting, almost like something alive. 

The input-to-output mapping utilizes four photoresistors to control movement. By covering, the player creates a shadow that maps to the Up, Down, Left, or Right arrow keys. For dialogue and interaction, a pressure sensor and a potentiometer are used. The pressure sensor maps to the 'Z' key for the confirm button, while the potentiometer serves as a macro for 'X' to skip dialogue. Finally, a microphone sensor maps a loud sound like a clap to the 'X' key for cancelling or opening the menu.

In this design, the signifiers are these elements coming together in one design. Shadows allow for movement, potentiometer allows for text skipping, and the clap not only cancels but gives menu access. Like mentioned before this controller responds to any player feedback in an organic way. 




What do you think of the potentiometer as a modern dialogue skipper that most RPGs have nowadays but being used as a macro for a game like Undertale that came out in 2015?


Controller Schematic





Code


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

//Pin Definitions
const int potPin = A1; //Potentiometer to A1 (Auto z/x)
const int pinUp = A2; // Photoresistor to A2 (W)
const int pinRight = A3; // Photoresistor to A3 (D)
const int pinDown = A4; // Photoresistor to A4 (S)
const int pinLeft = A5; // Photoresistor to A5 (A)
const int pinZ = A6; // Pressure Sensor to A6 (Z)

//Treshold stuff
const int lightTreshold = 150;
const int pressureTreshold = 200;
const int potTreshold = 10;
const int soundTreshold = 85;

//Tracking variables to fix the spam stuff
bool stateUp = false;
bool stateLeft = false;
bool stateDown = false;
bool stateRight = false;
bool stateZ = false;

//Clap variables
unsigned long lastClapTime = 0;
const unsigned long clapCooldown = 500; //cooldown to prevent clap spam

// Variables for the potentiometer delay
unsigned long lastPotActionTime = 0;
const unsigned long potInterval = 50; // Delay for the potentiometer spam
bool potIsPressed = false;

void setup()
{
    Serial.begin(9600); 
    CircuitPlayground.begin(); 
    Keyboard.begin();
}

void loop()
{
    // 1. Read sensors
    int valUp = analogRead(pinUp);
    int valRight = analogRead(pinRight);
    int valDown = analogRead(pinDown);
    int valLeft = analogRead(pinLeft);
    int valZ = analogRead(pinZ);
    int potVal = analogRead(potPin);

    // 2 Movement

    //W
    bool currentUp = (valUp < lightTreshold);
    if (currentUp && !stateUp)
    {
        Keyboard.press(KEY_UP_ARROW);
        stateUp = true;
    }
    else if (!currentUp && stateUp)
    {
        Keyboard.release(KEY_UP_ARROW);
        stateUp = false;
    }

    //A
    bool currentLeft = (valLeft < lightTreshold);
    if (currentLeft && !stateLeft)
    {
        Keyboard.press(KEY_LEFT_ARROW);
        stateLeft = true;
    }
    else if (!currentLeft && stateLeft)
    {
        Keyboard.release(KEY_LEFT_ARROW);
        stateLeft = false;
    }

    //S
    bool currentDown = (valDown < lightTreshold);
    if (currentDown && !stateDown)
    {
        Keyboard.press(KEY_DOWN_ARROW);
        stateDown = true;
    }
    else if (!currentDown && stateDown)
    {
        Keyboard.release(KEY_DOWN_ARROW);
        stateDown = false;
    }

    //D
    bool currentRight = (valRight < lightTreshold);
    if (currentRight && !stateRight)
    {
        Keyboard.press(KEY_RIGHT_ARROW);
        stateRight = true;
    }
    else if (!currentRight && stateRight)
    {
        Keyboard.release(KEY_RIGHT_ARROW);
        stateRight = false;
    }

    // 3 confirm (z) Checks if the sensor is pressed
    bool currentZ = (valZ < pressureTreshold);
    if (currentZ && !stateZ)
    {
        Keyboard.press('z');
        stateZ = true;
    }
    else if (!currentZ && stateZ)
    {
        Keyboard.release('z');
        stateZ = false;
    }

    // 4 Cancel (X)
    float sound = CircuitPlayground.mic.soundPressureLevel(10);
    if (sound > soundTreshold && (millis() - lastClapTime > clapCooldown))
    {
        Keyboard.write('x'); //millis reset the timer and .write presses it instantly
        lastClapTime = millis();

        //TO TEST CircuitPlayground.setPixelColor(0, 0, 255, 0); delay(50); CircuitPlayground.clearPixels(); this flashes a light for the mic
    }

    // 5 Potentiometer(Dialogue Skip "Z" and "X")
    if (potVal > potTreshold)
    {
        if (millis() - lastPotActionTime > potInterval)
        {
            if (!potIsPressed)
            {
                Keyboard.press('x'); // skip text typing
                potIsPressed = true;
            }
            else
            {
                Keyboard.release('x');
                potIsPressed = false;
            }
            lastPotActionTime = millis();
        }
    }
    else if (potIsPressed)
    {
        Keyboard.release('x');
        potIsPressed = false;
    }

    delay(10); // Small delay for everything to loop properly
}

Video




Final Controller

 


https://youtu.be/AZuoEMlLxeE

Our controller for our game is a tank that was put together and has the controls inside like a normal tank. The way this model works well with our game is that it is a literal tank inside the tank game. We devised it to have the shooting button in the little gunner position and have its movements attached to the movement of the tank you hold. Our controller design has the connection in the back of the tank as if it was running on the gas you used in the tank. The logical side of our design is that whenever the player wants to move the character they need to go in that direction in order for the actual character to move in that direction. We also have it to where the button for shooting isn't being pressed quickly so the player can't repeatedly shoot over and over again at a fast speed. The way this controller connects to our game is that they are both tanks and they are also able to be controlled in a similar fashion or resemble a tanks capabilities in real life. We originally wanted to do a tank helmet which would make the player feel like they are inside the tank but we switched it to an actual tank design so the player really feels like they are controlling the tank in the game. How would you improve the engineering side of this tank and the internal mechanics?



  1. #include <Adafruit_CircuitPlayground.h> #include <Keyboard.h> //dead zone float forwardValue = 3.0; float backwardValue = -3.0; float leftValue = -3.0; float rightValue = 3.0; const int buttonPin = A1; bool lastButtonState = HIGH; //begin code void setup() { CircuitPlayground.begin(); Keyboard.begin(); pinMode(buttonPin, INPUT_PULLUP); } //detect angle output to x and y void loop() { float x = CircuitPlayground.motionX(); float y = CircuitPlayground.motionY(); // Up if (y > forwardValue) { Keyboard.press(KEY_UP_ARROW); } else { Keyboard.release(KEY_UP_ARROW); } // Down if (y < backwardValue) { Keyboard.press(KEY_DOWN_ARROW); } else { Keyboard.release(KEY_DOWN_ARROW); } // Left if (x < leftValue) { Keyboard.press(KEY_LEFT_ARROW); } else { Keyboard.release(KEY_LEFT_ARROW); } // Right if (x > rightValue) { Keyboard.press(KEY_RIGHT_ARROW); } else { Keyboard.release(KEY_RIGHT_ARROW); } // Button Z bool currentButtonState = digitalRead(buttonPin); if (lastButtonState == HIGH && currentButtonState == LOW) { Keyboard.press('z'); delay(50); Keyboard.release('z'); } lastButtonState = currentButtonState; }