Ghost Busters – Imposter Edition 👀
Inspiration
When I was in 10th grade, I really got into ghost-busting shows. The thrill of seeing people go to renowned places of horror was amazing. Especially when none of it had anything to do with me, as I sit comfortably in my couch.
One of the devices that caught my attention appears in the show Buzzfeed Unsolved. When the hosts Ryan Bergara and Shane goes into places that are suspected to have entities, they open a sound-making device called the “spirit box”. The spirit box allegedly goes through multiple radio channels at a rapid pace, producing a sort of white noise. In this white noise, it is believed that ghost and entities would be able to make some sort of sound to communicate with the living
Specifics
For my project, I wanted to make a device that mimics what the spirit box does, adding some additional features. The system is composed of an Arduino and my laptop (processing). On the Arduino, there are two systems.
The detection system includes a photoresistor and an ultrasonic distance sensor. When the user walks into a haunted place, the photoresistor will notice the light getting darker, giving it an indication that there is a possibility for ghost activity. Then, the distance sensor will detect whether the user is close to a corner, wall, or object. These are all areas that are likely to have paranormal activity. There is also a piezo disk on the Arduino. While detecting, the piezo disk will inconsistently beep. There’s no particular use for it, just to add tension and mimic the original spirit-box.
Once an activity is detected, the box sends the signal to the controlling centre (laptop/processing). The box is able to detect what type of ghost it is as well. A good one, a neutral one, or an evil one.
On processing, there is a “game” that is used to “release” the ghost from its half-dead state. The game difficulty varies based on the type of ghost. The game also utilizes the second system on the Arduino, the “control system”. This is composed of a knob (potentiometer) and a button.
The Game
The Game layouts look like this
Good ghost:
Neutral ghost:
Evil ghost:
The disk in the middle is a “ghostnet”. To control the ghost net, the players use the “control system” on the Arduino. The knob controls direction, and the button controls move or stop. There are 20 spectres every time a ghost is caught, and the number of spectres required to be caught depends on the “evilness” of the ghost. A certain amount of time is given to the players to finish this task, and a red countdown is displayed on the screen. A count for spectres left to catch is also displayed on the screen in white.
Success Screen (Jacob’s your boss):
Fail Screen:
Arduino Layout
Pitfalls and Future Improvements
One of the main troubles that I ran into was the control system for the game. The knob and the buttons were a bit hard to use for the game. Although I originally intended for the controls in the game to be harder, I had to lower the difficulty holistically to account for the difficult control system.
Another difficulty and potential improvement was the mobility of the system. To use the system, the user has to bring a laptop, while taking care of the wire between the Arduino and the computer. For future purposes, the connection between the platforms could be made wireless.
Game Demonstration
Detection Demonstration
Full Code
Arduino
#include "pitches.h" int piezo = 2; bool piezoState = false; int trig = 3; int echo = 4; int button = 5; int buttonState = 0; int prevButton = 0; long timer; int timerlength = 100; int degree; //ghost types bool good = false; bool neutral = false; bool evil = false; int ghostType = 0; int ghost = 0; void setup() { Serial.begin (9600); Serial.write(0); timer = millis() + timerlength; pinMode(trig, OUTPUT); pinMode(echo, INPUT); pinMode(button, INPUT); } void loop() { int knobValue = analogRead(A1); buttonState = digitalRead(button); Piezo(); if (good == false && neutral == false && evil == false){ Evaluate(); } else{ noTone(piezo); knobValue /= 4; degree = map(knobValue, 0, 255, 0, 12); Serial.print(degree); Serial.print(','); Serial.println(buttonState); } } void Evaluate() { // For Distance int time; int distance; digitalWrite(trig, HIGH); delay(0.01); digitalWrite(trig, LOW); time = pulseIn(echo, HIGH); // speed of sound = 343 m/s = 0.0343 cm/µs // distance = time * speed distance = time * 0.0343 / 2; // Serial.print(distance); // Serial.println(" cm"); // For brightness int brightness = analogRead(A0); //overall evaluation if (millis()%10 == 1 && (Serial.available()>0)){ // Serial.println("here"); if (brightness <= 40 && brightness > 30 && distance <= 30 && distance > 20){ ghost = random(1, 101); if (ghost < 2){ good = true; Serial.write(ghost); } } if (brightness <= 20 && brightness > 10 && distance <= 20 && distance >10){ ghost = random(1,101); if (ghost = 1){ good = true; Serial.write(ghost); } if (ghost = 2){ neutral = true; Serial.write(ghost); } } if (brightness <= 10 && distance <= 10){ ghost = random(1,101); if (ghost == 1){ good = true; Serial.write(ghost); } if (ghost == 2){ neutral = true; Serial.write(ghost); } if (ghost == 3){ evil = true; Serial.write(ghost); } } } } void Piezo() { if (good == false && neutral == false && evil == false){ if (millis() > timer){ tone(piezo, C5); timer = millis() + timerlength; } else if (millis() == timer){ noTone(piezo); } } }
Processing
Main code
import processing.serial.*; Serial myPort; import processing.sound.*; SoundFile die; SoundFile music2; PFont font; //images PImage bg; PImage bg1; PImage bg2; PImage bg3; PImage good; PImage neutral; PImage evil; PImage net; //values: int ghost = 0; boolean go = false; int ndegree = 0; int fire = 0; boolean win = false; //objects: Net n1; Spectre[] spectre; //initializing all the spectre int count = 20; int livecount; void setup(){ size(1200,800); printArray(Serial.list()); String portname=Serial.list()[3]; println(portname); myPort = new Serial(this,portname,9600); bg = loadImage("bg.jpeg"); bg1 = loadImage("bg1.jpg"); bg2 = loadImage("bg2.jpg"); bg3 = loadImage("bg3.jpg"); good = loadImage("good.png"); neutral = loadImage("neutral.png"); evil = loadImage("evil.png"); net = loadImage("net.png"); die = new SoundFile(this, "die.mp3"); music2 = new SoundFile(this, "music2.mp3"); //initializing objects n1 = new Net(width/2, height/2); spectre = new Spectre[count]; int index = 0; for (int x = 0; x < count; x++) { spectre[index++] = new Spectre(random(0, width), random(0, height), 40, 40); } for (int i =0; i<count;i++){ //spectre speed initializing spectre[i].xSpeed = random(-2,2); spectre[i].ySpeed = random(-2,2); } music2.loop(); } void draw(){ background(0); //detection phase if (ghost == 0){ font = createFont("BreatheFire-65pg.ttf", 60); textFont(font); textAlign(CENTER); text("detecting . . .", width/2, 350); } //detected if (ghost != 0){ menu(); } if (go == true){ game(); checkwin(); } } void game(){ if (ghost == 1){ background(bg1); } if (ghost == 2){ background(bg2); } if (ghost == 3){ background(bg3); } n1.display(); n1.move(); n1.checkEdges(); for (Spectre spectre : spectre) { spectre.display(); spectre.collision(); spectre.update(); spectre.checkEdges(); } font = createFont("BreatheFire-65pg.ttf", 60); textFont(font); textAlign(CENTER); text(livecount, width/2, 185); } void menu(){ if (go == false){ background(bg); fill(255); font = createFont("BreatheFire-65pg.ttf", 60); textFont(font); textAlign(CENTER); if (ghost == 1){ text("A kind ghost is in your vicinity. \n set it free", width/2, 185); count = 10; } if (ghost == 2){ text("A neutral ghost is in your vicinity. \n proceed with caution", width/2, 185); count = 15; } if (ghost == 3){ text("An evil ghost is in your vicinity. \n succeed or die", width/2, 185); count = 20; } livecount = count; font = createFont("Georgia", 40); textFont(font, 30); text("Use the spiritbox knob to control ghostnet direction \n Use spiritbox button to move ghostnet \n Catch all spectres for mission success", width /2, 550); stroke(0); fill(0); rectMode(CENTER); rect(width/2, 680, 200, 72); fill(255); textFont(font, 60); font = createFont("BreatheFire-65pg.ttf", 42); textFont(font); textAlign(CENTER); text("PLAY", width/2, 700); if (mouseX>=(width/2 - 100) & mouseX<=(width/2 + 100) & mouseY>=650 & mouseY<=720){ stroke(255); noFill(); rect(width/2, 680, 200, 72); if (mousePressed){ go = true; music.stop(); } } } } void checkwin(){ if (livecount < 1){ win = true; background(bg); fill(255); font = createFont("BreatheFire-65pg.ttf", 60); textFont(font); textAlign(CENTER); text("mission success \n report back to Jacob", width/2, 185); myPort.write(int(win)); } } void serialEvent(Serial myPort){ if (ghost == 0){ ghost = myPort.read(); } else if (ghost != 0){ String s=myPort.readStringUntil('\n'); s=trim(s); if (s!=null){ int values[]=int(split(s,',')); if (values.length==2){ //println(values); ndegree = values[0]; fire = values[1]; } } } myPort.write(0); }
Ghost Net Class
class Net { float x; float y; float diameter; int score; float xSpeed; float ySpeed; Net(float tempX, float tempY) { x = tempX; y = tempY; diameter = 45; score = 0; } void display() { imageMode(CENTER); image(net,x,y,diameter,diameter); } void move() { xSpeed = -(cos(ndegree) * 3); ySpeed = - (sin(ndegree) * 3); if (fire == 1){ x += xSpeed; y += ySpeed; } } void checkEdges() { if (y>height) { ySpeed = -ySpeed; } if (y<0) { ySpeed = -ySpeed; } if (x>width) { xSpeed = -xSpeed; } if (x<0) { xSpeed = -xSpeed; } } }
Spectre Class
class Spectre { float x; float y; float w; float h;; float xSpeed, ySpeed; boolean caught = false; Spectre(float tempX, float tempY, float tempW, float tempH) { x = tempX; y = tempY; w = tempW; h = tempH; xSpeed = ySpeed = 0; } void display() { if (caught == false){ if (ghost == 1){ image(good, x, y, w, h); } if (ghost == 2){ image(neutral, x, y, w, h); } if (ghost == 3){ image(evil, x, y, w, h); } } } //collision function for when players catch the right and wrong spectre void collision() { if ((n1.x - n1.diameter < x && x < n1.x + n1.diameter) & (n1.y - n1.diameter < y && y < n1.y + n1.diameter)){ x = -50; y = -50; caught = true; livecount --; die.play(); } } void update(){ x += xSpeed; y += ySpeed; } void checkEdges() { if (y>height) { ySpeed = -ySpeed; } if (y<0) { ySpeed = -ySpeed; } if (x>width) { xSpeed = -xSpeed; } if (x<0) { xSpeed = -xSpeed; } } }