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
|
class Peg
def initialize(x, y, block_size)
@x = x # x cordinate of the LEFT side of the peg
@y = y # y cordinate of the RIGHT side of the peg
@block_size = block_size # diameter of the peg
@radius = @block_size/2.0 # radius of the peg
@center = { # cordinatees of the CENTER of the peg
x: @x+@block_size/2.0,
y: @y+@block_size/2.0
}
@r = 255 # color of the peg
@g = 0
@b = 0
@velocity = {x: 2, y: 0}
end
def draw args
args.outputs.sprites << [ # draw the peg according to the @x, @y, @radius, and the RGB
@x,
@y,
@radius*2.0,
@radius*2.0,
"sprites/circle-white.png",
0,
255,
@r, #r
@g, #g
@b #b
]
end
def calc args
if collisionWithBounce? args # if the is a collision with the bouncing ball
collide args
@r = 0
@b = 0
@g = 255
else
end
end
# do two circles (the ball and this peg) intersect
def collisionWithBounce? args
squareDistance = ( # the squared distance between the ball's center and this peg's center
(args.state.ball.center.x - @center.x) ** 2.0 +
(args.state.ball.center.y - @center.y) ** 2.0
)
radiusSum = ( # the sum of the radius squared of the this peg and the ball
(args.state.ball.radius + @radius) ** 2.0
)
# if the squareDistance is less or equal to radiusSum, then there is a radial intersection between the ball and this peg
return (squareDistance <= radiusSum)
end
# ! The following links explain the getRepelMagnitude function !
# https://raw.githubusercontent.com/DragonRuby/dragonruby-game-toolkit-physics/master/docs/docImages/LinearCollider_4.png
# https://raw.githubusercontent.com/DragonRuby/dragonruby-game-toolkit-physics/master/docs/docImages/LinearCollider_5.png
# https://github.com/DragonRuby/dragonruby-game-toolkit-physics/blob/master/docs/LinearCollider.md
def getRepelMagnitude (args, fbx, fby, vrx, vry, ballMag)
a = fbx ; b = vrx ; c = fby
d = vry ; e = ballMag
if b**2 + d**2 == 0
#unexpected
end
x1 = (-a*b+-c*d + (e**2 * b**2 - b**2 * c**2 + 2*a*b*c*d + e**2 + d**2 - a**2 * d**2)**0.5)/(b**2 + d**2)
x2 = -((a*b + c*d + (e**2 * b**2 - b**2 * c**2 + 2*a*b*c*d + e**2 * d**2 - a**2 * d**2)**0.5)/(b**2 + d**2))
err = 0.00001
o = ((fbx + x1*vrx)**2 + (fby + x1*vry)**2 ) ** 0.5
p = ((fbx + x2*vrx)**2 + (fby + x2*vry)**2 ) ** 0.5
r = 0
if (ballMag >= o-err and ballMag <= o+err)
r = x1
elsif (ballMag >= p-err and ballMag <= p+err)
r = x2
else
#unexpected
end
if (args.state.ball.center.x > @center.x)
return x2*-1
end
return x2
#return r
end
#this sets the new velocity of the ball once it has collided with this peg
def collide args
normalOfRCCollision = [ #this is the normal of the collision in COMPONENT FORM
{x: @center.x, y: @center.y}, #see https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.mathscard.co.uk%2Fonline%2Fcircle-coordinate-geometry%2F&psig=AOvVaw2GcD-e2-nJR_IUKpw3hO98&ust=1605731315521000&source=images&cd=vfe&ved=0CAIQjRxqFwoTCMjBo7e1iu0CFQAAAAAdAAAAABAD
{x: args.state.ball.center.x, y: args.state.ball.center.y},
]
normalSlope = ( #normalSlope is the slope of normalOfRCCollision
(normalOfRCCollision[1].y - normalOfRCCollision[0].y) /
(normalOfRCCollision[1].x - normalOfRCCollision[0].x)
)
slope = normalSlope**-1.0 * -1 # slope is the slope of the tangent
# args.state.display_value = slope
pointA = { # pointA and pointB are using the var slope to tangent in COMPONENT FORM
x: args.state.ball.center.x-1,
y: -(slope-args.state.ball.center.y)
}
pointB = {
x: args.state.ball.center.x+1,
y: slope+args.state.ball.center.y
}
perpVect = {x: pointB.x - pointA.x, y:pointB.y - pointA.y} # perpVect is to be VECTOR of the perpendicular tangent
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
perpVect = {x: -perpVect.y, y: perpVect.x} # swap the x and y and multiply by -1 to make the vector perpendicular
args.state.display_value = perpVect
if perpVect.y > 0 #ensure perpVect points upward
perpVect = {x: perpVect.x*-1, y: perpVect.y*-1}
end
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
}
yInterc = pointA.y + -slope*pointA.x
if slope == INFINITY # the perpVect presently either points in the correct dirrection or it is 180 degrees off we need to correct this
if previousPosition.x < pointA.x
perpVect = {x: perpVect.x*-1, y: perpVect.y*-1}
yInterc = -INFINITY
end
elsif previousPosition.y < slope*previousPosition.x + yInterc # check if ball is bellow or above the collider to determine if perpVect is - or +
perpVect = {x: perpVect.x*-1, y: perpVect.y*-1}
end
velocityMag = # the current velocity magnitude of the ball
(args.state.ball.velocity.x**2 + args.state.ball.velocity.y**2)**0.5
theta_ball=
Math.atan2(args.state.ball.velocity.y,args.state.ball.velocity.x) #the angle of the ball's velocity
theta_repel=
Math.atan2(args.state.ball.center.y,args.state.ball.center.x) #the angle of the repelling force(perpVect)
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
repelMag = getRepelMagnitude( # the magniude of the collision vector
args,
fbx,
fby,
perpVect.x,
perpVect.y,
(args.state.ball.velocity.x**2 + args.state.ball.velocity.y**2)**0.5
)
frx = repelMag* Math.cos(theta_repel) #the x component of the repel's velocity | magnitude is set to twice of fbx
fry = repelMag* Math.sin(theta_repel) #the y component of the repel's velocity | magnitude is set to twice of fby
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
if (args.state.ball.center.x >= @center.x) # this is necessary for the ball colliding on the right side of the peg
xnew=xnew.abs
end
args.state.ball.velocity.x = xnew # set the x-velocity to the new velocity
if args.state.ball.center.y > @center.y # if the ball is above the middle of the peg we need to temporarily ignore some of the gravity
args.state.ball.velocity.y = ynew + GRAVITY * 0.01
else
args.state.ball.velocity.y = ynew - GRAVITY * 0.01 # if the ball is bellow the middle of the peg we need to temporarily increase the power of the gravity
end
args.state.ball.center.x+= args.state.ball.velocity.x # update the position of the ball so it never looks like the ball is intersecting the circle
args.state.ball.center.y+= args.state.ball.velocity.y
end
end
|