Summary
Meltdown is a reaction game built on the Adafruit Circuit Playground Express where you are trying to keep a nuclear reactor from melting down. When you flip the switch on, a red NeoPixel appears somewhere on the ring and that is your target. Use the potentiometer to slide a green pixel around the ring and line it up exactly with the red one. When you have got them matched, hit the breadboard button to confirm and score the point. Nail it and all the pixels flash green before a new target spawns somewhere random. Hit ten successful alignments and you win. The catch is that orange warning pixels start filling the ring the moment a new target appears, and if they fill up before you confirm your alignment the reactor overloads. When that happens everything flashes red and you have to physically shake the board to vent the reactor and get back in the game. It is a simple concept but the timer keeps the pressure on, and the shaking mechanic makes recoveries feel pretty satisfying. Flip the switch off at any point to reset and start over.
Image
Video Example
Code
#include <Adafruit_CircuitPlayground.h>
//Definitions
#define POT_PIN A2
#define BUTTON_PIN 10
//Game Settings
#define WIN_SCORE 10
#define OVERLOAD_TIME 5000
#define SHAKE_THRESHOLD 12.0
//Game States
enum GameState { PLAYING, OVERLOAD, WIN };
GameState gameState = PLAYING;
//Game Variables
int score = 0;
int targetPixel = 0;
int greenPixel = 0;
unsigned long roundStartTime = 0;
bool buttonWasPressed = false;
//SETUP
void setup() {
CircuitPlayground.begin();
CircuitPlayground.setBrightness(40);
pinMode(BUTTON_PIN, INPUT);
//Seed random number generator
randomSeed(analogRead(A0));
delay(1000);
startNewRound();
}
// LOOP
void loop() {
//Switch Off
if (!gameEnabled()) {
// Reset game so it starts fresh when switched back on
score = 0;
gameState = PLAYING;
CircuitPlayground.clearPixels();
showIdleState();
return;
}
//Switch On
static bool wasOff = true;
if (wasOff) {
startNewRound();
wasOff = false;
}
if (gameState == PLAYING) {
//Read Potentiometer and Map to Pixel Position
int potValue = analogRead(POT_PIN);
greenPixel = map(potValue, 0, 1023, 0, 9);
//Check Overload Timer
if (millis() - roundStartTime > OVERLOAD_TIME) {
gameState = OVERLOAD;
return;
}
//Reactor Display
showPlayingState();
//Read Button for Alignment Confirmation
bool buttonPressed = digitalRead(BUTTON_PIN);
//Only trigger on a fresh press, not a held button
if (buttonPressed && !buttonWasPressed) {
if (greenPixel == targetPixel) {
//SUCCESS
score++;
if (score >= WIN_SCORE) {
gameState = WIN;
} else {
flashSuccess();
startNewRound();
}
}
}
buttonWasPressed = buttonPressed;
}
//Overload State: flash red, wait for shake
else if (gameState == OVERLOAD) {
flashOverload();
if (isShaking()) {
//reset the round timer and resume play
roundStartTime = millis();
gameState = PLAYING;
delay(500);
}
}
//Win State
else if (gameState == WIN) {
flashWin();
//After winning, flipping switch off and back on restarts
wasOff = true;
}
//Track switch state
if (!gameEnabled()) wasOff = true;
}
//GAME ENABLED
//Reads the D7 slide switch. Returns true if game should run.
bool gameEnabled() {
return CircuitPlayground.slideSwitch();
}
//START NEW ROUND
//Assigns a new random target pixel and resets the timer
void startNewRound() {
targetPixel = random(0, 10);
roundStartTime = millis();
buttonWasPressed = false;
}
//PLAYING STATE
//Displays the red target pixel and green player pixel.
//Yellow/orange pixels fill in over time as a warning the overload
void showPlayingState() {
CircuitPlayground.clearPixels();
unsigned long elapsed = millis() - roundStartTime;
int warningPixels = map(elapsed, 0, OVERLOAD_TIME, 0, 8);
int filled = 0;
for (int p = 0; p < 10 && filled < warningPixels; p++) {
if (p != targetPixel && p != greenPixel) {
CircuitPlayground.setPixelColor(p, 80, 30, 0);
filled++;
}
}
if (greenPixel == targetPixel) {
CircuitPlayground.setPixelColor(targetPixel, 255, 200, 0);
} else {
CircuitPlayground.setPixelColor(targetPixel, 255, 0, 0);
CircuitPlayground.setPixelColor(greenPixel, 0, 255, 0);
}
}
//IDLE STATE
//Displayed when the switch is off
void showIdleState() {
for (int p = 0; p < 10; p++) {
CircuitPlayground.setPixelColor(p, 0, 0, 20);
}
delay(500);
CircuitPlayground.clearPixels();
delay(500);
}
//FLASH SUCCESS
//All pixels flash green 3 times on a successful alignment
void flashSuccess() {
for (int i = 0; i < 3; i++) {
for (int p = 0; p < 10; p++) {
CircuitPlayground.setPixelColor(p, 0, 255, 0);
}
delay(200);
CircuitPlayground.clearPixels();
delay(200);
}
}
//FLASH OVERLOAD
//All pixels flash red while in the overload state.
void flashOverload() {
for (int p = 0; p < 10; p++) {
CircuitPlayground.setPixelColor(p, 255, 0, 0);
}
delay(150);
CircuitPlayground.clearPixels();
delay(150);
}
//FLASH WIN
//All pixels flash green when the player wins
void flashWin() {
for (int p = 0; p < 10; p++) {
CircuitPlayground.setPixelColor(p, 0, 255, 0);
}
delay(400);
CircuitPlayground.clearPixels();
delay(400);
}
//SHAKE DETECTION
//Reads all three accelerometers and checks if shaken
bool isShaking() {
float x = CircuitPlayground.motionX();
float y = CircuitPlayground.motionY();
float z = CircuitPlayground.motionZ();
float shake = sqrt(x * x + y * y + z * z);
return shake > SHAKE_THRESHOLD;
}


No comments:
Post a Comment
Note: Only a member of this blog may post a comment.