Build a light target game

A printed target lights up for a moment, and you have to hit it with a torch beam before it goes dark. The photoresistor counts your hits and the buzzer cheers.
  • Level:Beginner
  • Duration: 50 min
  • Ages 10+
  • Platform: Arduino

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Build a light target game – EEMENTOR STEM™ lesson

Fairground shooting games use light guns: the "gun" sends a beam, and a sensor in the target sees it. You can build the same thing with a torch and the photoresistor. The target lights up a green LED for a short, random time. Hit it with the torch beam while it is green and you score a point; hit it while it is dark and you lose one. After ten targets the game shows your score. Please only use a normal torch or a phone light, and never shine it in anyone's eyes.

What you'll learn

  • How to calibrate a light sensor to the light in the room
  • How to detect a sudden jump in brightness
  • How to make a game with random timing
  • How to keep score and finish a game after a set number of rounds

What you need

  • UNO R3 board and USB cable (UNO R3 Starter Kit)
  • Photoresistor module (45-in-1 Sensor Kit)
  • Active buzzer module (45-in-1 Sensor Kit)
  • Green LED and 1 resistor, 220 Ω (UNO R3 Starter Kit)
  • 1 push button (UNO R3 Starter Kit)
  • A torch or a phone with a torch
  • The printed parts from the 3D printing box below (target), or the cardboard version described there
  • Breadboard and jumper wires (UNO R3 Starter Kit)

Wiring

Breadboard wiring diagram: Build a light target game
Breadboard view. Check each connection against the wiring table below.
From To
Photoresistor module S UNO pin A0
Photoresistor module + and − 5V and GND
UNO pin 5 220 Ω resistor, then the green LED's long leg
Green LED short leg GND
Active buzzer module S UNO pin 8
Active buzzer module + and − 5V and GND
Start button, one leg UNO pin 2
Start button, the diagonally opposite leg GND

Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.

The sketch

const int LIGHT_PIN = A0;
const int TARGET_LED = 5;
const int BUZZER_PIN = 8;
const int START_PIN = 2;
const int ROUNDS = 10;
const int JUMP = 120;                 // how much brighter counts as a hit
const bool BRIGHTER_IS_HIGHER = false; // see "Check your sensor"

int roomLight = 0;

int light() {
  int v = analogRead(LIGHT_PIN);
  return BRIGHTER_IS_HIGHER ? v : 1023 - v;
}

void beep(int ms) {
  digitalWrite(BUZZER_PIN, HIGH);
  delay(ms);
  digitalWrite(BUZZER_PIN, LOW);
}

void calibrate() {
  long sum = 0;
  for (int i = 0; i < 50; i++) {
    sum += light();
    delay(10);
  }
  roomLight = sum / 50;
  Serial.print("Room light: ");
  Serial.println(roomLight);
}

bool hit() {
  return light() > roomLight + JUMP;
}

void playGame() {
  int score = 0;
  for (int round = 1; round <= ROUNDS; round++) {
    delay(random(800, 2500));                 // wait a random time
    if (hit()) {                              // shining too early costs a point
      score--;
      beep(400);
      Serial.println("Too early! -1");
      while (hit()) delay(10);
      continue;
    }
    digitalWrite(TARGET_LED, HIGH);
    unsigned long upTime = random(700, 1600);
    unsigned long start = millis();
    bool scored = false;
    while (millis() - start < upTime) {
      if (hit()) { scored = true; break; }
    }
    digitalWrite(TARGET_LED, LOW);
    if (scored) {
      score++;
      beep(60); delay(60); beep(60);
      Serial.print("Hit in ");
      Serial.print(millis() - start);
      Serial.println(" ms");
    } else {
      Serial.println("Missed");
    }
    while (hit()) delay(10);                  // wait until the beam is away
  }
  Serial.print("Game over! Score: ");
  Serial.print(score);
  Serial.print(" of ");
  Serial.println(ROUNDS);
  for (int i = 0; i < score; i++) {           // blink once for every point
    digitalWrite(TARGET_LED, HIGH); delay(200);
    digitalWrite(TARGET_LED, LOW); delay(200);
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(TARGET_LED, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(START_PIN, INPUT_PULLUP);
  Serial.println("Press the button to start");
}

void loop() {
  if (digitalRead(START_PIN) == LOW) {
    randomSeed(micros());
    calibrate();
    beep(300);
    playGame();
  }
}

Check your sensor

  1. Open the Serial Monitor at 9600 baud and press the button. It prints the room light.
  2. Shine the torch at the target from where you will play. If hits are never counted, change BRIGHTER_IS_HIGHER to true.
  3. If the target counts hits without the torch, make JUMP bigger; if it misses real hits from far away, make it smaller or dim the room.

How it works

A light sensor on its own can't tell "torch" from "bright room". So the game first calibrates: it measures the normal room light 50 times and takes the average. A hit is any reading that is much brighter than that, by at least JUMP.

Random waiting times stop players from guessing the rhythm, and the "too early" rule stops them from simply holding the torch on the target. while (hit()) delay(10); waits until the beam has moved away, so one long flash counts only once.

Try this next

  • Make the target stay up for a shorter time every round, so the game gets harder.
  • Add a second target with a second photoresistor and light a random one.
  • Show the score on the 1602 LCD.

Troubleshooting

  • Every round says "Too early": the room light changed after calibrating, or JUMP is too small.
  • Hits are never counted: see "Check your sensor", and aim at the centre hole.
  • The buzzer doesn't sound: it must be the active buzzer module, with S on pin 8.
  • A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

45-in-1 Sensor Kit | Compatible with Arduino IDE and ESP32 $37.00

Shop the kit