17 April 2022

Donut County Game Controller

 

For the Game Controller Project, I decided that I wanted to base it on the charming game Donut County. The gameplay for this game is centered around controlling a hole and using it to solve puzzles. You move the hole around with your mouse and as objects fall into the hole it grows in size. The hole will also take on different properties dependent upon what has fallen into it, but the entire gameplay is driven by moving the hole with the mouse. That is why i decided a great compliment to the visual feedback and immersion would be to use an actual donut to control the hole! The concept is simple, by using the built in accelerometer on the CPE we can tilt the donut on the X and Y axis and translate that to a mouse input. So you tilt the donut to move... it's hole! The enclosure itself is a simple donut, the CPE rests in a pouch beneath the hole and is obscured from view. The CPE is still able to be directly contacted for moments requiring a button input such as menu navigation and skipping dialogue. With this simple design, donut becomes a wired controller with all the functionality required to play through the entire experience! The goal for this was the create a thematic game controller that adds to the experience. With it's easy to discern signifiers and visual feedback and it's approachable means of interaction and aesthetic. I believe I have made a viable thematic controller!


The code:
#include <Adafruit_CircuitPlayground.h>
#include <Mouse.h>

// values to adjust the sensitivity and speed of the mouse.
// X axis
#define XACCEL_MIN 0.1      // Minimum range of X axis acceleration
#define XACCEL_MAX 8.0      // Maximum range of X axis acceleration
#define XMOUSE_RANGE 25.0   // Range of velocity for mouse movements
#define XMOUSE_SCALE 1      // Scaling value to apply to mouse movement

// Y axis
#define YACCEL_MIN XACCEL_MIN
#define YACCEL_MAX XACCEL_MAX
#define YMOUSE_RANGE XMOUSE_RANGE
#define YMOUSE_SCALE 1

// Set this true to flip the mouse X/Y axis with the board X/Y axis
#define FLIP_AXES true


// linear interpolation function that is used to transform
// each axis of acceleration to mouse velocity/speed. Linear_interpolation
float lerp 
(float x, float x0, float x1, float y0, float y1) { 
  // Check if the input value (x) is outside its desired range
    if (x <= x0) {
    return y0;
  }
  else if (x >= x1) {
    return y1;
  }
  return y0 + (y1-y0)*((x-x0)/(x1-x0));
}


void setup() {
  // Initialize Circuit Playground library.
  CircuitPlayground.begin();
  // Initialize Arduino mouse library.
  Mouse.begin();
}

void loop() {
  // Check if the slide switch is enabled (on +) and if not then just exit out, run the loop again
  if (!CircuitPlayground.slideSwitch()) {
    return;
  }

  // Grab initial left & right button states to later check if they are pressed
  boolean left_first = CircuitPlayground.leftButton();
  boolean right_first = CircuitPlayground.rightButton();

  // Grab x, y acceleration values (in m/s^2).
  float x = CircuitPlayground.motionX();
  float y = CircuitPlayground.motionY();
  // Use the magnitude of acceleration to interpolate the mouse velocity.
  float x_mag = abs(x);
  float x_mouse = lerp(x_mag, XACCEL_MIN, XACCEL_MAX, 0.0, XMOUSE_RANGE);
  float y_mag = abs(y);
  float y_mouse = lerp(y_mag, YACCEL_MIN, YACCEL_MAX, 0.0, YMOUSE_RANGE);
  // Change the mouse direction based on the direction of the acceleration.
  if (x < 0) {
    x_mouse *= -1.0;
  }
  if (y < 0) {
    y_mouse *= -1.0;
  }
  // Apply any global scaling to the axis (to flip it for example) and truncate
  // to an integer value.
  x_mouse = floor(x_mouse*XMOUSE_SCALE);
  y_mouse = floor(y_mouse*YMOUSE_SCALE);

  // Move mouse.
  if (!FLIP_AXES) {
    // Non-flipped axes, just map board X/Y to mouse X/Y.
    Mouse.move((int)x_mouse, (int)y_mouse, 0);
  }
  else {
    // Flipped axes, swap them around.
    Mouse.move((int)y_mouse, (int)x_mouse, 0);
  }

  // Small delay to wait for button state changes and slow down processing a bit.
  delay(10);

  // Grab a second button state reading to check if the buttons were pressed or
  // released.
  boolean left_second = CircuitPlayground.leftButton();
  boolean right_second = CircuitPlayground.rightButton();

  // Check for left button pressed / released.
  if (!left_first && left_second) {
    // Low then high, button was pressed!
    Mouse.press(MOUSE_LEFT);
  }
  else if (left_first && !left_second) {
    // High then low, button was released!
    Mouse.release(MOUSE_LEFT);
  }
  // Check for right button pressed / released.
  if (!right_first && right_second) {
    // Low then high, button was pressed!
    Mouse.press(MOUSE_RIGHT);
  }
  else if (right_first && !right_second) {
    // High then low, button was released!
    Mouse.release(MOUSE_RIGHT);
  }
}



 


No comments:

Post a Comment

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