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
|
DEGREES_TO_RADIANS = Math::PI / 180
class Block
def initialize(x, y, block_size, rotation)
@x = x
@y = y
@block_size = block_size
@rotation = rotation
#The repel velocity?
@velocity = {x: 2, y: 0}
horizontal_offset = (3 * block_size) * Math.cos(rotation * DEGREES_TO_RADIANS)
vertical_offset = block_size * Math.sin(rotation * DEGREES_TO_RADIANS)
if rotation >= 0
theta = 90 - rotation
#The line doesn't visually line up exactly with the edge of the sprite, so artificially move it a bit
modifier = 5
x_offset = modifier * Math.cos(theta * DEGREES_TO_RADIANS)
y_offset = modifier * Math.sin(theta * DEGREES_TO_RADIANS)
@x1 = @x - x_offset
@y1 = @y + y_offset
@x2 = @x1 + horizontal_offset
@y2 = @y1 + (vertical_offset * 3)
@imaginary_line = [ @x1, @y1, @x2, @y2 ]
else
theta = 90 + rotation
x_offset = @block_size * Math.cos(theta * DEGREES_TO_RADIANS)
y_offset = @block_size * Math.sin(theta * DEGREES_TO_RADIANS)
@x1 = @x + x_offset
@y1 = @y + y_offset + 19
@x2 = @x1 + horizontal_offset
@y2 = @y1 + (vertical_offset * 3)
@imaginary_line = [ @x1, @y1, @x2, @y2 ]
end
end
def draw args
args.outputs.sprites << [
@x,
@y,
@block_size*3,
@block_size,
"sprites/square-green.png",
@rotation
]
args.outputs.lines << @imaginary_line
args.outputs.solids << @debug_shape
end
def multiply_matricies
end
def calc args
if collision? args
collide args
end
end
#Determine if the ball and block are touching
def collision? args
#The minimum area enclosed by the center of the ball and the 2 corners of the block
#If the area ever drops below this value, we know there is a collision
min_area = ((@block_size * 3) * args.state.ball.radius) / 2
#https://www.mathopenref.com/coordtrianglearea.html
ax = @x1
ay = @y1
bx = @x2
by = @y2
cx = args.state.ball.center.x
cy = args.state.ball.center.y
current_area = (ax*(by-cy)+bx*(cy-ay)+cx*(ay-by))/2
collision = false
if @rotation >= 0
if (current_area < min_area &&
current_area > 0 &&
args.state.ball.center.y > @y1 &&
args.state.ball.center.x < @x2)
collision = true
end
else
if (current_area < min_area &&
current_area > 0 &&
args.state.ball.center.y > @y2 &&
args.state.ball.center.x > @x1)
collision = true
end
end
return collision
end
def collide args
#Slope of the block
slope = (@y2 - @y1) / (@x2 - @x1)
#Create a unit vector and tilt it (@rotation) number of degrees
x = -Math.cos(@rotation * DEGREES_TO_RADIANS)
y = Math.sin(@rotation * DEGREES_TO_RADIANS)
#Find the vector that is perpendicular to the slope
perpVect = { x: x, y: y }
mag = (perpVect.x**2 + perpVect.y**2)**0.5 # find the magniude of the perpVect
perpVect = {x: perpVect.x/(mag), y: perpVect.y/(mag)} # divide the perpVect by the magniude to make it a unit vector
previousPosition = { # calculate an ESTIMATE of the previousPosition of the ball
x:args.state.ball.center.x-args.state.ball.velocity.x,
y:args.state.ball.center.y-args.state.ball.velocity.y
}
velocityMag = (args.state.ball.velocity.x**2 + args.state.ball.velocity.y**2)**0.5 # the current velocity magnitude of the ball
theta_ball = Math.atan2(args.state.ball.velocity.y, args.state.ball.velocity.x) #the angle of the ball's velocity
theta_repel = (180 * DEGREES_TO_RADIANS) - theta_ball + (@rotation * DEGREES_TO_RADIANS)
fbx = velocityMag * Math.cos(theta_ball) #the x component of the ball's velocity
fby = velocityMag * Math.sin(theta_ball) #the y component of the ball's velocity
frx = velocityMag * Math.cos(theta_repel) #the x component of the repel's velocity | magnitude is set to twice of fbx
fry = velocityMag * Math.sin(theta_repel) #the y component of the repel's velocity | magnitude is set to twice of fby
args.state.display_value = velocityMag
fsumx = fbx+frx #sum of x forces
fsumy = fby+fry #sum of y forces
fr = velocityMag #fr is the resulting magnitude
thetaNew = Math.atan2(fsumy, fsumx) #thetaNew is the resulting angle
xnew = fr*Math.cos(thetaNew) #resulting x velocity
ynew = fr*Math.sin(thetaNew) #resulting y velocity
dampener = 0.3
ynew *= dampener * 0.5
#If the bounce is very low, that means the ball is rolling and we don't want to dampenen the X velocity
if ynew > -0.1
xnew *= dampener
end
#Add the sine component of gravity back in (X component)
gravity_x = 4 * Math.sin(@rotation * DEGREES_TO_RADIANS)
xnew += gravity_x
args.state.ball.velocity.x = -xnew
args.state.ball.velocity.y = -ynew
#Set the position of the ball to the previous position so it doesn't warp throught the block
args.state.ball.center.x = previousPosition.x
args.state.ball.center.y = previousPosition.y
end
end
|