The fundamental Computer Vision and Machine Learning Game that uses the Mediapipe library to track the hand gestures of the player.
import pygame
import random
import image
from settings import *
from zombie import Zombie

class Angel(Zombie):
    def __init__(self):
        #size
        random_size_value = random.uniform(ANGEL_SIZE_RANDOMIZE[0], ANGEL_SIZE_RANDOMIZE[1])
        size = (int(ANGEL_SIZES[0] * random_size_value), int(ANGEL_SIZES[1] * random_size_value))
        # moving
        moving_direction, start_pos = self.define_spawn_pos(size)
        # sprite
        self.rect = pygame.Rect(start_pos[0], start_pos[1], size[0]//1.4, size[1]//1.4)
        # self.images = [image.load(f"Assets/angel/{nb}.png", size=size, flip=moving_direction=="right") for nb in range(1, 7)] # load the ima0ges
        self.images = [image.load(f"Assets/angel/angel.png", size=size, flip=moving_direction=="right")] # load the image
        self.current_frame = 0
        self.animation_timer = 0
        

    def kill(self, zombies): # remove the zombie from the list
        zombies.remove(self)
        return -ANGEL_PENALITY

angel.py
tovanhieu/MachineLearningGame-ZombieKiller
import pygame
import time
import random
from settings import *
from background import Background
from hand import Hand
from hand_tracking import HandTracking
from zombie import Zombie
from angel import Angel
import cv2
import ui

class Game:
    def __init__(self, surface):
        self.surface = surface
        self.background = Background()

        # Load camera
        self.cap = cv2.VideoCapture(0)

        self.sounds = {}
        self.sounds["slap"] = pygame.mixer.Sound(f"Assets/Sounds/slap.wav")
        self.sounds["slap"].set_volume(SOUNDS_VOLUME)
        self.sounds["screaming"] = pygame.mixer.Sound(f"Assets/Sounds/screaming.wav")
        self.sounds["screaming"].set_volume(SOUNDS_VOLUME)


    def reset(self): # reset all the needed variables
        self.hand_tracking = HandTracking()
        self.hand = Hand()
        self.animators = []
        self.animators_spawn_timer = 0
        self.score = 0
        self.game_start_time = time.time()


    def spawn_animators(self):
        t = time.time()
        if t > self.animators_spawn_timer:
            self.insects_spawn_timer = t + ZOMBIES_SPAWN_TIME

            # increase the probability that the animation will be a angel or a zombie over time
            nb = (GAME_DURATION-self.time_left)/GAME_DURATION * 100  /50  # increase from 0 to 50 during all  the game (linear)
            if random.randint(0, 20) < nb:
                self.animators.append(Angel())
            else:
                self.animators.append(Zombie())

            # spawn a other zombie after the half of the game
            if self.time_left < GAME_DURATION/2:
                self.animators.append(Zombie())

    def load_camera(self):
        _, self.frame = self.cap.read()


    def set_hand_position(self):
        self.frame = self.hand_tracking.scan_hands(self.frame)
        (x, y) = self.hand_tracking.get_hand_center()
        self.hand.rect.center = (x, y)

    def draw(self):
        # draw the background
        self.background.draw(self.surface)
        # draw the animators
        for animator in self.animators:
            animator.draw(self.surface)
        # draw the hand
        self.hand.draw(self.surface)
        # draw the score
        ui.draw_text(self.surface, f"Score : {self.score}", (5, 5), COLORS["score"], font=FONTS["medium"],
                    shadow=True, shadow_color=(255,255,255))
        # draw the time left
        timer_text_color = (160, 40, 0) if self.time_left < 5 else COLORS["timer"] # change the text color if less than 5 s left
        ui.draw_text(self.surface, f"Time left : {self.time_left}", (SCREEN_WIDTH//2, 5),  timer_text_color, font=FONTS["medium"],
                    shadow=True, shadow_color=(255,255,255))


    def game_time_update(self):
        self.time_left = max(round(GAME_DURATION - (time.time() - self.game_start_time), 1), 0)



    def update(self):

        self.load_camera()
        self.set_hand_position()
        self.game_time_update()

        self.draw()

        if self.time_left > 0:
            self.spawn_animators()
            (x, y) = self.hand_tracking.get_hand_center()
            self.hand.rect.center = (x, y)
            self.hand.left_click = self.hand_tracking.hand_closed
            print("Hand closed", self.hand.left_click)
            if self.hand.left_click:
                self.hand.image = self.hand.image_smaller.copy()
            else:
                self.hand.image = self.hand.orig_image.copy()
            self.score = self.hand.kill_animators(self.animators, self.score, self.sounds)
            for animator in self.animators:
                animator.move()

        else: # when the game is over
            if ui.button(self.surface, 540, "Continue", click_sound=self.sounds["slap"]):
                return "menu"


        cv2.imshow("Frame", self.frame)
        cv2.waitKey(1)

game.py
tovanhieu/MachineLearningGame-ZombieKiller
import pygame
import image
from settings import *
from hand_tracking import HandTracking
import cv2

class Hand:
    def __init__(self):
        self.orig_image = image.load("Assets/hand.png", size=(HAND_SIZE, HAND_SIZE))
        self.image = self.orig_image.copy()
        self.image_smaller = image.load("Assets/hand.png", size=(HAND_SIZE - 50, HAND_SIZE - 50))
        self.rect = pygame.Rect(SCREEN_WIDTH//2, SCREEN_HEIGHT//2, HAND_HITBOX_SIZE[0], HAND_HITBOX_SIZE[1])
        self.left_click = False
        #self.hand_tracking = HandTracking()


    def follow_mouse(self): # change the hand pos center at the mouse pos
        self.rect.center = pygame.mouse.get_pos()
        # self.hand_tracking.display_hand()

    def follow_mediapipe_hand(self, x, y):
        self.rect.center = (x, y)

    def draw_hitbox(self, surface):
        pygame.draw.rect(surface, (200, 60, 0), self.rect)


    def draw(self, surface):
        image.draw(surface, self.image, self.rect.center, pos_mode="center")

        if DRAW_HITBOX:
            self.draw_hitbox(surface)


    def on_animator(self, animators): # return a list with all animations that collide with the hand hitbox
        return [animator for animator in animators if self.rect.colliderect(animator.rect)]


    def kill_animators(self, animators, score, sounds): # will kill the animators that collide with the hand when the left mouse button is pressed
        if self.left_click: # if left click
            for animator in self.on_animator(animators):
                animator_score = animator.kill(animators)
                score += animator_score
                sounds["slap"].play()
                if animator_score < 0:
                    sounds["screaming"].play()
        else:
            self.left_click = False
        return score

hand.py
tovanhieu/MachineLearningGame-ZombieKiller
# Setup Python ----------------------------------------------- #
import pygame
import sys
import os
from settings import *
from game import Game
from menu import Menu




# Setup pygame/window --------------------------------------------- #
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100,32) # windows position
pygame.init()
pygame.display.set_caption(WINDOW_NAME)
SCREEN = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT),0,32)

mainClock = pygame.time.Clock()

# Fonts ----------------------------------------------------------- #
fps_font = pygame.font.SysFont("coopbl", 22)

# Music ----------------------------------------------------------- #
pygame.mixer.music.load("Assets/Sounds/Summertime-background-music.mp3")
pygame.mixer.music.set_volume(MUSIC_VOLUME)
pygame.mixer.music.play(-1)
# Variables ------------------------------------------------------- #
state = "menu"

# Creation -------------------------------------------------------- #
game = Game(SCREEN)
menu = Menu(SCREEN)



# Functions ------------------------------------------------------ #
def user_events():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                sys.exit()


def update():
    global state
    if state == "menu":
        if menu.update() == "game":
            game.reset() # reset the game to start a new game
            state = "game"
    elif state == "game":
        if game.update() == "menu":
            state = "menu"
    pygame.display.update()
    mainClock.tick(FPS)



# Loop ------------------------------------------------------------ #
while True:

    # Buttons ----------------------------------------------------- #
    user_events()

    # Update ------------------------------------------------------ #
    update()

    # FPS
    if DRAW_FPS:
        fps_label = fps_font.render(f"FPS: {int(mainClock.get_fps())}", 1, (255,200,20))
        SCREEN.blit(fps_label, (5,5))

main.py
tovanhieu/MachineLearningGame-ZombieKiller
import pygame

WINDOW_NAME = "Zombie Killer Game - Creator: Hieu (Toni) github:tovanhieu "
GAME_TITLE = WINDOW_NAME

SCREEN_WIDTH, SCREEN_HEIGHT = 1200, 700

FPS = 90
DRAW_FPS = True


# sizes
BUTTONS_SIZES = (240, 90)
HAND_SIZE = 200
HAND_HITBOX_SIZE = (60, 80)
ZOMBIES_SIZES = (50, 38)
ZOMBIE_SIZE_RANDOMIZE = (1,2) # for each new zombie, it will multiply the size with an random value between X and Y
ANGEL_SIZES = (50, 50)
ANGEL_SIZE_RANDOMIZE = (1.2, 1.5)

# drawing
DRAW_HITBOX = False # will draw all the hitbox

# animation
ANIMATION_SPEED = 3 # the frame of the zombies will change every X sec

# difficulty
GAME_DURATION = 60 # the game will last X sec
ZOMBIES_SPAWN_TIME = 1
ZOMBIES_MOVE_SPEED = {"min": 0.5, "max": 2}
ANGEL_PENALITY = 1 # will remove X of the score of the player (if you kills a angel)

# colors
COLORS = {"title": (38, 61, 39), "score": (38, 61, 39), "timer": (38, 61, 39),
            "buttons": {"default": (56, 67, 209), "second":  (87, 99, 255),
                        "text": (255, 255, 255), "shadow": (46, 54, 163)}} # second is the color when the mouse is on the button

# sounds / music
MUSIC_VOLUME = 0.4 # value between 0 and 1
SOUNDS_VOLUME = 5

# fonts
pygame.font.init()
FONTS = {}
FONTS["small"] = pygame.font.Font(None, 30)
FONTS["medium"] = pygame.font.Font(None, 40)
FONTS["big"] = pygame.font.Font(None, 60)

settings.py
tovanhieu/MachineLearningGame-ZombieKiller
import pygame
import random
import time
import image
from settings import *

class Zombie:
    def __init__(self):
        #size
        random_size_value = random.uniform(ZOMBIE_SIZE_RANDOMIZE[0], ZOMBIE_SIZE_RANDOMIZE[1])
        size = (int(ZOMBIES_SIZES[0] * random_size_value), int(ZOMBIES_SIZES[1] * random_size_value))
        # moving
        moving_direction, start_pos = self.define_spawn_pos(size)
        # sprite
        self.rect = pygame.Rect(start_pos[0], start_pos[1], size[0]//1.4, size[1]//1.4)
        self.images = [image.load("Assets/zombie/zombie.png", size=size, flip=moving_direction=="right")]
        self.current_frame = 0
        self.animation_timer = 0


    def define_spawn_pos(self, size): # define the start pos and moving vel of the zombie
        vel = random.uniform(ZOMBIES_MOVE_SPEED["min"], ZOMBIES_MOVE_SPEED["max"])
        moving_direction = random.choice(("left", "right", "up", "down"))
        if moving_direction == "right":
            start_pos = (-size[0], random.randint(size[1], SCREEN_HEIGHT-size[1]))
            self.vel = [vel, 0]
        if moving_direction == "left":
            start_pos = (SCREEN_WIDTH + size[0], random.randint(size[1], SCREEN_HEIGHT-size[1]))
            self.vel = [-vel, 0]
        if moving_direction == "up":
            start_pos = (random.randint(size[0], SCREEN_WIDTH-size[0]), SCREEN_HEIGHT+size[1])
            self.vel = [0, -vel]
        if moving_direction == "down":
            start_pos = (random.randint(size[0], SCREEN_WIDTH-size[0]), -size[1])
            self.vel = [0, vel]
        return moving_direction, start_pos


    def move(self):
        self.rect.move_ip(self.vel)


    def animate(self): # change the frame of the zombie when needed
        t = time.time()
        if t > self.animation_timer:
            self.animation_timer = t + ANIMATION_SPEED
            self.current_frame += 1
            if self.current_frame > len(self.images)-1:
                self.current_frame = 0


    def draw_hitbox(self, surface):
        pygame.draw.rect(surface, (200, 60, 0), self.rect)



    def draw(self, surface):
        self.animate()
        image.draw(surface, self.images[self.current_frame], self.rect.center, pos_mode="center")
        if DRAW_HITBOX:
            self.draw_hitbox(surface)


    def kill(self, mosquitos): # remove the zombie from the list
        mosquitos.remove(self)
        return 1

zombie.py
tovanhieu/MachineLearningGame-ZombieKiller