首页 > 解决方案 > Elixir:循环遍历地图,将其与自身进行比较并更新它

问题描述

我有一张台球桌的地图,如下:

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}
}

我需要对它应用碰撞。为了使其正常工作,必须将每个球与其他球进行检查,并且必须在检查时更新球的位置。在我之前的尝试中,我制作了一个“碰撞图”,然后在之后应用了新的位置,但这对随后的碰撞有问题。

所以我想做的是

我想出的代码是这样的:

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)

现在这不起作用,因为我得到了一个BadMapError(我假设与accelse 上的 s 有关),但我也很肯定这将导致列表中的列表。

我对 Elixir 还是很陌生,我不确定我还能做什么。我还有什么可以尝试的吗?是否有非枚举方法(递归算吗?)?

编辑:这是其他正在使用的功能:

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

Circle和功能请Vector参考这里

标签: elixir

解决方案


推荐阅读