For our midterm, we just wanted to make something really simple but fun and to really focus on the craftsmanship. And I can say that we achieved that goal! Our idea as described in the previous blog post is a simple two-player reaction-based game where the goal is to the hit the indicated button as fast as possible. The idea came to us after seeing this game by Funzo Cheng and we wanted to take the countdown and reactive component and make it a competition.
We made sure to keep the concept straightforward and not over-complicate things from the get-go. We knew we wanted the three countdown LEDs in the center of the gameboard. The countdown serves as a crucial element to the game’s mechanic in that it builds suspense and anticipation. Without the countdown, the players wouldn’t have a visual indicator that prepares them for the smackdown and thus, removes that excitement of waiting for the light to turn on. This was a quick mock-up we did to test out the layout of the buttons:
One of the coding challenges was figuring out how to make the Arduino recognize that a round has been played. Eventually we realized we needed different states and a game counter to keep track of the rounds. The portion for the servo motor was actually the easiest part of the entire code. Even a simple set of interactions that looks easy to comprehend on the surface takes a much more complex of code to accomplish.
#include "Servo.h"
Servo servoMotor;
const int startButton = 2;
const int countDownLEDPin1 = 3;
const int countDownLEDPin2 = 4;
const int countDownLEDPin3 = 5;
const int player1RedLEDPin = 6;
const int player1YellowLEDPin = 7;
const int player1GreenLEDPin = 8;
const int player2RedLEDPin = 16;
const int player2YellowLEDPin = 17;
const int player2GreenLEDPin = 18;
const int player1RedButtonPin = 9;
const int player1YellowButtonPin = 10;
const int player1GreenButtonPin = 11;
const int player2RedButtonPin = 13;
const int player2YellowButtonPin = 14;
const int player2GreenButtonPin = 15;
const int servoMotorPin = 19;
int player1RandomPin;
int player2RandomPin;
// start button states
int currentStartButtonState;
int lastStartButtonState = LOW;
//player 1 button states
int currentPlayer1LEDButtonState;
int lastPlayer1LEDButtonState = LOW;
//player 2 button states
int currentPlayer2LEDButtonState;
int lastPlayer2LEDButtonState = LOW;
bool player1Reacted = false;
bool player2Reacted = false;
// player 1 reaction time
float player1LightOnTime;
float player1ReactionTime;
// player 2 reaction time
float player2LightOnTime = 0;
float player2ReactionTime = 0;
int gameCounter = 0;
void setup() {
Serial.begin(9600);
servoMotor.attach(servoMotorPin);
pinMode(startButton, INPUT_PULLDOWN);
// countdown LED pins
pinMode(countDownLEDPin1, OUTPUT);
pinMode(countDownLEDPin2, OUTPUT);
pinMode(countDownLEDPin3, OUTPUT);
// player 1 reaction LED
pinMode(player1RedLEDPin, OUTPUT);
pinMode(player1YellowLEDPin, OUTPUT);
pinMode(player1GreenLEDPin, OUTPUT);
// player 2 reaction LED
pinMode(player2RedLEDPin, OUTPUT);
pinMode(player2YellowLEDPin, OUTPUT);
pinMode(player2GreenLEDPin, OUTPUT);
// player 1 buttons
pinMode(player1RedButtonPin, INPUT_PULLDOWN);
pinMode(player1YellowButtonPin, INPUT_PULLDOWN);
pinMode(player1GreenButtonPin, INPUT_PULLDOWN);
// player 2 buttons
pinMode(player2RedButtonPin, INPUT_PULLDOWN);
pinMode(player2YellowButtonPin, INPUT_PULLDOWN);
pinMode(player2GreenButtonPin, INPUT_PULLDOWN);
}
// Reset everything
void reset() {
player1Reacted = false;
player2Reacted = false;
digitalWrite(countDownLEDPin1, LOW);
digitalWrite(countDownLEDPin2, LOW);
digitalWrite(countDownLEDPin3, LOW);
digitalWrite(player1RandomPin, LOW);
digitalWrite(player2RandomPin, LOW);
delay(500);
}
// Lightup LED for a game play
void lightUpLed() {
delay(500);
digitalWrite(countDownLEDPin1, HIGH);
delay(500);
digitalWrite(countDownLEDPin2, HIGH);
delay(500);
digitalWrite(countDownLEDPin3, HIGH);
delay(500);
player1RandomPin = random(player1RedLEDPin, player1GreenLEDPin+1);
player2RandomPin = player1RandomPin + 10;
digitalWrite(player1RandomPin, HIGH);
player1LightOnTime = millis();
digitalWrite(player2RandomPin, HIGH);
player2LightOnTime = millis();
}
// helper function for determine if a button is pressed
bool isPressed(int current, int prev) {
return current > prev;
}
// LED light flashing effect for the winner
void winnerLightEffect(int redLedPin, int yellowLedPin, int greenLEDPin) {
for (int i = 0; i < 5; i++) {
digitalWrite(redLedPin, HIGH);
delay(100);
digitalWrite(yellowLedPin, HIGH);
delay(100);
digitalWrite(greenLEDPin, HIGH);
delay(200);
digitalWrite(greenLEDPin, LOW);
delay(100);
digitalWrite(yellowLedPin, LOW);
delay(100);
digitalWrite(redLedPin, LOW);
delay(200);
}
for (int i = 0; i < 4; i++) {
digitalWrite(redLedPin, HIGH);
digitalWrite(yellowLedPin, HIGH);
digitalWrite(greenLEDPin, HIGH);
delay(400);
digitalWrite(redLedPin, LOW);
digitalWrite(yellowLedPin, LOW);
digitalWrite(greenLEDPin, LOW);
delay(400);
}
player1ReactionTime = 0;
player2ReactionTime = 0;
}
void loop() {
currentStartButtonState = digitalRead(startButton);
currentPlayer1LEDButtonState = digitalRead(player1RandomPin + 3);
currentPlayer2LEDButtonState = digitalRead(player2RandomPin - 3);
if (isPressed(currentStartButtonState, lastStartButtonState)) {
servoMotor.write(90);
lightUpLed();
} else if (isPressed(currentPlayer1LEDButtonState, lastPlayer1LEDButtonState) && gameCounter < 5){
player1ReactionTime += millis() - player1LightOnTime;
player1Reacted = true;
digitalWrite(player1RandomPin, LOW);
delay(500);
} else if (isPressed(currentPlayer2LEDButtonState, lastPlayer2LEDButtonState) && gameCounter < 5){
player2ReactionTime += millis() - player2LightOnTime;
player2Reacted = true;
digitalWrite(player2RandomPin, LOW);
delay(500);
} else if (player1Reacted && player2Reacted && gameCounter < 5){
gameCounter++;
if (gameCounter < 5) {
reset();
lightUpLed();
}
} else if (gameCounter == 5) {
if (player1ReactionTime > player2ReactionTime) {
servoMotor.write(180);
winnerLightEffect(player2RedLEDPin, player2YellowLEDPin, player2GreenLEDPin);
} else {
servoMotor.write(0);
winnerLightEffect(player1RedLEDPin, player1YellowLEDPin, player1GreenLEDPin);
}
reset();
gameCounter = 0;
}
lastStartButtonState = currentStartButtonState;
lastPlayer1LEDButtonState = currentPlayer1LEDButtonState;
lastPlayer2LEDButtonState = currentPlayer2LEDButtonState;
}
We started testing out the game with just LEDs and small push buttons straight into the breadboard and making the game work in single player first. Once we got the game mechanics working for one player then we moved onto making it multiplayer. It was quite a challenge fitting everything on one breadboard and this will foreshadow the challenges later in the final fabrication.
When it came to the fabrication for the final product, we wanted to use a sturdy material since the reaction-based nature of our game requires people to hit the buttons as fast as possible and thus needed to withstand considerable amount of force. We went with 1/8″ birch wood for the enclosure and we wanted to notch all the sides for more stability and also to give the gameboard a crafty, handmade feel to it. We laser cut all the pieces and even thought there were some re-cuts needed, the overall process went smoothly.
The real challenge that we had not anticipated came when we had to actually connect the wires from the actual components to the breadboard and fit all that into the small enclosure. When we were using the small push buttons and plugging LEDs directly into the breadboard, even though it was a bit crowded, everything fit and didn’t conflict with each other. When we switched to the big push buttons and having to extend the legs of the LEDs from the top of the gameboard to the breadboard underneath, we ran into issues of wires not being long enough. Initially, we thought having long wires meant it would be harder to fit everything into the enclosure so we trimmed the wires. But then when it came to plugging them in, the wires ran too short. Needless to say, a lot of re-soldering was done and I’m glad that part is over. Moreover, we had to consider the issue of not having the legs of the LEDs touch each other to avoid a short circuit. We ended up bending the legs in opposite directions once we fitted the LEDs through the top of the gameboard.
And the final challenge came once we fitted and secured all the LEDs and buttons to the top board: connecting the wires underneath to the breadboard without unplugging any by accident. The trouble was getting the wires to stay in place because the sockets weren’t a perfect fit which meant any pulling movement would cause the wires to pop out. Thus, we had to re-solder a lot of the wires to be longer so that we can plug them and proceed to plug the other ones in without unplugging any in the process. It was quite frustrating to do and took a lot of patience and precise movements to achieve. But the end result made it all worth it. Just look at how clean it is.
We set out to create a simple and fun interactive game and I think that is precisely what we achieved. The best moment was definitely seeing how much fun everyone had playing our game during class. It made all the frustration and hard work of the fabrication worth it. If we were to continue forward with this idea or to expand upon it, I think it would be to change the code a bit so that the LEDs that light up are in different places for each player. We can also introduce different levels where the time between the LEDs lighting get shorter and shorter as the game progresses. There is a lot of possibility with the framework we’ve created and the changes can just be made to the code itself. Overall, I had a really fun time creating this project and it will be a good party game for the future!