Skip to main content

๐ŸŽฏ AI Shooting Game Using Python & ESP32-CAM | Hand Tracking Game


In this project, we will build an exciting AI Hand Gesture Shooting Game using Python, OpenCV, and MediaPipe. Instead of using a mouse, you will control the crosshair with your finger and shoot using a pinch gesture ๐Ÿค.

๐Ÿ’ฐ Pro Tip: If you want to make this project portable and advanced, you can use an ESP32-CAM module instead of a normal camera and stream video to your laptop.

๐Ÿงฐ 1. Hardware Required

  • ๐Ÿ’ป Laptop / PC (Windows recommended)
  • ๐Ÿ“ท ESP32-CAM Module (Recommended for smart AI projects)
  • ๐Ÿ”Œ USB to TTL Converter (for programming ESP32-CAM)
  • ⚡ Jumper Wires
  • ๐Ÿ“ถ WiFi Connection

๐Ÿ‘‰ Recommended Module: ESP32-CAM (OV2640 Camera)


๐Ÿ“š 2. Python Libraries Required

Open Command Prompt and install the following libraries:

pip install opencv-python mediapipe

That’s it! Now you're ready to run the AI game ๐Ÿš€


๐Ÿง  3. Complete Python Code

import cv2
import mediapipe as mp
import random
import math
import time

# =============================
# SETUP HAND TRACKING
# =============================
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(max_num_hands=1)
mp_draw = mp.solutions.drawing_utils

cap = cv2.VideoCapture(0)

# Fullscreen mode
cv2.namedWindow("AI Shooting Game", cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty("AI Shooting Game", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)

# =============================
# GAME VARIABLES
# =============================
score = 0
disc_radius = 30
disc_speed = 6
disc_x = 0
disc_y = 200

last_shot_time = 0
cooldown = 0.5  # time between shots

# Create new disc
def new_disc(width, height):
    x = 0
    y = random.randint(100, height - 100)
    return x, y

# =============================
# MAIN LOOP
# =============================
while True:
    success, frame = cap.read()
    if not success:
        break

    frame = cv2.flip(frame, 1)
    height, width, _ = frame.shape

    rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    result = hands.process(rgb)

    cross_x = width // 2
    cross_y = height // 2
    shoot = False

    # =============================
    # HAND DETECTION
    # =============================
    if result.multi_hand_landmarks:
        for hand_landmarks in result.multi_hand_landmarks:

            landmarks = hand_landmarks.landmark

            # INDEX FINGER TIP (8)
            index_tip = landmarks[8]
            cross_x = int(index_tip.x * width)
            cross_y = int(index_tip.y * height)

            # THUMB TIP (4)
            thumb_tip = landmarks[4]
            thumb_x = int(thumb_tip.x * width)
            thumb_y = int(thumb_tip.y * height)

            # Draw green circle on index
            cv2.circle(frame, (cross_x, cross_y), 12, (0, 255, 0), -1)

            # Draw blue circle on thumb
            cv2.circle(frame, (thumb_x, thumb_y), 12, (255, 0, 0), -1)

            # -------- PINCH DETECTION --------
            distance = math.sqrt((cross_x - thumb_x)**2 +
                                 (cross_y - thumb_y)**2)

            if distance < 40:  # Sensitivity
                current_time = time.time()
                if current_time - last_shot_time > cooldown:
                    shoot = True
                    last_shot_time = current_time

    # =============================
    # DISC MOVEMENT
    # =============================
    disc_x += disc_speed

    if disc_x > width:
        disc_x, disc_y = new_disc(width, height)

    # Draw flying disc
    cv2.circle(frame, (disc_x, disc_y), disc_radius, (255, 200, 0), -1)
    cv2.circle(frame, (disc_x, disc_y), disc_radius, (0, 0, 0), 3)

    # =============================
    # SHOOT CHECK
    # =============================
    if shoot:
        distance_to_disc = math.sqrt((cross_x - disc_x)**2 +
                                     (cross_y - disc_y)**2)

        if distance_to_disc < disc_radius:
            score += 1
            disc_x, disc_y = new_disc(width, height)

        # Small shoot flash
        cv2.putText(frame, "SHOOT!",
                    (cross_x - 40, cross_y - 40),
                    cv2.FONT_HERSHEY_SIMPLEX,
                    1,
                    (0, 255, 255),
                    3)

    # =============================
    # DRAW RED CROSSHAIR
    # =============================
    cv2.line(frame, (cross_x - 20, cross_y),
             (cross_x + 20, cross_y), (0, 0, 255), 2)
    cv2.line(frame, (cross_x, cross_y - 20),
             (cross_x, cross_y + 20), (0, 0, 255), 2)
    cv2.circle(frame, (cross_x, cross_y), 8, (0, 0, 255), 2)

    # =============================
    # DISPLAY SCORE
    # =============================
    cv2.putText(frame, "Score: " + str(score),
                (50, 70),
                cv2.FONT_HERSHEY_SIMPLEX,
                1.5,
                (0, 255, 0),
                3)

    cv2.imshow("AI Shooting Game", frame)

    # ESC to exit
    if cv2.waitKey(1) == 27:
        break

cap.release()
cv2.destroyAllWindows()

๐Ÿ” 4. Code Explanation (Block by Block)

๐Ÿ“Œ Import Libraries

import cv2
import mediapipe as mp
import random
import math
import time

cv2 → Used for video processing ✅ mediapipe → Used for hand tracking ✅ random → To generate random disc positions ✅ math → For distance calculation ✅ time → For shooting cooldown system


๐Ÿ“Œ Setup Hand Tracking

mp_hands = mp.solutions.hands
hands = mp_hands.Hands(max_num_hands=1)
mp_draw = mp.solutions.drawing_utils

This initializes MediaPipe Hand Detection. We detect only one hand for better performance.


๐Ÿ“Œ Game Variables

score = 0
disc_radius = 30
disc_speed = 6

๐ŸŽฏ score → Stores player score ๐ŸŽฏ disc_radius → Size of target ๐ŸŽฏ disc_speed → Speed of flying disc


๐Ÿ“Œ Pinch Detection (Shoot Logic)

distance = math.sqrt((cross_x - thumb_x)**2 +
                     (cross_y - thumb_y)**2)

if distance < 40:
    shoot = True

๐Ÿค When thumb and index finger come close, distance becomes small → system triggers SHOOT.


๐Ÿ“Œ Disc Movement

disc_x += disc_speed

if disc_x > width:
    disc_x, disc_y = new_disc(width, height)

The disc keeps moving horizontally. If it goes outside the screen → new disc appears randomly.


๐Ÿ“Œ Hit Detection

if distance_to_disc < disc_radius:
    score += 1

If crosshair touches disc → Score increases ๐ŸŽ‰


๐ŸŽฎ How the Game Works

  • ๐Ÿ‘‰ Move index finger to control crosshair
  • ๐Ÿ‘‰ Pinch thumb + index to shoot
  • ๐Ÿ‘‰ Hit the flying disc
  • ๐Ÿ‘‰ Score increases
  • ๐Ÿ‘‰ Press ESC to exit

๐Ÿš€ Why Use ESP32-CAM?

  • ๐Ÿ“ท Low cost AI camera module
  • ๐Ÿ“ถ Wireless streaming
  • ๐Ÿค– Perfect for robotics projects
  • ๐ŸŽ“ Ideal for school & college AI demos

๐Ÿ‘‰ Get your ESP32-CAM here: ๐Ÿ”ฅ Buy ESP32-CAM Now


๐Ÿ’ก Future Improvements

  • ๐Ÿ”ซ Add gun sound effects
  • ๐ŸŽฏ Add multiple targets
  • ⏱ Add timer mode
  • ๐Ÿ† Add leaderboard

๐Ÿ“ข Final Words

This project is perfect for students learning AI + Computer Vision + Python. If you enjoyed this tutorial, share it with your friends and try building it using ESP32-CAM for advanced learning!

Happy Coding ๐ŸŽ‰

Comments

Popular posts from this blog

TOUCH PLATE BASED DOOR BELL

Title:  Touch plate based door bell  Circuit:  Components: IC 555 Resistors: 1 M, 100k, 330 ohms Transistor: BC547  PN2222A Capacitor: 10n 1 Copper plate : as touch plate. A 6v battery An LED / Ic UM66 Description: This is the simple circuit for touch plate based security system. In this project what basically done is, circuit detects stray voltages produced by mains voltage and electrostatic built  up in the room.If sufficient static voltage is detected by the plate then chip will charge up. Transistor BC 547 or PN2222A is used basically to increase the sensitivity.In place of led just connect IC um 66(melody IC). Applications: In homes, of course. This can be specially used in places like hospitals, when patients need to call doctor by himself.

Interfacing Load Cell with Raspberry Pi 3 (via HX711) ⚖️

Interfacing Load Cell with Raspberry Pi 3 (via HX711) ⚖️ Interfacing Load Cell with Raspberry Pi 3 (via HX711) ⚖️ A load cell is a transducer that converts force (weight) into an electrical signal. The HX711 is a precision 24-bit analog-to-digital converter (ADC) designed for weigh scales. Today we’ll connect a load cell to Raspberry Pi 3 using the HX711 module. ๐Ÿงช ๐Ÿ”ง Components Required Component Quantity Raspberry Pi 3 1 Load Cell 1 HX711 Module 1 Jumper Wires 6 Breadboard (optional) 1 ๐Ÿ”Œ Pin Connections HX711 Pin Raspberry Pi Pin Pin Number VCC 5V Pin 2 GND Ground Pin 6 DT GPIO 5 Pin 29 SCK GPIO 6 Pin 31 Figure: Load Cell connected to Raspberry Pi 3 via HX711 ๐Ÿ’ป Python Code from hx711 import HX711 import RPi.GPIO as GPIO import time hx = HX711(dout_pin=5, pd_sck_pin=6) hx.set_reading_format("MSB", "MSB") hx.set_reference_unit(1) hx.reset()...

Interfacing Sound Sensor with Raspberry Pi 3

๐Ÿ”น Overview The KY-037 is a high-sensitivity sound detection sensor that can detect noise levels in the environment. It provides both analog and digital outputs. In this tutorial, we’ll interface the digital output of KY-037 with Raspberry Pi 3 Model B+ (without using an ADC like MCP3008) and detect sound events.