We created a custom controller for
the fast-paced competitive platformer, SpeedRunners. The controller is
housed withing a custom 3D-printed box designed to resemble the suitcase
obstacle that shows up frequently in the game. Our conceptual model focuses on
taking this harmful game element and making it the main way for the player to
interact with the game. Instead of being an obstacle, the suitcase becomes a
tool for the player to physically interact and manipulate from the game world
to control their character. This increases immersion and creates a unique
gameplay experience based on the game’s own iconography.
The input to output mapping
involves interactions with the controller components to make game actions. A
potentiometer is used as the directional control, turning the knob is mapped
directly to the character’s left and right movement. The accelerometer is used
to make the player jump or slide. A toggle switch is used to control the
grappling hook, flipping the switch on and off activates and deactivates the
hook, very similar to how it works in the game. The ultrasonic distance sensor
is connected to boost activation and item use, putting your hand over the
sensor at different distances leads to different actions.
The controller’s design has simple
signifiers to show how to use it. The suitcase links it to the game, the knob
is the only input that fits direction, the switch is to activate and deactivate
(the grapple is the obvious one but can also see this applied to the boost),
and the motion sensor shows that there is an element of interaction with
movement in that area. The player knows these actions worked by seeing the
character in the game perform the actions and hearing the switch flipping or
the knob turning.
All these elements combine to use
the player’s familiarity to the game and common input devices such as a knob or
switch, while introducing uncommon interactions such as the distance sensor
that can make gameplay more rewarding for the player.
Question:
Do you think the use of the ultrasonic sensor for potentially fast actions like boosting and item use is effective? Can the player maintain precise control over distance while in a fast-paced match of SpeedRunners?
Contributions:
Concept- Rodrigo
C & Ralfo M
Wiring – Rodrigo C
Initial Code – Ralfo M
Updated Code + bug testing – Rodrigo C
Final Code –
Ralfo M
Description –
Rodrigo C
Prototype Enclosure – Rodrigo C
3D Print Enclosure – Ralfo M
Video and Photograph – Ralfo M
Schematics – Rodrigo C
Code:
#include <Adafruit_CircuitPlayground.h>
#include <Keyboard.h>
#include <NewPing.h>
// Map pins for Ultrasonic sensors
#define trigPin 9
#define echoPin 6
// Map pins for Potentiometer, Toggle Switch & Kill Switch
#define POT_PIN A5
#define SWITCH_PIN A6
#define MASTER_SWITCH_PIN A4
void setup()
{
CircuitPlayground.begin();
Keyboard.begin();
// Initialize Serial Monitor
Serial.begin(9600);
// Map Potentiometer
pinMode(POT_PIN, INPUT);
// Map Toggle switch & Kill switch with pull-up resistor
pinMode(SWITCH_PIN, INPUT_PULLUP);
pinMode(MASTER_SWITCH_PIN, INPUT_PULLUP);
// Map Ultrasonic sensor pins as inputs
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
// CPX initialization buffer
delay(1000);
}
void loop()
{
// Check if master switch is OFF
if (digitalRead(MASTER_SWITCH_PIN) == HIGH)
{
Serial.println("Controller disabled");
Keyboard.releaseAll();
delay(100);
return;
}
int potValue = analogRead(POT_PIN);
float accelY = CircuitPlayground.motionY();
int switchState = digitalRead(SWITCH_PIN);
//Debugging: Print sensor values to Serial Monitor
//Serial.print("Boost Sensor: "); Serial.println(boostDistance);
//Serial.print("Item Sensor: "); Serial.println(itemDistance);
//Serial.print("Potentiometer: "); Serial.println(potValue);
//Serial.print("Accelerometer Z: "); Serial.println(accelZ);
//Serial.print("Switch State: "); Serial.println(switchState);
Serial.println(accelY);
//Declaring variables for Ultrasonic sensor use
long duration, distance;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration/2) / 29.1;
Serial.print(distance);
Serial.println(" cm");
//If hand detected close to sensor, use boost
if (distance <= 7)
{
Keyboard.press(0X20);
Serial.println("Boost used!");
}
else
{
Keyboard.release(0X20);
}
//If hand detected far from sensor, use item
if (distance >= 8 && distance <= 100)
{
Keyboard.press('c');
Serial.println("Item used!");
}
else
{
Keyboard.release('c');
}
delay(100);
// Potentiometer for left/right movement
if (potValue > 500)
{
Keyboard.press(KEY_LEFT_ARROW);
Keyboard.release(KEY_RIGHT_ARROW);
Serial.println("Moving Left!");
}
else if (potValue < 500)
{
Keyboard.press(KEY_RIGHT_ARROW);
Keyboard.release(KEY_LEFT_ARROW);
Serial.println("Moving Right!");
}
// CPX Accelerometer for jump & slide
if (accelY > 2.00)
{
Keyboard.press('z');
Serial.println("Jump used!");
}
else
{
Keyboard.release('z');
}
if (accelY < -2.00)
{
Keyboard.press(KEY_DOWN_ARROW);
Serial.println("Slide used!");
}
else
{
Keyboard.release(KEY_DOWN_ARROW);
}
// Toggle Switch for grappling hook
if (switchState == LOW)
{
Keyboard.press('x');
Serial.println("Grappling hook used!");
}
else
{
Keyboard.release('x');
}
// Small delay to prevent excessive key presses
//delay(50);
}
Image: