-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathskeet.py
253 lines (196 loc) · 7.32 KB
/
skeet.py
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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
"""
File: skeet.py
Original Author: Br. Burton
Designed to be completed by others
This program implements an awesome version of skeet.
"""
import arcade
import time
import math
import random
from skeet_classes import (Point, Bullet, Target, Strong_Target, Safe_Target)
# These are Global constants to use throughout the game
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 500
RIFLE_WIDTH = 100
RIFLE_HEIGHT = 20
RIFLE_COLOR = arcade.color.DARK_RED
BULLET_RADIUS = 3
BULLET_COLOR = arcade.color.BLACK_OLIVE
BULLET_SPEED = 10
TARGET_RADIUS = 20
TARGET_COLOR = arcade.color.CARROT_ORANGE
TARGET_STRONG_LIVES = 3
TARGET_SAFE_COLOR = arcade.color.AIR_FORCE_BLUE
TARGET_SAFE_RADIUS = 15
# Global constants for explosions
EXPLOSION_RADIUS = 30
EXPLOSION_PART1_COLOR = arcade.color.RED
EXPLOSION_PART2_COLOR = arcade.color.YELLOW
EXPLOSION_WAIT_TIME = .25
class Rifle:
"""
The rifle is a rectangle that tracks the mouse.
"""
def __init__(self):
self.center = Point()
self.center.x = 0
self.center.y = 0
self.angle = 45
def draw(self):
arcade.draw_rectangle_filled(self.center.x, self.center.y, RIFLE_WIDTH, RIFLE_HEIGHT, RIFLE_COLOR, 360-self.angle)
class Game(arcade.Window):
"""
This class handles all the game callbacks and interaction
It assumes the following classes exist:
Rifle
Target (and it's sub-classes)
Point
Velocity
Bullet
This class will then call the appropriate functions of
each of the above classes.
You are welcome to modify anything in this class, but mostly
you shouldn't have to. There are a few sections that you
must add code to.
"""
def __init__(self, width, height):
"""
Sets up the initial conditions of the game
:param width: Screen width
:param height: Screen height
"""
super().__init__(width, height)
self.rifle = Rifle()
self.score = 0
self.bullets = []
self.targets = []
# List for explosions from a target being destroyed
self.explosions = []
arcade.set_background_color(arcade.color.WHITE)
def on_draw(self):
"""
Called automatically by the arcade framework.
Handles the responsibility of drawing all elements.
"""
# clear the screen to begin drawing
arcade.start_render()
# draw each object
self.rifle.draw()
for bullet in self.bullets:
bullet.draw()
for target in self.targets:
target.draw()
self.draw_score()
# Draws explosions when target is hit
for explosion in self.explosions:
explosion.draw()
def draw_score(self):
"""
Puts the current score on the screen
"""
score_text = "Score: {}".format(self.score)
start_x = 10
start_y = SCREEN_HEIGHT - 20
arcade.draw_text(score_text, start_x=start_x, start_y=start_y, font_size=12, color=arcade.color.NAVY_BLUE)
def update(self, delta_time):
"""
Update each object in the game.
:param delta_time: tells us how much time has actually elapsed
"""
self.check_collisions()
self.check_off_screen()
# decide if we should start a target
if random.randint(1, 50) == 1:
self.create_target()
for bullet in self.bullets:
bullet.advance()
for target in self.targets:
target.advance()
# Decides if we should get rid of an explosion
for explosion in self.explosions:
if time.time() - explosion.start_time >= EXPLOSION_WAIT_TIME:
self.explosions.remove(explosion)
def create_target(self):
"""
Creates a new target of a random type and adds it to the list.
:return:
"""
# Generates random number between 1 and 3 to determine which target to generate.
target_number = random.randint(1, 3)
if target_number == 1:
self.targets.append(Target())
elif target_number == 2:
self.targets.append(Strong_Target())
else:
self.targets.append(Safe_Target())
def check_collisions(self):
"""
Checks to see if bullets have hit targets.
Updates scores and removes dead items.
:return:
"""
# NOTE: This assumes you named your targets list "targets"
for bullet in self.bullets:
for target in self.targets:
# Make sure they are both alive before checking for a collision
if bullet.alive and target.alive:
too_close = bullet.radius + target.radius
if (abs(bullet.center.x - target.center.x) < too_close and
abs(bullet.center.y - target.center.y) < too_close):
# its a hit!
bullet.alive = False
self.score += target.hit()
# We will wait to remove the dead objects until after we
# finish going through the list
# Now, check for anything that is dead, and remove it
self.cleanup_zombies()
def cleanup_zombies(self):
"""
Removes any dead bullets or targets from the list. Also creates explosion.
:return:
"""
for bullet in self.bullets:
if not bullet.alive:
self.bullets.remove(bullet)
for target in self.targets:
if not target.alive:
self.targets.remove(target)
# Above and beyond method for explosion
self.explosions.append(target.explode())
def check_off_screen(self):
"""
Checks to see if bullets or targets have left the screen
and if so, removes them from their lists.
:return:
"""
for bullet in self.bullets:
if bullet.is_off_screen(SCREEN_WIDTH, SCREEN_HEIGHT):
self.bullets.remove(bullet)
for target in self.targets:
if target.is_off_screen(SCREEN_WIDTH, SCREEN_HEIGHT):
self.targets.remove(target)
def on_mouse_motion(self, x: float, y: float, dx: float, dy: float):
# set the rifle angle in degrees
self.rifle.angle = self._get_angle_degrees(x, y)
def on_mouse_press(self, x: float, y: float, button: int, modifiers: int):
# Fire!
angle = self._get_angle_degrees(x, y)
bullet = Bullet()
bullet.fire(angle)
self.bullets.append(bullet)
def _get_angle_degrees(self, x, y):
"""
Gets the value of an angle (in degrees) defined
by the provided x and y.
Note: This could be a static method, but we haven't
discussed them yet...
"""
# get the angle in radians
angle_radians = math.atan2(y, x)
# convert to degrees
angle_degrees = math.degrees(angle_radians)
return angle_degrees
# Creates the game and starts it going
window = Game(SCREEN_WIDTH, SCREEN_HEIGHT)
arcade.run()