The Legend of Zelda Themed Quiz With SFX
by ShowieG in Circuits > Arduino
292 Views, 0 Favorites, 0 Comments
The Legend of Zelda Themed Quiz With SFX
This is a school project made for the subject 'If This Then That' at HKU in the Netherlands.
It's a 'The Legend of Zelda'-themed quiz setup for 3 teams and a judge. The judge gives a question and the team that is the quickest to push the button can answer. Their most left LED starts blinking, indicating they can answer. Once the answer has been given, the judge can then push the right or wrong button. If the answer is right the LED stays on and if it's wrong the LED goes off. Each team has three LED's meaning three points. At 3 points, you win.
The winner then pushes their button again, which activates a sound effect and the opening of the chest. Then the judge can click the reset button to start over. The reset button can actually be pressed at any time. This allows for a reset for when the judge gave the score.
If you're wondering why it's Zelda themed; I was looking at sound effects for the opening of chests and found the famous one from Zelda, which made me think of making the whole thing this theme, as I do like the franchise a lot. That's also why I chose the colors yellow, red, and green. When I looked up the logo, it featured those colors.
Supplies
Circuit
- Arduino Uno
- Jumpwires
- Breadbord
- 9x LED's (3 Yellow, 3 Green and 3 Red)
- 6x buttons (3 for judge, 1 for each team)
- 1x buzzer
- 1x servo
- 6x 10k Ohm Resistors (for buttons)
- 9x 330 Ohm Resistors (for LEDs)
Case
- Small chest
- Material to make the case (I used Medium-Density Fibreboards)
- Paperclip
- Extensions for the buttons
Assembly
- Gluegun
- Solder Iron
- (optional) Multimeter
Wiring the Circuit
Wire the circuit as shown in the image above. The video shows my setup for testing the code.
For clarity, I separated the circuit into two breadboards. The upper breadboard contains the team's LEDs and buttons while the bottom one contains the buttons for the judge. This can of course be put onto one breadboard, but in the actual box it will be separated from each other too.
Typing the Code
The code is quite lengthy, so I put it in different tabs in the IDE. When you make a tab without any dots in the name, the program will put the code beneath main tab's script. I took advantage of that to make my code more manageable. I'll put down the code per tab down here. You can find the pitches.h as a file in the attachments.
You might notice the code for each team are actually the same, except for the int currentTeam which is different for each team. With this knowledge you can increase or decrease the number of teams. Just copy paste the code and make sure the int currentTeam changes to a different number, when it's their turn. I'm pretty sure the code can be optimized, but I'm not knowledgeable enough right now to do that.
///// MAIN tab /////
#include
#include "pitches.h" //add note library
//notes in the melody
int melody1[] = {NOTE_G2, NOTE_A2, NOTE_B2, NOTE_CS3, NOTE_G2, NOTE_A2, NOTE_B2, NOTE_CS3,
NOTE_GS2, NOTE_AS2, NOTE_C3, NOTE_D3, NOTE_GS2, NOTE_AS2, NOTE_C3, NOTE_D3,
NOTE_A2, NOTE_B2, NOTE_CS3, NOTE_DS3, NOTE_A2, NOTE_B2, NOTE_CS3, NOTE_DS3,
NOTE_AS2, NOTE_C3, NOTE_D3, NOTE_E3, NOTE_AS2, NOTE_C3, NOTE_D3, NOTE_E3,
NOTE_B2, NOTE_CS3, NOTE_DS3, NOTE_F3,
NOTE_C3, NOTE_D3, NOTE_E3, NOTE_FS3,
NOTE_CS3, NOTE_DS3, NOTE_F3, NOTE_G3,
NOTE_D3, NOTE_E3, NOTE_FS3, NOTE_GS3
};
int melody2[] = {NOTE_A3, NOTE_AS3, NOTE_B3, NOTE_C4};
int melodyRight[] = {NOTE_CS4, NOTE_A3, NOTE_CS4, NOTE_A3};
int melodyWrong[] = {NOTE_AS2, NOTE_AS2, NOTE_AS2};
//note durations. 4=quarter note / 8=eighth note
int noteDuration1[] = {4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6
};
int noteDuration2[] = {8, 8, 8, 3};
int noteDurationRight[] = {8, 8, 8, 8};
int noteDurationWrong[] = {2, 2, 2};
Servo servo;
int servoPos = 250; //position servo in degrees
int buzzer = A0;
int buttonYellow = 5;
int buttonGreen = 9;
int buttonRed = 13;
int buttonReset = A3;
int buttonRight = A4;
int buttonWrong = A5;
const int LEDYellow1 = 2;
const int LEDYellow2 = 3;
const int LEDYellow3 = 4;
const int LEDGreen1 = 6;
const int LEDGreen2 = 7;
const int LEDGreen3 = 8;
const int LEDRed1 = 10;
const int LEDRed2 = 11;
const int LEDRed3 = 12;
int LEDStateYellow1 = LOW;
int LEDStateYellow2 = LOW;
int LEDStateYellow3 = LOW;
int LEDStateGreen1 = LOW;
int LEDStateGreen2 = LOW;
int LEDStateGreen3 = LOW;
int LEDStateRed1 = LOW;
int LEDStateRed2 = LOW;
int LEDStateRed3 = LOW;
//0 = unanswered
//1 = answering
//2 = right
//3 = wrong
int answerState = 0;
int pointsYellow = 0;
int pointsGreen = 0;
int pointsRed = 0;
//0 = no team
//1 = yellow team
//2 = green team
//3 = red team
int currentTeam = 0;
unsigned long previousMillis = 0;
const long interval = 400;
const long interval2 = 100;
void setup() {
pinMode(buttonYellow, INPUT);
pinMode(buttonGreen, INPUT);
pinMode(buttonRed, INPUT);
pinMode(buttonReset, INPUT);
pinMode(buttonRight, INPUT);
pinMode(buttonWrong, INPUT);
pinMode(LEDYellow1, OUTPUT);
pinMode(LEDYellow2, OUTPUT);
pinMode(LEDYellow3, OUTPUT);
pinMode(LEDGreen1, OUTPUT);;
pinMode(LEDGreen2, OUTPUT);
pinMode(LEDGreen3, OUTPUT);
pinMode(LEDRed1, OUTPUT);
pinMode(LEDRed2, OUTPUT);
pinMode(LEDRed3, OUTPUT);
servo.attach(A1); //which pin is the servo
}
void loop() {
programLoop();
}
///// LOOP tab /////
void
programLoop() {
int buttonStateYellow = digitalRead(buttonYellow);
int buttonStateGreen = digitalRead(buttonGreen);
int buttonStateRed = digitalRead(buttonRed);
int buttonStateReset = digitalRead(buttonReset);
int buttonStateRight = digitalRead(buttonRight);
int buttonStateWrong = digitalRead(buttonWrong);
digitalWrite(LEDYellow1, LEDStateYellow1);
digitalWrite(LEDYellow2, LEDStateYellow2);
digitalWrite(LEDYellow3, LEDStateYellow3);
digitalWrite(LEDGreen1, LEDStateGreen1);
digitalWrite(LEDGreen2, LEDStateGreen2);
digitalWrite(LEDGreen3, LEDStateGreen3);
digitalWrite(LEDRed1, LEDStateRed1);
digitalWrite(LEDRed2, LEDStateRed2);
digitalWrite(LEDRed3, LEDStateRed3);
servo.write(servoPos);
unsigned long currentMillis = millis();
if (buttonStateReset == 1) { //resets score, servoposition etc.
reset();
}
if (currentTeam == 0 && pointsYellow != 3 && pointsGreen != 3 && pointsRed != 3) { //decides who pressed first only when someone hasnt reached 3 points yet
if (buttonStateYellow == 1) {
currentTeam = 1;
answerState = 1;
}
if (buttonStateGreen == 1) {
currentTeam = 2;
answerState = 1;
}
if (buttonStateRed == 1) {
currentTeam = 3;
answerState = 1;
}
}
/*************************************************
YELLOW PLAYER
*************************************************/
if (pointsYellow == 0 && currentTeam == 1) {
if (answerState == 1) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (LEDStateYellow1 == LOW) { //blinks LED after pushing the button
LEDStateYellow1 = HIGH;
} else {
LEDStateYellow1 = LOW;
}
}
if (buttonStateRight == 1) {
answerState = 2; //right
rightSFX();
}
if (buttonStateWrong == 1) {
answerState = 3; //wrong
wrongSFX();
}
}
if (answerState == 2) {
LEDStateYellow1 = HIGH;
pointsYellow = 1;
smallreset();
}
if (answerState == 3) {
LEDStateYellow1 = LOW;
smallreset();
}
}
if (pointsYellow == 1 && currentTeam == 1) {
if (answerState == 1) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (LEDStateYellow2 == LOW) {
LEDStateYellow2 = HIGH;
} else {
LEDStateYellow2 = LOW;
}
}
if (buttonStateRight == 1) {
answerState = 2; //right
rightSFX();
}
if (buttonStateWrong == 1) {
answerState = 3; //wrong
wrongSFX();
}
}
if (answerState == 2) {
LEDStateYellow2 = HIGH;
pointsYellow = 2;
smallreset();
}
if (answerState == 3) {
LEDStateYellow2 = LOW;
smallreset();
}
}
if (pointsYellow == 2 && currentTeam == 1) {
if (answerState == 1) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (LEDStateYellow3 == LOW) {
LEDStateYellow3 = HIGH;
} else {
LEDStateYellow3 = LOW;
}
}
if (buttonStateRight == 1) {
answerState = 2; //right
rightSFX();
}
if (buttonStateWrong == 1) {
answerState = 3; //wrong
wrongSFX();
}
}
if (answerState == 2) {
LEDStateYellow3 = HIGH;
pointsYellow = 3;
smallreset();
}
if (answerState == 3) {
LEDStateYellow3 = LOW;
smallreset();
}
}
if (pointsYellow == 3 && buttonStateYellow == 1) { //the winner pushes their button again to open the chest
LEDStateRed1 = LOW;
LEDStateRed2 = LOW;
LEDStateRed3 = LOW;
LEDStateGreen1 = LOW;
LEDStateGreen2 = LOW;
LEDStateGreen3 = LOW;
LEDStateYellow1 = HIGH;
LEDStateYellow2 = HIGH;
LEDStateYellow3 = HIGH;
digitalWrite(LEDRed1, LEDStateRed1);
digitalWrite(LEDRed2, LEDStateRed2);
digitalWrite(LEDRed3, LEDStateRed3);
digitalWrite(LEDGreen1, LEDStateGreen1);
digitalWrite(LEDGreen2, LEDStateGreen2);
digitalWrite(LEDGreen3, LEDStateGreen3);
digitalWrite(LEDYellow1, LEDStateYellow1);
digitalWrite(LEDYellow2, LEDStateYellow2);
digitalWrite(LEDYellow3, LEDStateYellow3);
if (servoPos > 200) {
chestLoadSFX();
for (servoPos = 250; servoPos >= 70; servoPos -= 1) { // goes from 250 degrees to 70 degrees
servo.write(servoPos); // tell servo to go to position in variable 'pos'
delay(5); // waits 5ms for the servo to reach the position
}
chestOpenSFX();
}
}
/*************************************************
GREEN PLAYER
*************************************************/
if (pointsGreen == 0 && currentTeam == 2) {
if (answerState == 1) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (LEDStateGreen1 == LOW) {
LEDStateGreen1 = HIGH;
} else {
LEDStateGreen1 = LOW;
}
}
if (buttonStateRight == 1) {
answerState = 2; //right
rightSFX();
}
if (buttonStateWrong == 1) {
answerState = 3; //wrong
wrongSFX();
}
}
if (answerState == 2) {
LEDStateGreen1 = HIGH;
pointsGreen = 1;
smallreset();
}
if (answerState == 3) {
LEDStateGreen1 = LOW;
smallreset();
}
}
if (pointsGreen == 1 && currentTeam == 2) {
if (answerState == 1) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (LEDStateGreen2 == LOW) {
LEDStateGreen2 = HIGH;
} else {
LEDStateGreen2 = LOW;
}
}
if (buttonStateRight == 1) {
answerState = 2; //right
rightSFX();
}
if (buttonStateWrong == 1) {
answerState = 3; //wrong
wrongSFX();
}
}
if (answerState == 2) {
LEDStateGreen2 = HIGH;
pointsGreen = 2;
smallreset();
}
if (answerState == 3) {
LEDStateGreen2 = LOW;
smallreset();
}
}
if (pointsGreen == 2 && currentTeam == 2) {
if (answerState == 1) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (LEDStateGreen3 == LOW) {
LEDStateGreen3 = HIGH;
} else {
LEDStateGreen3 = LOW;
}
}
if (buttonStateRight == 1) {
answerState = 2; //right
rightSFX();
}
if (buttonStateWrong == 1) {
answerState = 3; //wrong
wrongSFX();
}
}
if (answerState == 2) {
LEDStateGreen3 = HIGH;
pointsGreen = 3;
smallreset();
}
if (answerState == 3) {
LEDStateGreen3 = LOW;
smallreset();
}
}
if (pointsGreen == 3 && buttonStateGreen == 1) {
LEDStateRed1 = LOW;
LEDStateRed2 = LOW;
LEDStateRed3 = LOW;
LEDStateGreen1 = HIGH;
LEDStateGreen2 = HIGH;
LEDStateGreen3 = HIGH;
LEDStateYellow1 = LOW;
LEDStateYellow2 = LOW;
LEDStateYellow3 = LOW;
digitalWrite(LEDRed1, LEDStateRed1);
digitalWrite(LEDRed2, LEDStateRed2);
digitalWrite(LEDRed3, LEDStateRed3);
digitalWrite(LEDGreen1, LEDStateGreen1);
digitalWrite(LEDGreen2, LEDStateGreen2);
digitalWrite(LEDGreen3, LEDStateGreen3);
digitalWrite(LEDYellow1, LEDStateYellow1);
digitalWrite(LEDYellow2, LEDStateYellow2);
digitalWrite(LEDYellow3, LEDStateYellow3);
if (servoPos > 200) {
chestLoadSFX();
for (servoPos = 250; servoPos >= 70; servoPos -= 1) { // goes from 250 degrees to 70 degrees
servo.write(servoPos); // tell servo to go to position in variable 'pos'
delay(5); // waits 5ms for the servo to reach the position
}
chestOpenSFX();
}
}
/*************************************************
RED PLAYER
*************************************************/
if (pointsRed == 0 && currentTeam == 3) {
if (answerState == 1) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (LEDStateRed1 == LOW) {
LEDStateRed1 = HIGH;
} else {
LEDStateRed1 = LOW;
}
}
if (buttonStateRight == 1) {
answerState = 2; //right
rightSFX();
}
if (buttonStateWrong == 1) {
answerState = 3; //wrong
wrongSFX();
}
}
if (answerState == 2) {
LEDStateRed1 = HIGH;
pointsRed = 1;
smallreset();
}
if (answerState == 3) {
LEDStateRed1 = LOW;
smallreset();
}
}
if (pointsRed == 1 && currentTeam == 3) {
if (answerState == 1) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (LEDStateRed2 == LOW) {
LEDStateRed2 = HIGH;
} else {
LEDStateRed2 = LOW;
}
}
if (buttonStateRight == 1) {
answerState = 2; //right
rightSFX();
}
if (buttonStateWrong == 1) {
answerState = 3; //wrong
wrongSFX();
}
}
if (answerState == 2) {
LEDStateRed2 = HIGH;
pointsRed = 2;
smallreset();
}
if (answerState == 3) {
LEDStateRed2 = LOW;
smallreset();
}
}
if (pointsRed == 2 && currentTeam == 3) {
if (answerState == 1) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (LEDStateRed3 == LOW) {
LEDStateRed3 = HIGH;
} else {
LEDStateRed3 = LOW;
}
}
if (buttonStateRight == 1) {
answerState = 2; //right
rightSFX();
}
if (buttonStateWrong == 1) {
answerState = 3; //wrong
wrongSFX();
}
}
if (answerState == 2) {
LEDStateRed3 = HIGH;
pointsRed = 3;
smallreset();
}
if (answerState == 3) {
LEDStateRed3 = LOW;
smallreset();
}
}
if (pointsRed == 3 && buttonStateRed == 1) {
LEDStateRed1 = HIGH;
LEDStateRed2 = HIGH;
LEDStateRed3 = HIGH;
LEDStateGreen1 = LOW;
LEDStateGreen2 = LOW;
LEDStateGreen3 = LOW;
LEDStateYellow1 = LOW;
LEDStateYellow2 = LOW;
LEDStateYellow3 = LOW;
digitalWrite(LEDRed1, LEDStateRed1);
digitalWrite(LEDRed2, LEDStateRed2);
digitalWrite(LEDRed3, LEDStateRed3);
digitalWrite(LEDGreen1, LEDStateGreen1);
digitalWrite(LEDGreen2, LEDStateGreen2);
digitalWrite(LEDGreen3, LEDStateGreen3);
digitalWrite(LEDYellow1, LEDStateYellow1);
digitalWrite(LEDYellow2, LEDStateYellow2);
digitalWrite(LEDYellow3, LEDStateYellow3);
if (servoPos > 200) {
chestLoadSFX();
for (servoPos = 250; servoPos >= 70; servoPos -= 1) { // goes from 250 degrees to 70 degrees
servo.write(servoPos); // tell servo to go to position in variable 'pos'
delay(5); // waits 5ms for the servo to reach the position
}
chestOpenSFX();
}
}
}
///// RESET tab /////
void reset() {
currentTeam = 0;
answerState = 0;
pointsYellow = 0;
pointsGreen = 0;
pointsRed = 0;
servoPos = 250;
LEDStateYellow1 = LOW;
LEDStateYellow2 = LOW;
LEDStateYellow3 = LOW;
LEDStateGreen1 = LOW;
LEDStateGreen2 = LOW;
LEDStateGreen3 = LOW;
LEDStateRed1 = LOW;
LEDStateRed2 = LOW;
LEDStateRed3 = LOW;
}
void smallreset() { //activates whenever an answer has been judged
currentTeam = 0;
answerState = 0;
///// SOUNDS tab /////
void rightSFX() {
for (int thisNote = 0; thisNote < 4; thisNote++) {
int noteDuration = 900 / noteDurationRight [thisNote];
tone(buzzer, melodyRight [thisNote], noteDuration);
//to distinguish the notes, set a minimum time between them
//the note's duration +30% seems to work well
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
//stop the tone playing
noTone(buzzer);
}
}
void wrongSFX() {
for (int thisNote = 0; thisNote < 3; thisNote++) {
int noteDuration = 500 / noteDurationWrong [thisNote];
tone(buzzer, melodyWrong [thisNote], noteDuration);
//to distinguish the notes, set a minimum time between them
//the note's duration +30% seems to work well
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
//stop the tone playing
noTone(buzzer);
}
}
void chestLoadSFX() {
for (int thisNote = 0; thisNote < 48; thisNote++) {
int noteDuration = 500 / noteDuration1 [thisNote];
tone(buzzer, melody1 [thisNote], noteDuration);
//to distinguish the notes, set a minimum time between them
//the note's duration +30% seems to work well
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
//stop the tone playing
noTone(buzzer);
}
}
void chestOpenSFX() {
for (int thisNote = 0; thisNote < 4; thisNote++) {
int noteDuration = 2000 / noteDuration2 [thisNote];
tone(buzzer, melody2 [thisNote], noteDuration);
//to distinguish the notes, set a minimum time between them
//the note's duration +30% seems to work well
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
//stop the tone playing
noTone(buzzer);
}
}
Downloads
Making the Case
Make a case by your own design or try mine. I drew my case in Adobe Illustrator and laser cut it at school.
I found the vectors for the logo and triforce here:
https://seeklogo.com/vector-logo/258099/the-legend...
https://seeklogo.com/vector-logo/312572/the-legend...
You can find my case file in the attachments as .dxf files.
The case is 10 cm in height, 20 cm in width and 15 cm in length. I used a glue-gun to glue it together.
Putting Everything Together
Time to solder. I had a rough time here, so I wish you good luck. Having a multimeter helped me out with figuring out if I soldered correctly or not.
My wires turned out to be quite short so I had to put the something under the Arduino so the wires could reach it.
Some other notes about this project:
- Once the buttons for the team were in place I could add button extensions, to make it bigger and easier to press.
- I bought the mini chest at a store called 'Action'
- I attached the servo to the inside of the chest and with a paperclip I made the arm that opens it. There's a hole where the arm can go through.
- I actually had a fourth LED for each team that would light up when the chest is about to open. This was to better indicate the winner. Currently the winner's LED's stay lit and the others' go off, when the chest is about to open. This got cancelled because there just aren't enough pins on the Arduino Uno.
- I had the idea of making this also a music quiz, which music fragments the buzzer could play. This however soon got cancelled because I found out that button-presses aren't registered during a song fragment. This is because of the delays.