27 April 2026

Team 21 - Final Controller: Getting Over it

 Image:


Video:


Description:

Our controller is a physical sledgehammer built to match the tool at the center of Getting Over It with Bennett Foddy, a game about perseverance, frustration, and the act of struggling with a simple object. The hammer is constructed from a PVC pipe handle and a 3D printed head, giving it the weight, shape, and feel of the real thing. The Circuit Playground Express is housed inside the head, keeping the exterior clean.

The conceptual model is simple: you are holding a hammer, and the hammer controls the game. When you tilt it, the cursor moves. When you click the button, it clicks. There is no hidden logic to learn or mental translation required. The idea is that anyone who picks it up should immediately understand what to do, because it behaves the way a physical tool in your hand should behave. A button mounted on the side of the handle acts as a left click for navigating menus. Its placement on the handle keeps it accessible during natural grip without interrupting play. A potentiometer, also mounted on the handle, controls pause and unpause: turn it one direction to pause, the other to resume.

The signifiers here are largely physical. The hammer's form tells you to grip it, tilt it, and interact with it as you would any real tool. The side mounted controls are within thumb reach, signaling that they are supplementary to the primary tilt input rather than competing with it. Feedback is delivered through on screen response: the cursor moves as you tilt, reinforcing the tilt to motion mapping immediately. Together, the design argues that the controller should feel like an extension of the game's theme: deliberate, physical, and a little absurd.

Peer review: Through trial and error, we were able to adjust the sensitivity of the hammer for the mouse movement, but there is no way to adjust this set up outside of the code. How would you go about adjusting the sensitivity/calibrating the hammer while playing?




Schematic:



Code:

#include <Keyboard.h>
#include <KeyboardLayout.h>
#include <Adafruit_CircuitPlayground.h>
#include <Mouse.h>
#include <Wire.h>
#include <SPI.h>

int buttonPin = A3; // button for left click
//int shakeThresh = 40; // shake for pause, shake removed

int potPin = A2; // potentiometer pin
int threshold = 800; // threshold for pause on pot
bool wasAbove = false; // state for potentiometer so esc is inputted both ways

// mouse settings
// x movement settings
#define X_MIN 0.1
#define X_MAX 8.0
#define X_SPEED 25.0
#define X_SCALE 1.5

// y movement settings
#define Y_MIN X_MIN
#define Y_MAX X_MAX
#define Y_SPEED X_SPEED
#define Y_SCALE 1.5

// Flip X and Y if needed
#define FLIP false // false makes up up, true is right up

// map a value from one range to another
float mapValue(float val, float inMin, float inMax, float outMin, float outMax) {
  if (val <= inMin) return outMin;
  if (val >= inMax) return outMax;
  return outMin + (outMax - outMin) * ((val - inMin) / (inMax - inMin));
}

void setup() {
  CircuitPlayground.begin();
  pinMode(buttonPin, INPUT_PULLUP); // start button
  Mouse.begin();             // start mouse control
  Serial.begin(9600);
  delay(1000);
}

void loop() {


  if (!CircuitPlayground.slideSwitch()) { // if switch is off controller is off
    return;
  }
  //button state removed with new button and new logic
  //bool button1 = digitalRead(buttonPin);
 
  float xTilt = CircuitPlayground.motionX(); // get values for which way its tilted
  float yTilt = CircuitPlayground.motionY();
 
  float xMove = mapValue(abs(xTilt), X_MIN, X_MAX, 0.0, X_SPEED); // turn tilt into speed
  float yMove = mapValue(abs(yTilt), Y_MIN, Y_MAX, 0.0, Y_SPEED);

  if (xTilt < 0) xMove *= -1; // direction of tilt
  if (yTilt < 0) yMove *= -1;
  xMove *= -1; // make sure right is right left is left

  // scale
  xMove = floor(xMove * X_SCALE);
  yMove = floor(yMove * Y_SCALE);

  // move mouse
  if (!FLIP) {
    Mouse.move((int)xMove, (int)yMove, 0);
  } else {
    Mouse.move((int)yMove, (int)xMove, 0);
  }

  delay(10); // lil delay

  // get button state later|removed with new button
  // bool button2 = digitalRead(buttonPin);
 
  // left click
  if (digitalRead(buttonPin) == LOW) {
  Mouse.press(MOUSE_LEFT);
} else {
  Mouse.release(MOUSE_LEFT);
}

 // escape button
  static bool wasPaused = false;
      int potValue = analogRead(potPin);

    bool isAbove = potValue > threshold;

   
    if (isAbove != wasAbove) { // if crossed the threshold (either direction)
      Keyboard.press(KEY_ESC);
      delay(50);
      Keyboard.release(KEY_ESC);
    }

    wasAbove = isAbove; // update da state
}




No comments:

Post a Comment

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