This is a project I have been thinking about for a long time. I love space and I want to make it more accessible to people in some way. I also love interactive experiences, and games.
I want to make a game where players (as pilots) follow directions on a screen to launch a spaceship and navigate through space by pressing buttons, turning knobs, and sliding pieces around on a control board.
My original plan was to use an LCD screen to display text instructions for the player, and use a servo to transport a small 3D printed spaceship across the board as the player completes different challenges, like a progress bar. I did not end up using these components because LCD screens use too many pins, so I opted for an actual computer screen interface.
Since this is a project that I have been thinking about for a long time, I have some initial sketches from months before I knew I was going actually implement the design, and before I really had an idea about how I would format the game and the interface. As I thought more about the project, my ideas became more concrete.
Alternate board shapes
Some of my inspiration came from images I found online for sci-fi control panels. I have also created two projects that have similar components to this one, and I combined what I learned from those projects to make this project.
A light game that uses arduino
A light box which uses analog circuits to make a simple interactive experience. It is very stimulating for people who like pressing buttons and seeing things light up.
The image I used as the background was found with a google images search for sci fi screen texture, and the font I used is called neuropol-x.
For playtesting, I had the basic board layout combined with simple instructions and a few buttons. Players seemed confused about the placement of the buttons on the breadboard, but overall the setup of the controls on the board made sense and players were eager to use the different components.
I laser cut all the pieces on an Epilog laser cutter, since I am very comfortable with the machine and how to design for 2D fabrication.
I then painted all the pieces with spray paint, and glued parts together
The wiring involved cutting apart and soldering connections between 19 neopixels. I wired all the positive wires together, and all ground wires together, and plugged each bundle into their respective pins on a breadboard connected to the Arduino microcontroller. All digital and analog input wires (each component had a separate one) into their respective pins on the breadboard.
The enclosure is made of 4 pieces: 2 rectangles and 2 right triangles. The enclosure is placed over the wires, covering all the electronic components that should not be seen by players. All pieces are hot glued together and painted silver to look like metal. All lights have plexiglass in front of them, which has been sanded and covered with a single ply of toilet paper to diffuse the light. The vent has black fabric behind it. The vent presses up against a button when it is open. A wire to connect to the computer's port runs out of the left side of the enclosure.
The game is in two parts: the computer screen and the control board. Instead of a traditional controller for a screen-based video game, this game is mainly played with a physical control board, with one large button, 4 medium sized buttons, 4 small buttons, 3 knobs (potentiometers), indicator lights for every interaction (19 connected LEDs on a neopixel strip), and a joystick. There is also a button-activated interaction that involves a player sliding a small door open and closed, as well as a photoresistor (light sensor) that simulates a fingerprint sensor.
These components are all placed within a board (with a shape reminiscent of a traditional game controller) that sits tilted towards the player. All the electronics save for the one wire that connects to the computer screen are hidden within the base enclosure of the board. The electronic components are all controlled by an Arduino Nano Every.
The computer screen interface is run off of P5.js, a Javascript library for coding. The Arduino sends data to P5.js, which prints different text to the screen depending on the data. The screen shows a futuristic interface, with text instructions about how to play the game.
When turned on, the board has no lights on, but the screen instructs the player to authenticate their identity with a fingerprint scan. The player then has to place their finger over a small piece of plexiglass, which darkens the environment around the photoresistor and activates the start of the game (basically acting as a button). The screen welcomes the player and introduces the game, saying that there is an artificial intelligence that will be guiding the player through the controls. When the player then presses the launch button, the game starts. The screen prints which controls should be used, and the player has a short amount of time to use the control properly. An indicator light flashes next to the control to show that it should be used. If the player fails to use the control in the specified amount of time, the indicator light turns red. The player can fail three times before losing the game. When the player uses a control correctly within the time frame, the indicator light turns green. The player must succeed 20 times in order to win the game. The indicator lights all turn white when the player wins. If the player wins or loses, they can restart the game by pressing the land button.
The controls that a player must manipulate during the game include 4 colored buttons (Red, Green, Blue, Yellow), 3 black buttons (labeled X, Y, Z, and Roll, Pan, and Tilt, respectively), a joystick (with options to move the stick up, down, left, or right), 3 knobs (labeled speed, air circulation, and rotate ship), and a door that reveals a vent when opened. For the colored buttons, the screen will instruct the player to press the Red, Green, Blue, or Yellow button. For the vent, the screen will say that the ship is overheating or too cold, depending on if the vent is currently open or closed, and instruct the player to open or close the vent. For the speed and air circulation knobs, the screen instructs the player to change the speed or air circulation, and the player must turn the knob until the indicator light turns green. For the rotate knob and black buttons, the player must hold down the specified button (the screen says to rotate the ship about the X, Y, or Z axis) while turning the rotate knob until the indicator light next to the button turns green.
Each time the player fails or succeeds at using a control, the game randomly picks the next control to be used. All controls are equally likely to be used.
This game is meant to bring the joy of space to more people, and be a fun, reflex-based game that challenges people and makes them think quickly. This is also a proof of concept for future projects that I want to do that involve similar controls and are much bigger and more complex.
As the game is played, the screen prints text at the same time as the indicator lights flash. An experienced player can ignore the screen and just use the lights to see which control should be used. Having both techniques for feedback to the player makes the game more accessible for people who might not be able to read or see the screen as well, or might be over-stimulated by the lights.
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
//define the digital pin that the LED strip is connected to
#define PIN 12
//#include <LiquidCrystal.h> //the liquid crystal library contains commands for printing to the display
//LiquidCrystal lcd(8, 7, 6, 5, 3, 4); // tell the RedBoard what pins are connected to the display
//the first parameter is the number of LEDs you have - change for your strip
Adafruit_NeoPixel strip = Adafruit_NeoPixel(19, PIN, NEO_GRB + NEO_KHZ800);
//potentiometers
const int airPin = A1;
int airVal = 0;
const int rotatePin = A2;
int rotateVal = 0;
const int speedPin = A3;
int speedVal = 0;
//Joystick
//const int JbuttonPin = 2;
//int JbuttonVal = 0;
const int XPin = A4;
int XVal = 0;
const int yJPin = A5;
int yJVal = 0;
//light sensors
const int ventPin = 3;
int ventVal = 0;
const int fingerPin = A0;
int fingerVal = 0;
//Buttons:
const int launchPin = 11;
int launchVal = 0;
const int landPin = 13;
int landVal = 0;
const int RPin = 10;
int RVal = 0;
const int GPin = 9;
int GVal = 0;
const int BPin = 8;
int BVal = 0;
const int YPin = 7;
int YVal = 0;
const int rollPin = 6;
int rollVal = 0;
const int panPin = 5;
int panVal = 0;
const int tiltPin = 4;
int tiltVal = 0;
//const int launchLight = 1;
//LED
const int launchLight = 2;
//buzzer
//const int buzzerPin = 3;
//Variables:
int activeTask = 0;
long timeSinceStart = 0;
long timekeeper = 0;
int fingerThreshold = 100;
bool dead = false;
bool done = false;
int info = 0;
int failures = 0;
int actions = -1;
int track = 0;
long Time = -1;
bool onLaunch = false;
int airRand = -1;
int airHold = -1;
int rotateRand = -1;
int panHold = -1;
int tiltHold = -1;
int rollHold = -1;
int speedRand = -1;
int speedHold = -1;
int numActions = 15;
//int numActions = 14;
int startNum = 1;
//int startNum = 13;
int difficulty = 20; //changed from 15
void setup() {
// put your setup code here, to run once:
pinMode(launchPin, INPUT);
pinMode(landPin, INPUT);
pinMode(RPin, INPUT);
pinMode(GPin, INPUT);
pinMode(BPin, INPUT);
pinMode(YPin, INPUT);
pinMode(rollPin, INPUT);
pinMode(panPin, INPUT);
pinMode(tiltPin, INPUT);
pinMode(fingerPin, INPUT);
pinMode(ventPin, INPUT);
pinMode(launchLight, OUTPUT);
Serial.begin(9600);
strip.begin(); //we always need to do this
strip.show(); // Initialize all pixels to 'off'
randomSeed(A7);
}
void loop() {
// put your main code here, to run repeatedly:
//read all sensor values and send to serial
//index in P5 Array:
airVal = analogRead(airPin); Serial.print(airVal); Serial.print(","); //0
rotateVal = analogRead(rotatePin); Serial.print(rotateVal); Serial.print(","); //1
speedVal = analogRead(speedPin); Serial.print(speedVal); Serial.print(","); //2
ventVal = digitalRead(ventPin); Serial.print(ventVal); Serial.print(","); //3
fingerVal = analogRead(fingerPin); Serial.print(fingerVal); Serial.print(","); //4
launchVal = digitalRead(launchPin); Serial.print(launchVal); Serial.print(","); //5
landVal = digitalRead(landPin); Serial.print(landVal); Serial.print(","); //6
RVal = digitalRead(RPin); Serial.print(RVal); Serial.print(","); //7
GVal = digitalRead(GPin); Serial.print(GVal); Serial.print(","); //8
BVal = digitalRead(BPin); Serial.print(BVal); Serial.print(","); //9
YVal = digitalRead(YPin); Serial.print(YVal); Serial.print(","); //10
rollVal = digitalRead(rollPin); Serial.print(rollVal); Serial.print(","); //11
tiltVal = digitalRead(tiltPin); Serial.print(tiltVal); Serial.print(","); //12
panVal = digitalRead(panPin); Serial.print(panVal); Serial.print(","); //13
XVal = analogRead(XPin); Serial.print(XVal); Serial.print(","); //14
yJVal = analogRead(yJPin); Serial.print(yJVal); Serial.print(","); //15
Serial.print(activeTask); Serial.print(","); //16
Serial.println(actions); //17
if (failures == 3)
{
dead = true; actions = -2;
strip.setPixelColor(0, 0, 0, 0); //takes RGB vals 0-255
strip.setPixelColor(1, 0, 0, 0); //takes RGB vals 0-255
strip.setPixelColor(2, 0, 0, 0); //takes RGB vals 0-255
strip.setPixelColor(3, 0, 0, 0); //takes RGB vals 0-255
strip.setPixelColor(4, 0, 0, 0); //takes RGB vals 0-255
strip.show();
if (landVal == HIGH)
{
activeTask = 0; actions = -1; failures = 0; dead = false; track = 0;
for(uint16_t i=0; i<strip.numPixels(); i++)
{
strip.setPixelColor(i, 0, 0, 0); //takes RGB vals 0-255
}
strip.show();
digitalWrite(launchLight, LOW);
}
}
if (activeTask == 6)
{
if (landVal == HIGH)
{
activeTask = 0; actions = -1; failures = 0; dead = false; track = 0;
for(uint16_t i=0; i<strip.numPixels(); i++)
{
strip.setPixelColor(i, 0, 0, 0); //takes RGB vals 0-255
}
strip.show();
}
}
if (activeTask == 0 && dead == false) //game has not started yet!
//all things should be off
{ actions = -1;
//text on screen should say "please authenticate identity. waiting for finger print authentication"
if (fingerVal <= 590)
{
activeTask = 1;
//print to screen, "identity authenticated. Welcome, Captain!"
//turn lights on:
delay(2000);
strip.setPixelColor(0, 0, 150, 0); //set to off, takes RGB vals 0-255
strip.setPixelColor(14, 0, 150, 0); //set to off, takes RGB vals 0-255
strip.show();
onLaunch = true;
delay(5000);
}
//game has started
//print opening instructions (story and game goal)
}
else if (activeTask == 1 && dead == false)
{
//flash launch button
if (actions == -1)
{
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 750)//check if it's been on long enough
{
onLaunch = true;
digitalWrite(launchLight, HIGH);
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 1500)//check if it's been off long enough
{
digitalWrite(launchLight, LOW);
onLaunch = false;
Time = millis(); //reset time counter
}
}
}
//wait for launch button to be pressed
if (launchVal == HIGH) //launch button has been pressed
{
if (actions == -1)
{
actions = random(1, numActions); Time = -1; //actions = 0;
strip.setPixelColor(2, 200, 100, 0); //takes RGB vals 0-255
strip.show();
delay(2000);
}
}
//the next few sequences will need to be done in quick timing. Use the controls when I tell you to.
//Ready? Go!
//RED
if (actions == 15)
{
//print press RED button
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 500)//check if it's been on long enough
{
strip.setPixelColor(7, 100, 100, 0); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 500)//check if it's been off long enough
{
strip.setPixelColor(7, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
}
//wait for red to be pressed
if (RVal == HIGH && (millis() - timekeeper <= 3000)) //pressed within 3 seconds
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions); //move to next button press
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(7, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 3000) //not pressed within 3 seconds
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions); //random(min, max)
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(7, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//GREEN
if (actions == 1)
{
//print press Green button
//inicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(8, 0, 0, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(8, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
}
if (GVal == HIGH && (millis() - timekeeper <= 2000)) //pressed within 3 seconds
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions); //move to next button press
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(8, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 2000) //not pressed within 3 seconds
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions); //random(min, max)
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(8, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//BLUE
if (actions == 2)
{
//print press Blue button
//inicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(6, 0, 0, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(6, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
}
if (BVal == HIGH && (millis() - timekeeper <= 2000))
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
strip.setPixelColor(6, 0, 100, 0); //takes RGB vals 0-255
track++; if (track == difficulty) { done = true; }
strip.show();
}
else if (millis() - timekeeper > 2000)
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(6, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//YELLOW
if (actions == 3)
{
//print press Yellow button
//inicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(9, 0, 0, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(9, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
}
//wait for red to be pressed
if (YVal == HIGH && (millis() - timekeeper <= 2000))
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(9, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 2000)
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(9, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//UP
if (actions == 4)
{
//print up!
//indicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(11, 0, 0, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(11, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
}
if (yJVal >= 750 && (millis() - timekeeper <= 2000))
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(11, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 2000)
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(11, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//DOWN
if (actions == 5)
{
//print down!
//indicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(13, 0, 0, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(13, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
}
if (yJVal <= 250 && (millis() - timekeeper <= 2000))
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(13, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 2000)
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(13, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//LEFT
if (actions == 6)
{
//print left
//inicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(10, 0, 0, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(10, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
}
if (XVal <= 250 && (millis() - timekeeper <= 2000)) //pressed within 3 seconds
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(10, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 2000)
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(10, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//RIGHT
if (actions == 7)
{
//print right
//indicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(12, 0, 0, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(12, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
}
//wait for red to be pressed
if (XVal >= 750 && (millis() - timekeeper <= 2000))
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(12, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 2000)
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++;
if (track == difficulty) { done = true; }
strip.setPixelColor(12, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//OPEN VENT
if (actions == 8 && ventVal == HIGH)
{
//print open vent
//indicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 600)//check if it's been on long enough
{
strip.setPixelColor(1, 100, 0, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 600)//check if it's been off long enough
{
strip.setPixelColor(1, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
}
if (ventVal == HIGH && (millis() - timekeeper <= 5000)) //pressed within 5 seconds
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(1, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 5000) //not pressed within 3 seconds
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++;
if (track == difficulty) { done = true; }
strip.setPixelColor(1, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
else if (actions == 8 && ventVal == LOW)
{
timekeeper = 0; Time = -1;
actions = random(startNum,numActions);
}
//CLOSE VENT
if (actions == 9 && ventVal == LOW)
{
//print close vent
//indicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 600)//check if it's been on long enough
{
strip.setPixelColor(1, 100, 0, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 600)//check if it's been off long enough
{
strip.setPixelColor(1, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
}
//wait for red to be pressed
if (ventVal == LOW && (millis() - timekeeper <= 5000))
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(1, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 5000)
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++;
if (track == difficulty) { done = true; }
strip.setPixelColor(1, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
else if (actions == 9 && ventVal != LOW)
{
timekeeper = 0; Time = -1;
actions = random(startNum,numActions);
}
//PAN
if (actions == 10)
{
//print rotate ship, pan to the left X degrees
//indicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(4, 100, 100, 100); //takes RGB vals 0-255
strip.setPixelColor(16, 0, 0, 200); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(4, 0, 0, 0); //takes RGB vals 0-255
strip.setPixelColor(16, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
rotateRand = random(1,1022);
}
if ((rotateVal >= rotateRand - 30) && (rotateVal <= rotateRand + 30) && panVal == HIGH && (millis() - timekeeper <= 10000)) // within 10 seconds
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(4, 0, 100, 0); //takes RGB vals 0-255
strip.setPixelColor(16, 50, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 10000) //not pressed within 10 seconds
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++;
if (track == difficulty) { done = true; }
strip.setPixelColor(4, 255, 0, 0); //takes RGB vals 0-255
strip.setPixelColor(16, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//TILT
if (actions == 11)
{
//print rotate ship, TILT to the left X degrees
//indicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(5, 100, 100, 100); //takes RGB vals 0-255
strip.setPixelColor(16, 0, 0, 200); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(5, 0, 0, 0); //takes RGB vals 0-255
strip.setPixelColor(16, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
rotateRand = random(1,1022);
}
if ((rotateVal >= rotateRand - 30) && (rotateVal <= rotateRand + 30) && tiltVal == HIGH && (millis() - timekeeper <= 10000))
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(5, 0, 100, 0); //takes RGB vals 0-255
strip.setPixelColor(16, 50, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 10000)
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++;
if (track == difficulty) { done = true; }
strip.setPixelColor(5, 255, 0, 0); //takes RGB vals 0-255
strip.setPixelColor(16, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//ROLL
if (actions == 12)
{
//print rotate ship, roll to the left X degrees
//indicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(3, 100, 100, 100); //takes RGB vals 0-255
strip.setPixelColor(16, 0, 0, 200); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(3, 0, 0, 0); //takes RGB vals 0-255
strip.setPixelColor(16, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
rotateRand = random(1,1022);
}
if ((rotateVal >= rotateRand - 30) && (rotateVal <= rotateRand + 30) && rollVal == HIGH && (millis() - timekeeper <= 10000)) //pressed within 10 seconds
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(3, 0, 100, 0); //takes RGB vals 0-255
strip.setPixelColor(16, 50, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 10000) //not pressed within 10 seconds
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++;
if (track == difficulty) { done = true; }
strip.setPixelColor(3, 255, 0, 0); //takes RGB vals 0-255
strip.setPixelColor(16, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//SPEED
if (actions == 13)
{
//print change speed
//inicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(17, 100, 100, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(17, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
speedRand = random(1,1022);
}
//Serial.println(speedRand);
//wait for red to be pressed
if ((speedVal >= speedRand - 30) && (speedVal <= speedRand + 30) && (millis() - timekeeper <= 7500)) //pressed within 7.5 seconds
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions); //random(min, max) //actions = 0; //move to next button press
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(17, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 7500)
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++;
if (track == difficulty) { done = true; }
strip.setPixelColor(17, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
//AIR
if (actions == 14)
{
//print increase/decrease air flow
//indicator:
if (onLaunch == false)//LED is on
{
if ( Time == -1 || (millis() - Time) >= 400)//check if it's been on long enough
{
strip.setPixelColor(15, 100, 100, 100); //takes RGB vals 0-255
strip.show(); onLaunch = true;
Time = millis(); //reset time counter
}
}
else //LED is off
{
if ( (millis() - Time) >= 400)//check if it's been off long enough
{
strip.setPixelColor(15, 0, 0, 0); //takes RGB vals 0-255
strip.show(); onLaunch = false;
Time = millis(); //reset time counter
}
}
//take time
if (timekeeper == 0)
{
timekeeper = millis();
airRand = random(1,1022);
}
//wait for red to be pressed
//Serial.println(airRand);
if ((airVal >= airRand - 30) && (airVal <= airRand + 30) && (millis() - timekeeper <= 7500))
{
timekeeper = 0; //reset time
Time = -1;
actions = random(startNum,numActions);
track++; if (track == difficulty) { done = true; }
strip.setPixelColor(15, 0, 100, 0); //takes RGB vals 0-255
strip.show();
}
else if (millis() - timekeeper > 7500)
{
//print message
failures++; Time = -1;
timekeeper = 0;
actions = random(startNum,numActions);
track++;
if (track == difficulty) { done = true; }
strip.setPixelColor(15, 255, 0, 0); //takes RGB vals 0-255
strip.show();
}
}
if (done) //task is done
{
activeTask = 6; actions = 0; done = false;
for(uint16_t i=0; i<strip.numPixels(); i++)
{
strip.setPixelColor(i, 200, 200, 200); //takes RGB vals 0-255
}
strip.show();
}
}// end activeTask 1, Launch
//this was meant to be a reward after finishing the game, in which players could jsut press buttons and make lights turn on for fun:
else if (activeTask == 6 && dead == false)
{
//GAME DONE, do final messages and go into fun mode:
if (RVal == HIGH)
{
//turn R light on
}
else
{
//turn R light off
}
if (GVal == HIGH)
{
//turn G light on
}
else
{
//turn G light off
}
if (BVal == HIGH)
{
//turn B light on
}
else
{
//turn B light off
}
if (YVal == HIGH)
{
//turn Y light on
}
else
{
//turn Y light off
}
//if (JButtonVal == HIGH)
// {
//turn all J lights on
//}
//for rotate and XYZ buttons:
if (panVal == HIGH)
{
//turn pan light on
//make random target value for rotate knob,
//if knob within threshold, turn light new color
}
else
{
//turn pan light off
}
if (tiltVal == HIGH)
{
//turn tilt light on
//make random target value for rotate knob,
//if knob within threshold, turn light new color
}
else
{
//turn pan light off
}
if (rollVal == HIGH)
{
//turn roll light on
//make random target value for rotate knob,
//if knob within threshold, turn light new color
}
else
{
//turn roll light off
}
if (landVal == HIGH)
{
done = true;
//turn land light on
}
}//end Fun Mode
else
{
//error
}
}
let serial;
let latestData = "waiting for data";
let finger = 0;
let launch = 0;
let X = false;
let Y = 0;
let R = 0;
let G = 0;
let activeTask = 0;
let actions = 0;
let airRand = -1;
let speedRand = -1;
let panRand = -1;
let img;
let font
function preload(){
img = loadImage('screen1.jpg')
font = loadFont('neuropol.otf');
}
function setup() {
createCanvas(windowWidth, windowHeight);
serial = new p5.SerialPort();
serial.list();
serial.open('COM5');
serial.on('connected', serverConnected);
serial.on('list', gotList);
serial.on('data', gotData);
serial.on('error', gotError);
serial.on('open', gotOpen);
serial.on('close', gotClose);
textFont(font);
textSize(15);
textAlign(LEFT, CENTER);
}
function serverConnected() {
print("Connected to Server");
}
function gotList(thelist) {
print("List of Serial Ports:");
for (let i = 0; i < thelist.length; i++) {
print(i + " " + thelist[i]);
}
}
function gotOpen() {
print("Serial Port is Open");
}
function gotClose(){
print("Serial Port is Closed");
latestData = "Serial Port is Closed";
}
function gotError(theerror) {
print(theerror);
}
function gotData() {
let currentString = serial.readLine();
if (!currentString) return;
latestData = currentString;
let Array = split(currentString, ",");
finger = int(Array[4]);
launch = int(Array[5]);
R = int(Array[7]);
G = int(Array[8]);
activeTask = int(Array[16]);
actions = int(Array[17]);
}
function draw() {
image(img, 0, -75, 1150,700);
if (finger > 600 && X == false && actions == -1 && activeTask == 0)
{
fill('gray');
text('Please Authenticate Identity', 400, 120);
text('Fingerprint Verification Needed', 400, 180);
}
if ((finger < 590 && finger > 1) || (X == true && actions == -1))
{
Y++;
fill('gray');
text('Identity Authenticated.', 400, 120);
if (Y >= 100 && Y < 300)
{
fill('gray');
text('Welcome, Captain!', 400, 180);
}
if (Y >= 300 && Y < 900)
{
image(img, 0, -75, 1150,700); //cover text
fill('gray')
text('I am your autopilot, V138.', 350, 90);
text('I will give you instructions from here,', 350, 160);
text('and indicate which controls should be used', 350, 185);
text ('by flashing an indicator light.', 350, 210);
text('Your Mission:', 350, 245);
text('Launch the ship and navigate', 350, 280);
text('through space to planet 1R78', 350, 315);
}
if (Y >= 900)
{
image(img, 0, -75, 1150,700); //cover text
text('Take a moment to look over the controls.', 350, 120);
text('You will need quick reflexes to succeed.', 350, 155);
text('A few mistakes are acceptable,', 350, 190);
text('but too many will destroy the ship.', 350, 225);
text('When you feel ready,', 350, 260);
text('press the LAUNCH button.', 350, 295);
}
X = true;
}
//
if(launch == 1) { Y = 0; actions = 0; }
if (actions == 0 && activeTask == 1 && Y <= 50) //launch button pressed
{
image(img, 0, -75, 1150,700); //cover text
text('LAUNCH SEQUENCE ACTIVATED', 360, 120);
text('Press the buttons quickly!', 360, 150);
Y++;
}
if (activeTask == 1)
{
if (actions == 15 )
{
image(img, 0, -75, 1150,700); //cover text
text('RED!', 350, 120);
}
if (actions == 1 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('GREEN!', 350, 120);
}
if (actions == 2 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('BLUE!', 350, 120);
}
if (actions == 3 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('YELLOW!', 350, 120);
}
if (actions == 4 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('UP!', 350, 120);
}
if (actions == 5 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('DOWN!', 350, 120);
}
if (actions == 6 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('LEFT!', 350, 120);
}
if (actions == 7 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('RIGHT!', 350, 120);
}
if (actions == 9 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('Overheating! OPEN THE VENT!', 350, 120);
}
if (actions == 8 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('The ship is too cold! CLOSE THE VENT!', 350, 120);
}
if (actions == 10 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('Rotate the ship on the Y axis!', 350, 120);
text('Press the PAN button while using ROTATE', 350, 180)
}
if (actions == 11 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('Rotate the ship on the Z axis!', 350, 120);
text('Press the TILT button while using ROTATE', 350, 180)
}
if (actions == 12 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('Rotate the ship on the X axis!', 350, 120);
text('Press the ROLL button while using ROTATE', 350, 180)
}
if (actions == 13 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('Change the SPEED!', 350, 120);
}
if (actions == 14 && activeTask == 1)
{
image(img, 0, -75, 1150,700); //cover text
text('Change the AIR CIRCULATION!', 350, 120);
}
}
if (actions == -2)
{
image(img, 0, -75, 1150,700); //cover text
text('Watch out!', 360, 100);
text('The ship is taking too much damage.', 360, 140);
text('LAUNCH FAILED', 360, 200);
text('Press the LAND button to start again.', 360, 270);
X = false;
}
if (actions == 0 && activeTask == 6)
{
image(img, 0, -75, 1150,700); //cover text
text('We made it through the atmosphere!', 360, 100);
text('The ship is on its way to planet 1R78.', 360, 140);
text('Press the LAND button to start again.', 360, 270)
X= false;
}
} //end DRAW