04 December 2023

Game Controller - The Bite of '87 (Clair Elso)


 

Hello hello? Hey, you’re doing great! Most people don’t last this long!…I mean, y’know. They usually move on to other classes by now. I’m not implying that they failed, that’s not what I meant…anyways, I better not take up too much of your time, things start getting real on this blog post - at least you’d hope so, given that it’s the final one. We’ve reached the end of this semester, which means it’s time for the final controller design!

 


This controller is meant to mimic the layout of the office you’re located in during FNAF 1, for a more immersive experience. Unfortunately, having tiny little door buttons would be far too finesse for a game where you’ll likely be jumping any time you need to press a button. So, I chose touch sensors for the door buttons to mimic the idea of slamming a door button. Using light sensors seemed like an obvious choice, but wound up having some ironic logic: you cover the light sensor in order to turn on the lights in-game. Maybe it’s something like turning out the overhead lights to find something glowing in your room. There are also 4 total LEDs (two on each side) used to signal the state of the doors & lights in-game: one RGB LED that changes between red and green depending on the door’s status of open or closed, and one white LED that lights up/flickers when the lights are active.

 

//left half, CPE & pot

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

// inputs
int touchSen = 6;
int lightSen = A2;
int pot = A7;

// outputs
int wLED = 10;
int rLED = A6;
int gLED = A5;

// variables for running
bool doorShut;
bool lightOn;
int potVal;

void setup() {
  Serial.begin(9600);
  Mouse.begin();
  Mouse.move(0, 0, 0);    // I think??? this moves the mouse to the bottom left corner of the screen
  pinMode(touchSen, INPUT);
  pinMode(wLED, OUTPUT);
  doorShut = false;
  lightOn = false;
  analogWrite(rLED, 255);
  delay(100);
}

void loop() {
  if(digitalRead(touchSen)) {
    Mouse.move(30, 500, 0);    // I'll be honest I'm estimating values here bc my CPE broke
    Mouse.click();
    delay(20);
    if(doorShut == false) {
      analogWrite(gLED, 255);
      analogWrite(rLED, 0);
      doorShut = true;
      delay(100);
    } else if(doorShut == true) {
      analogWrite(rLED, 255);
      analogWrite(gLED, 0);
      doorShut = false;
      delay(100);
    }
  }
 
  if(lightOn == false) {
    Mouse.move(40, 550, 0);
    Mouse.click();
    delay(20);
    if(map(analogRead(lightSen), 0, 1023, 0, 10) < 4) {
      digitalWrite(wLED, HIGH);
      lightOn = true;
      delay(100);
    }
  }
  else if(lightOn == true) {
    Mouse.move(40, 550, 0);
    Mouse.click();
    delay(20);
    if(map(analogRead(lightSen), 0, 1023, 0, 10) >= 4) {
      digitalWrite(wLED, LOW);
      lightOn = false;
      delay(100);
    }
  }

  potVal = map(analogRead(pot), 0. 1023, 0, 11);    // cycle through 11 cams, plus first option for shutting them
  switch(potVal) {
    case 1:
      // move mouse & click for cam 1a
      delay(100);
      break;
    case 2:
      // move mouse & click for cam 1b
      delay(100);
      break;
    case 3:
      // move mouse & click for cam 1c
      delay(100);
      break;
    case 4:
      // move mouse & click for cam 2a
      delay(100);
      break;
    case 5:
      // move mouse & click for cam 2b
      delay(100);
      break;
    case 6:
      // move mouse & click for cam 3
      delay(100);
      break;
    case 7:
      // move mouse & click for cam 4a
      delay(100);
      break;
    case 8:
      // move mouse & click for cam 4b
      delay(100);
      break;
    case 9:
      // move mouse & click for cam 5
      delay(100);
      break;
    case 10:
      // move mouse & click for cam 6
      delay(100);
      break;
    case 11:
      // move mouse & click for cam 7
      delay(100);
      break;
    default:
      // move mouse to shut cameras
      delay(100);
      break;
  }
}

 

 
//right half, CPB & toggle

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

// inputs
int touchSen = 1;
int lightSen = A3;
int tog = 12;

// outputs
int wLED = 3;
int rLED = A1;
int gLED = A2;

// variables for running
bool doorShut;
bool lightOn;
bool togOn;
int togVal;

void setup() {
  Serial.begin(9600);
  Mouse.begin();
  Mouse.move(0, 0, 0); // I'm assuming this moves it to bottom left corner
  delay(20);
  pinMode(touchSen, INPUT);
  pinMode(wLED, OUTPUT);
  pinMode(togVal, INPUT_PULLUP);
  doorShut = false;
  lightOn = false;
  togOn = false;
  analogWrite(rLED, 255);
  delay(100);
}

void loop() {
  if(digitalRead(touchSen)) {
    Mouse.move(1000, 500, 0);   // estimating values again
    Mouse.click();
    if(doorShut == false) {
      analogWrite(gLED, 255);
      analogWrite(rLED, 0);
      doorShut = true;
      delay(100);
    } else if(doorShut == true) {
      analogWrite(rLED, 255);
      analogWrite(gLED, 0);
      doorShut = false;
      delay(100);
    }
  }
 
  if(lightOn == false) {
    Mouse.move(990, 550, 0);   // estimating values again
    Mouse.click();
    if(map(analogRead(lightSen), 0, 1023, 0, 10) < 4) {
      digitalWrite(wLED, HIGH);
      lightOn = true;
      delay(100);
    }
  }
  else if(lightOn == true) {
    Mouse.move(990, 550, 0);   // estimating values again
    Mouse.click();
    if(map(analogRead(lightSen), 0, 1023, 0, 10) >= 4) {
      digitalWrite(wLED, LOW);
      lightOn = false;
      delay(100);
    }
  }

  togVal = digitalRead(tog); // I'm realizing now that I mis-wired this; probably needed two different values sent to the CPB to designate direction

  Mouse.move(0, 0, 0);   // reset mouse position

}

 

The potentiometer and toggle switch in the stage floor are parallels, yet opposites. The toggle switch allows for camera movement; since the game is controlled with the mouse, this is the only way you’re able to move the game camera without pressing buttons you don’t want to. The potentiometer is how you control the cameras; clamped into 10 sections, with the first one meaning that the cameras are closed, and the subsequent 9 opening the 9 security cameras you see in the game. It’ll lead to a lot of flashing cameras, but luckily with FNAF 1 it’s not seeing the big picture that’s important, it’s tricking the animatronics - specifically Foxy - into thinking you’re looking at him.

 

(Video)

 

For my design question: what kinds of props should I add to the miniature office?

 

Thanks for sticking with this project for this long. Catch you on the flip side!