I have a map of a billiard ball table, as follows:
ball_map = %{
"cue" => {"x":-15.0, "z": 0.0, "velocity_x": 0.0, "velocity_z": 0.0, "is_idle": true},
"ball_1" => {"x":15.0, "z": 0.0, "velocity_x": 0.0, "velocity_z": 0.0, "is_idle": true},
"ball_2" => {"x":17.0, "z": 1.1, "velocity_x": 0.0, "velocity_z": 0.0, "is_idle": true},
"ball_3" => {"x":17.0, "z": -1.1, "velocity_x": 0.0, "velocity_z": 0.0, "is_idle": true}
}
I need to apply collisions to it. For that to work properly, each ball must be checked against the others, and the ball locations must be updated as it checks. In my previous attempts, I made a "collision map" and then applied the new locations after, but this had problems with subsequent collisions.
So what I'm trying to do is
- Loop through each item in the map.
- Compare that item with every other item, and run a check for collision on it (I have a function for that).
- Update both items upon collision, while keeping the format of
ball_map
.
The code I've come up with for that is this:
collision_result = Enum.reduce(ball_map, fn {ball_a, body_a}, acc ->
new_result = Enum.map(ball_map, fn {ball_b, body_b} ->
has_collided = checkStaticCollisions(ball_a, body_a, ball_b, body_b, const_physics["billiard_ball_radius"])
# Takes in parameters (Key of Ball A, Value of Ball A, Key of Ball B, Value of Ball B, radius)
# Returns true or false depending if Ball A != Ball B and collisions happen
if has_collided == true do
calc = calculatePosAfterCollision(ball_a, body_a, ball_b, body_b, const_physics["billiard_ball_radius"])
# Takes in parameters (Key of Ball A, Value of Ball A, Key of Ball B, Value of Ball B, radius)
# Returns {"id_of_ball_a" => {new X and Z pos}, "id_of_ball_b" => {new X and Z pos}}
new_a_pos = calc[ball_a]
new_b_pos = calc[ball_b]
new_a = %{"x" => new_a_pos["x"], "z" => new_a_pos["z"], "velocity_x" => body_a["velocity_x"], "velocity_z" => body_a["velocity_z"], "is_idle" => body_a["is_idle"]}
new_b = %{"x" => new_b_pos["x"], "z" => new_b_pos["z"], "velocity_x" => body_b["velocity_x"], "velocity_z" => body_b["velocity_z"], "is_idle" => body_b["is_idle"]}
# Supposedly, Update both ball A and ball B, keep the rest
Map.put(acc, ball_a, new_a) #?
Map.put(acc, ball_b, new_b) #?
else
# Supposedly, No change for ball A or ball B
%{ball_a => body_a, ball_b => body_b}
end
end)
# Not sure what to output here, if any
end)
Now this doesn't work, as I'm getting a BadMapError
(I assume something to do with the acc
s on else's), but I'm also positive this will result in a list within a list.
I'm still rather new to Elixir, and I'm not sure what else I can do. Is there anything else I can try to make this work? Are there non enum-approaches (does recursion count?)?
EDIT: here are the other functions in-use:
def checkStaticCollisions(key_a, body_a, key_b, body_b, ball_radius) do
if (key_a == key_b) do
false
else
pos_a = PGS.Vector.new(body_a["x"], 0.0, body_a["z"])
pos_b = PGS.Vector.new(body_b["x"], 0.0, body_b["z"])
circle_a = PGS.Circle.new(pos_a, ball_radius)
circle_b = PGS.Circle.new(pos_b, ball_radius)
checkCircleCollision(circle_a, circle_b)
end
end
def checkCircleCollision(circleA, circleB) do
posA = circleA.position
posB = circleB.position
radiusA = circleA.radius
radiusB = circleB.radius
distance = PGS.Vector.distance(posA, posB)
circleDistance = radiusA + radiusB
if distance < circleDistance do
# collision occured
true
else
false
end
end
def calculatePosAfterCollision(id_a, ball_a, id_b, ball_b, radius) do
pos_a = PGS.Vector.new(ball_a["x"], 0.0, ball_a["z"])
pos_b = PGS.Vector.new(ball_b["x"], 0.0, ball_b["z"])
radius_a = radius
radius_b = radius
distance = PGS.Vector.distance(pos_a, pos_b)
pos_delta = PGS.Vector.sub(pos_a, pos_b)
overlap_dist = (distance - radius_a - radius_b) * 0.5
vector_difference = PGS.Vector.new(overlap_dist * pos_delta.x / distance, overlap_dist * pos_delta.y / distance, overlap_dist * pos_delta.z / distance)
new_pos_a = PGS.Vector.sub(pos_a, vector_difference)
new_pos_b = PGS.Vector.add(pos_b, vector_difference)
%{id_a => new_pos_a, id_b => new_pos_b}
end
For the Circle
and Vector
functions, please refer to this.