aboutsummaryrefslogtreecommitdiff
path: root/game.py
blob: 9158e378fea0113778f22920975007fefe5e1dbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
import cv2
import numpy as np

import pygame
import random
import threading
import sys

from cockroach import Cockroach
from asteroid import Asteroid

# Initialize Pygame
pygame.init()

# Define screen dimensions
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 400

class MyGame:
    def __init__(self) -> None:
        # Open the webcam (0 is usually the default)
        self.cap = cv2.VideoCapture(0)

        # Create a list to hold cockroaches
        self.cockroaches = []
        self.asteroids = []

        # Create the game screen
        self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
        pygame.display.set_caption('Camera Feed and Cockroach Panel')

        # Load font
        self.font = pygame.font.SysFont(None, 40)

        # Track whether the Pygame window should be closed
        self.running = True

        # Create an event to signal the camera feed thread to stop
        self.stop_event = threading.Event()

        # Game state variables
        self.lives = 5
        self.game_over = False

    def spawn_objects(self):
        # Spawn a new cockroach randomly
        if len(self.cockroaches) < 5 and random.random() < 0.02:
            self.cockroaches.append(Cockroach())

        # Spawn a new asteroid randomly
        if len(self.asteroids) < 2 and random.random() < 0.016:
            self.asteroids.append(Asteroid())

    def move_objects(self):
        # Move cockroaches downwards and remove off-screen ones
        for cockroach in self.cockroaches:
            cockroach.move_down()
            if cockroach.y >= SCREEN_HEIGHT:
                self.cockroaches.remove(cockroach)

        # Move asteroid downwards and remove off-screen ones
        for asteroid in self.asteroids:
            asteroid.move_down()
            if asteroid.y >= SCREEN_HEIGHT:
                self.asteroids.remove(asteroid)

    def detect_collision_cockroach(self, x, y, w, h):
        cockroaches_to_remove = []
        for cockroach in self.cockroaches:
            # Calculate left, right, top, and bottom boundaries of the cockroach
            cockroach_left = cockroach.x
            cockroach_right = cockroach.x + cockroach.width
            cockroach_top = cockroach.y
            cockroach_bottom = cockroach.y + cockroach.height

            # Calculate left, right, top, and bottom boundaries of the detection rectangle
            detection_left = x
            detection_right = x + w
            detection_top = y
            detection_bottom = y + h

            # Check for overlap
            if (cockroach_right >= detection_left and cockroach_left <= detection_right) and \
               (cockroach_bottom >= detection_top and cockroach_top <= detection_bottom):
                cockroaches_to_remove.append(cockroach)

        for cockroach in cockroaches_to_remove:
            self.cockroaches.remove(cockroach)

    def detect_collision_asteroid(self, x, y, w, h):
        asteroids_to_remove = []
        for asteroid in self.asteroids:
            # Calculate left, right, top, and bottom boundaries of the asteroid
            asteroid_left = asteroid.x
            asteroid_right = asteroid.x + asteroid.width
            asteroid_top = asteroid.y
            asteroid_bottom = asteroid.y + asteroid.height

            # Calculate left, right, top, and bottom boundaries of the detection rectangle
            detection_left = x
            detection_right = x + w
            detection_top = y
            detection_bottom = y + h

            # Check for overlap
            if (asteroid_right >= detection_left and asteroid_left <= detection_right) and \
               (asteroid_bottom >= detection_top and asteroid_top <= detection_bottom):
                asteroids_to_remove.append(asteroid)

        for asteroid in asteroids_to_remove:
            self.asteroids.remove(asteroid)
            self.lives -= 1
            if self.lives == 0:
                self.game_over = True

    def draw_text(self, text, color, x, y):
        text_surface = self.font.render(text, True, color)
        text_rect = text_surface.get_rect()
        text_rect.midtop = (x, y)
        self.screen.blit(text_surface, text_rect)

    def start_game(self):

        while not self.stop_event.is_set() and not self.game_over:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    # Set the stop event when the 'x' button is clicked
                    self.stop_event.set()

            self.spawn_objects()
            self.move_objects()

            # Read frame from the webcam
            ret, frame = self.cap.read()

            # If frame is not returned, break
            if not ret:
                break

            # Rotate the frame counter-clockwise by 90 degrees
            frame = cv2.rotate(frame, cv2.ROTATE_90_COUNTERCLOCKWISE)

            # Convert frame from RGB to HSV, and define frame used for detection
            det_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

            # Define lower green and upper green ranges
            lower_green1 = np.array([0, 100, 100])
            upper_green1 = np.array([30, 255, 255])

            lower_green2 = np.array([60, 100, 100])
            upper_green2 = np.array([100, 255, 255])

            # Combine the green masks
            green_mask1 = cv2.inRange(det_frame, lower_green1, upper_green1)
            green_mask2 = cv2.inRange(det_frame, lower_green2, upper_green2)
            green_mask = cv2.bitwise_or(green_mask1, green_mask2)


            # Detect contours            
            contours, _ = cv2.findContours(green_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            
            # Find largest contour
            largest_contour = max(contours, key=cv2.contourArea) if contours else None

            # If largest contour is present, draw a green rectangle
            if largest_contour is not None:
                x, y, w, h = cv2.boundingRect(largest_contour)
                self.detect_collision_cockroach(x, y, w, h)
                self.detect_collision_asteroid(x, y, w, h)
                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
            
            frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

            # Convert frame to Pygame surface
            frame_pygame = pygame.surfarray.make_surface(frame_rgb)

            # Draw the camera feed as the background
            self.screen.blit(frame_pygame, (0, 0))

            # Draw the cockroach panel x,yon top of the camera feed
            for cockroach in self.cockroaches:
                cockroach.draw(self.screen)

            # Draw the cockroach panel on top of the camera feed
            for asteroid in self.asteroids:
                asteroid.draw(self.screen)

            # Draw the lives count
            self.draw_text("Lives: " + str(self.lives), (255, 255, 255), SCREEN_WIDTH - 70, 10)

            # Update the display
            pygame.display.flip()

        # Release the captured webcam
        self.cap.release()

        # If the game is over, show "Game Over" message
        if self.game_over:
            self.screen.fill((0, 0, 0))
            self.draw_text("Game Over", (255, 0, 0), SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
            pygame.display.flip()
            # Wait for a few seconds before stopping the game
            pygame.time.wait(2000)

        # Destroy all CV2 Windows
        cv2.destroyAllWindows()

    def loop(self) -> None:
        # Start the webcam feed and cockroach panel in separate threads
        cockroach_thread = threading.Thread(target=self.start_game)
        cockroach_thread.start()

        while self.running:
            # Handle events for the Pygame window
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    # Close the Pygame window
                    self.running = False

            # Quit if 'q' is pressed
            if cv2.waitKey(1) & 0xFF == ord('q'):
                self.running = False

        # Signal the camera feed thread to stop
        self.stop_event.set()

if __name__ == '__main__':
    game = MyGame()

    # Start the webcam feed and cockroach panel together
    game.loop()