-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdrawn_object.py
219 lines (166 loc) · 7.08 KB
/
drawn_object.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
from proj import *
from mat2 import *
from vec2 import *
class Drawn_Object:
drawer = Turtle()
drawer.hideturtle()
drawer.pencolor('WHITE')
# Initialize the object
def __init__(self, collidable = True):
self.num_vertices = len(self.vertices)
if collidable:
self.normal_vectors = self._map(range(self.num_vertices), self._get_normal_vectors)
self.vertex_distances = self._map(range(self.num_vertices), self._get_vertex_distances)
self.vertex_distances.sort(key=lambda dist: dist[1], reverse=True)
# I just wrote this myself for the sake of it
def _map(self, values, mapper):
accumulator = []
for value in values:
accumulator.append(mapper(value))
return accumulator
# Find normals of all sides to be used for collision checking
def _get_normal_vectors(self, i):
next_i = i + 1
if i == self.num_vertices - 1:
next_i = 0
temp = self.vertices[next_i] - self.vertices[i]
temp = self.vertices[next_i] - self.vertices[i]
return temp.get_normal()
# Get distance from center to each vertex to be used to determine if collision
# needs to be checked
def _get_vertex_distances(self, i):
return (i, (self.center - self.vertices[i]).get_magnitude())
# Draws the object
def draw_object(self, enclosed=True):
# Draw all edges aside from the last
for i in range(self.num_vertices - 1):
self.draw_line(self.vertices[i], self.vertices[i + 1])
# Draw the closing edge if needed
if enclosed:
self.draw_line(self.vertices[-1], self.vertices[0])
# Draw a line between two vertices
def draw_line(self, start, end):
self.drawer.penup()
self.drawer.goto(start.x, start.y)
self.drawer.pendown()
self.drawer.goto(end.x, end.y)
def move_object(self):
vertices_out = 0
# Move the vertices and the center of the object
for vertex in self.vertices:
vertex += self.velocity
# Check for edge clipping
vertices_out += self.check_edge(vertex)
self.center += self.velocity
# If all the vertices are off the screen move the object
if vertices_out == self.num_vertices:
if self.center.x < 0:
self._move_to_opposite('x', 1)
else:
self._move_to_opposite('x', -1)
elif vertices_out == -self.num_vertices:
if self.center.y < 0:
self._move_to_opposite('y', 1)
else:
self._move_to_opposite('y', -1)
def rotate_object(self, dir):
if dir == 'd':
spin_dir = -360
else:
spin_dir = 360
rotation = radians(spin_dir * frame)
rot_matrix = Mat2(cos(rotation), sin(rotation),
-sin(rotation), cos(rotation))
for vertex in self.vertices:
vertex -= self.center
vertex *= rot_matrix
vertex += self.center
# If the object rotated its normals must be recalculated
self.normal_vectors = self._map(range(self.num_vertices),
self._get_normal_vectors)
# Add velocity to the object
def accelerate_object(self):
self.velocity += ((self.vertices[1] - self.center) * 2) * frame
self.velocity.clamp_magnitude(8)
# Remove velocity from the object
def decelerate_object(self):
current_speed = self.velocity.get_magnitude()
if 0 <= current_speed <= (4 * frame):
self.velocity = Vec2(0, 0)
else:
self.velocity.clamp_magnitude(current_speed - (4 * frame))
# Check if the vertex has gone off the screen
def check_edge(self, vertex):
if abs(vertex.x) >= width:
return 1
if abs(vertex.y) >= height:
return -1
return 0
# Move the object to the other side of the screen if it has clipped
def _move_to_opposite(self, axis, direction):
# If it clipped off the x axis flip it there
if axis == 'x':
move = Vec2((width * 2 + 30) * direction, 0)
# Otherwise flip it on the y
else:
move = Vec2(0, (height * 2 + 30) * direction)
# Add the flipping vector to the vertices
for vertex in self.vertices:
vertex += move
# Move the center as well
self.center += move
# Check for continuous collision
def continuous_collision_check(self, collidables):
# Store current position for later
temp_vertices = [Vec2(self.vertices[0].x, self.vertices[0].y),
Vec2(self.vertices[1].x, self.vertices[1].y)]
# Basically extrude the bullet to its location next frame
self.vertices[0] += self.velocity
self.vertices[1] += self.velocity
collision_data = self.collision_testing(collidables)
self.vertices[0] = temp_vertices[0]
self.vertices[1] = temp_vertices[1]
return collision_data
# Check for collision with collidables
def collision_testing(self, collidables):
# Loop through all possible collidables
for i in range(len(collidables)):
# Loop through all of this object's vertices
for vertex in self.vertices:
# If they are close enough they may be colliding check for collision
if self._in_collision_distance(collidables[i], vertex):
if self._run_collision_test(collidables[i]):
return [True, i]
return [False, None]
# Check if there is any possibility of collision
def _in_collision_distance(self, collidable, vertex):
# See if the collider vertex is closer to the collidable center than the collidable vertex
if (collidable.center - vertex).get_magnitude() <= collidable.vertex_distances[0][1]:
return True
return False
def _run_collision_test(self, collidable):
# Get all the axes we need to check
axes = self.normal_vectors + collidable.normal_vectors
# Loop through the axes
for axis in axes:
# Get the min and max projections on the axis
s_min, s_max = self._get_projections(axis)
c_min, c_max = collidable._get_projections(axis)
# If the projections do not overlap return False
if (s_min < c_min and s_max < c_min) or (s_max > c_max and s_min > c_max):
return False
# Return True if no non overlapping axis was found
return True
def _get_projections(self, axis):
# Get placeholders for projections
min_proj = self.vertices[0].dot_product(axis)
max_proj = min_proj
# Loop through all vertices finding min and max projections along axis
for vertex in self.vertices[1:]:
dot = vertex.dot_product(axis)
if dot < min_proj:
min_proj = dot
elif dot > max_proj:
max_proj = dot
# Return projections
return min_proj, max_proj