forked from MTN-RowinAndruscavage/STEM4T-Display-ESP32
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path_g_roids.py
358 lines (291 loc) · 12.2 KB
/
_g_roids.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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
"""
roids.py - Asteroids style game demo using polygons.
"""
import math
import random
import utime
import micropython
import st7789
import tft_config
from machine import Pin
tft = tft_config.config(1, buffer_size=64*64*2)
btn1=Pin(0, mode=Pin.IN, pull=Pin.PULL_UP)
btn2=Pin(35, mode=Pin.IN, pull=Pin.PULL_UP)
def main():
'''
Game on!
'''
class Poly():
'''
Poly class to keep track of a polygon based sprite
'''
def __init__(
self,
# list (x,y) tuples of convex polygon, must be closed
polygon,
x=None, # x location of polygon
y=None, # y location of polygon
v_x=None, # velocity in x axis
v_y=None, # velocity in y axis
angle=None, # angle in radians polygon is facing
spin=None, # spin in radians per frame_time
scale=None, # scale factor for polygon
radius=None, # radius of polygon for collision detection
max_velocity=10, # max velocity of polygon
counter=0):
# scale the polygon if scale was given
self.polygon = (
polygon if scale is None else [(int(scale*x[0]), int(scale*x[1])) for x in polygon])
# if no location given assign a random location
self.x = random.randint(0, width) if x is None else x
self.y = random.randint(0, width) if y is None else y
# set angle if given
self.angle = float(0) if angle is None else angle
# set random spin unless one was given
self.spin = random.randint(-3, 3) / 16 if spin is None else spin
# set random velocity unless one was given
self.velocity_x = random.uniform(
0.50, 0.99)*6-3 + 0.75 if v_x is None else v_x
self.velocity_y = random.uniform(
0.50, 0.99)*6-3 + 0.75 if v_y is None else v_y
# set radius, max_velocity and radius counter
self.radius = radius
self.max_velocity = max_velocity
self.counter = counter
def rotate(self, rad):
'''
Rotate polygon in radians
'''
self.angle += rad
if self.angle > rad_max:
self.angle = 0
elif self.angle < 0:
self.angle = rad_max
def move(self):
'''
Rotate and move polygon velocity distance.
'''
if self.spin != 0:
self.rotate(self.spin)
self.x += int(self.velocity_x)
self.y += int(self.velocity_y)
self.x %= width
self.y %= height
def draw(self, color):
'''
Draw the polygon
'''
tft.polygon(self.polygon, self.x, self.y, color, self.angle, 0, 0)
def collision(self, poly):
'''
Detect collisions using overlapping radiuses.
Returns True on collision.
'''
return abs(
((self.x - poly.x) * (self.x - poly.x) +
(self.y - poly.y) * (self.y - poly.y))
< (self.radius + poly.radius) * (self.radius + poly.radius))
def create_roid(size, x=None, y=None, v_x=None, v_y=None):
'''
Create a new roid with the given parameters.
'''
return Poly(
roid_poly,
x=x,
y=y,
v_x=v_x,
v_y=v_y,
scale=roid_scale[size],
radius=roid_radius[size],
counter=size)
def update_missiles():
'''
Update active missiles and handle asteroid hits.
'''
for missile in list(missiles): # for each missile
missile.draw(st7789.BLACK) # erase old missile
if missile.counter > 0: # if counter > 0 missile is active
missile.move() # update missile position
for roid in list(roids): # for each roid
if missile.collision(roid): # check if missile collides with roid
roid.draw(st7789.BLACK) # erase the roid
if roid.counter > 0: # if roid is not the smallest size
roids.append( # add first smaller roid
create_roid(
roid.counter-1,
x=roid.x,
y=roid.y,
v_x=roid.velocity_x,
v_y=roid.velocity_y))
roids.append( # add second smaller roid
create_roid(
roid.counter-1,
x=roid.x,
y=roid.y,
v_x=-roid.velocity_x,
v_y=-roid.velocity_y))
roids.remove(roid) # remove the roid that was hit
missile.counter = 0
if missile.counter > 0: # if the missile has life left
missile.draw(st7789.CYAN) # draw missile
missile.counter -= 1 # reduce missile life
else:
missiles.remove(missile) # remove exploded missile
else:
missiles.remove(missile) # remove expired missile
def update_ship():
'''
Update ship velocity and limit to max_velocity
'''
# apply drag to velocity of ship so it will eventually slow to a stop
ship.velocity_x -= ship.velocity_x * ship_drag_frame
ship.velocity_y -= ship.velocity_y * ship_drag_frame
if ship.velocity_x > ship.max_velocity: # Limit ship velocity to +/- max_velocity
ship.velocity_x = ship.max_velocity
elif ship.velocity_x < -ship.max_velocity:
ship.velocity_x = -ship.max_velocity
if ship.velocity_y > ship.max_velocity:
ship.velocity_y = ship.max_velocity
elif ship.velocity_y < -ship.max_velocity:
ship.velocity_y = -ship.max_velocity
if abs(ship.velocity_x) < 0.1: # if ship is moving very slowly, stop it
ship.velocity_x = 0.0
if abs(ship.velocity_y) < 0.1:
ship.velocity_y = 0.0
ship.move() # move the ship and draw it
ship.draw(TMOMAGENTA)
def update_roids():
'''
Update roid positions handle ship collisions
Returns True if not hit, False if hit
'''
not_hit = True
for roid in roids: # for each roid, erase, move then draw
roid.draw(st7789.BLACK)
roid.move()
roid.draw(st7789.WHITE)
if roid.collision(ship): # check for roid/ship collision
ship.draw(st7789.BLACK) # erase ship
ship.velocity_x = 0.0 # stop movement
ship.velocity_y = 0.0
not_hit = False
return not_hit
def explode_ship():
'''
Increment explosion step and alternate between drawing
explosion poly and explosion poly rotated 45 degrees
Returns True when explosion is finished
'''
ship.counter += 1
if ship.counter % 2 == 0:
tft.polygon(explosion_poly, ship.x, ship.y, st7789.BLACK, 0.785398)
tft.polygon(explosion_poly, ship.x, ship.y, st7789.RED)
else:
tft.polygon(explosion_poly, ship.x, ship.y, st7789.WHITE, 0.785398)
tft.polygon(explosion_poly, ship.x, ship.y, st7789.BLACK)
if ship.counter > 25:
# erase explosion, move ship to center and stop explosion
tft.polygon(explosion_poly, ship.x, ship.y, st7789.BLACK)
# move ship to center
ship.x = width >> 1
ship.y = height >> 1
ship.counter = 0
return True
return False
# enable display and clear screen
tft.init()
tft.fill(st7789.BLACK)
width = tft.width()
height = tft.height()
rad_max = 2 * math.pi # 360' in radians
# ship variables
ship_alive = True
ship_radius = 7
ship_rad_frame = rad_max / 16 # turning rate per frame
ship_accel_frame = 0.6 # acceleration per frame
ship_drag_frame = 0.015 # drag factor per frame
ship_poly = [(-7, -7), (7, 0), (-7, 7), (-3, 0), (-7, -7)]
ship = Poly(
ship_poly,
x=width >> 1,
y=height >> 1,
v_x=0,
v_y=0,
radius=ship_radius,
spin=0.0)
explosion_poly = [(-4, -4), (-4, 4), (4, 4), (4, -4), (-4, -4)]
# asteroid variables
roid_radius = [5, 10, 16]
roid_scale = [0.33, 0.66, 1.0]
roid_poly = [
(-5, -15), (-2, -13), (11, -14), (15, -7), (14, 0),
(16, 5), (11, 16), (7, 16), (-7, 14), (-14, 7),
(-13, 1), (-14, -8), (-11, -15), (-5, -15)]
roids = []
# missile variables
missile_velocity = 8
missile_max = 8
missile_life = 20
missile_rate = 200
missile_last = utime.ticks_ms()
missile_poly = [(-1, -1), (1, -1), (1, 1), (-1, 1), (-1, -1)]
missiles = []
frame_time = 60 # target frame rate delay
# game loop
while True:
last_frame = utime.ticks_ms()
# add roids if there are none
if len(roids) == 0:
roids = [create_roid(2), create_roid(2)]
update_missiles()
# Erase the ship
ship.draw(st7789.BLACK)
if ship_alive:
# if left button pressed
if btn1.value() == Pin.DRIVE_0 and btn2.value() == Pin.DRIVE_1:
# rotate ship counter clockwise
ship.rotate(-ship_rad_frame)
# if right button pressed
if btn2.value() == Pin.DRIVE_0 and btn1.value() == Pin.DRIVE_1:
# rotate ship clockwise
ship.rotate(ship_rad_frame)
# if both buttons pressed
if btn1.value() == Pin.DRIVE_0 and btn2.value() == Pin.DRIVE_0:
# accelerate ship in the direction the ship is facing
d_y = math.sin(ship.angle) * ship_accel_frame
d_x = math.cos(ship.angle) * ship_accel_frame
ship.velocity_x += d_x
ship.velocity_y += d_y
# spam missiles continuously
if len(missiles) < missile_max:
# limit missiles firing to once every missile_rate ms
if last_frame - missile_last > missile_rate:
# fire missile in direction ship in facing
v_y = math.sin(ship.angle) * missile_velocity
v_x = math.cos(ship.angle) * missile_velocity
# create new missile
missile = Poly(
missile_poly,
x=ship.x,
y=ship.y,
v_x=v_x,
v_y=v_y,
angle=ship.angle,
radius=1,
spin=0.0,
counter=missile_life)
# add to to missile list and save last fire time
missiles.append(missile)
missile_last = last_frame
update_ship()
else:
# explosion animation until returns True
ship_alive = explode_ship()
# update roids and return if ship was not hit
not_hit = update_roids()
if ship_alive:
ship_alive = not_hit
# wait until frame time expires
while utime.ticks_ms() - last_frame < frame_time:
pass
main()