使用圆形数学进行实体碰撞

Using circle math for solid collision

    function distance(x1, y1, x2, y2) {
            var x = x1 - x2;
            var y = y1 - y2;
            return(Math.sqrt((x*x) + (y*y)))   
    };

    function collisionCirc(circ1, circ2) {
            var d = distance(circ1.x, circ1.y, circ2.x, circ2.y);
            var r = circ1.radius + circ2.radius;
            return(r > d);
    };

    function collisionCircPoint(circ1, circ2) {
            var cx = ((circ1.x * circ2.radius) + (circ2.x * circ1.radius)) / (circ1.radius + circ2.radius);
            var cy = ((circ1.y * circ2.radius) + (circ2.y * circ1.radius)) / (circ1.radius + circ2.radius);
            var p = [cx, cy];
            return p;
    };

    function angleDegrees(x1, y1, x2, y2) {
            return (Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI) + 180;
    };

    function updateCollisions() {
            var a;
            var p;

            Player.hitArea = new PIXI.Circle(Player.sprite.x, Player.sprite.y, 20);
            MapObjects.chest.hitArea = new PIXI.Circle(MapObjects.chest.x, MapObjects.chest.y, 20);

            if (collisionCirc(Player.hitArea, MapObjects.chest.hitArea)) {
                a = angleDegrees(Player.sprite.x, Player.sprite.y, MapObjects.chest.x, MapObjects.chest.y);
                p = collisionCircPoint(Player.hitArea, MapObjects.chest.hitArea);
                        Player.sprite.x = p[0];
                        Player.sprite.y = p[1];
            };

    };

我在地图上有 2 个精灵,每个精灵都定义了一个圆形 hitArea。我正在尝试进行玩家无法通过的平滑圆形碰撞。我以为我可以将 Player.sprite 的坐标设置为碰撞点,但它只是将他扭曲到 MapObjects.chest 的坐标,即使碰撞点是正确的并且距离碰撞点 20 像素MapObject.chest的中心。我做错了什么或者需要什么更多的信息来创建碰撞,就像 JavaScript 物理库,我可以在其中围绕一个圆形对象旋转?

碰撞点在玩家和障碍物之间。如果将玩家移向碰撞点,实际上是在将玩家移近。例如,如果玩家和障碍物之间正好有 40 px (r1+r2),那么碰撞点就在他们之间,距离障碍物只有 20 px!

当您有多个对象时,很难在碰撞已经发生时正确处理。如果附近只有一个障碍物,您只需将玩家直接移离障碍物即可。但是,这样玩家实际上可能会陷入另一个障碍物中。

另一个解决办法是回到起点,尝试小幅度的运动,直到没有碰撞。这样你最终会做对,但这也可能很慢。

数学上正确的解决方案是计算碰撞发生前移动的最大距离。这是通过求解以下向量方程来完成的:

# p = player position before moving
# o = obstacle position
# u = player direction (unit vector)
# d = distance to move
distance(o, p + d * u) = o.radius + p.radius

那是数学,你可以自己解决,也可以使用 Wolfram Alpha 之类的工具解决。

求解该方程式将为您提供零个、一个或两个可能的距离值。您可以忽略负值,因为它们意味着玩家已经越过了障碍。如果你只得到一个值,这意味着玩家只会擦过障碍物,你也可以忽略它。两个值表示碰撞发生在这些距离之间;较小的值是碰撞开始的地方,较大的值是玩家已经穿过障碍物的地方。另外,如果一个值为正,另一个为负,则意味着玩家已经在障碍物内,这种情况永远不会发生。

你应该 运行 检查附近的所有障碍物,然后根据最小的 non-negative 结果(即零或正)或更小的结果移动玩家,如果玩家不能'没那么快。

最后,为了围绕一个圆形物体转圈,你可以在碰撞后将玩家沿垂直方向移动一点(向左或向右,取决于玩家将通过障碍物的哪一侧),如果这不会引起任何新的碰撞。

还有许多其他可能的实现方式。

        Player.hitArea = new PIXI.Circle(Player.sprite.x, Player.sprite.y, 20);
        MapObjects.chest.hitArea = new PIXI.Circle(MapObjects.chest.x, MapObjects.chest.y, 20);
        if (collisionCirc(Player.hitArea, MapObjects.chest.hitArea)) {
            p = collisionCircPoint(Player.hitArea, MapObjects.chest.hitArea);
            a = angleDegrees(Player.sprite.x, Player.sprite.y, MapObjects.chest.x, MapObjects.chest.y);
                if (Player.sprite.x - MapObjects.chest.x > 0) {
                    Player.sprite.x += 1;
                } else  if (Player.sprite.x + MapObjects.chest.x > 0) {
                    Player.sprite.x -= 1;
                };
                if (Player.sprite.y - MapObjects.chest.y > 0) {
                    Player.sprite.y += 1;
                } else  if (Player.sprite.y + MapObjects.chest.y > 0) {
                    Player.sprite.y -= 1;
                };
            };
        };

我补充说,它实际上工作得很好,只是当 运行 以特定角度进入 MapObjects.chest 的命中区域时玩家速度稍微过快。稍后处理。