检测线条碰撞并限制运动

时间:2018-11-13 07:02:34

标签: java libgdx 2d collision-detection

我正在用Java中的libGDX制作游戏。我正在尝试进行碰撞检测。如您在图像中看到的,我有一条线是一堵墙和具有指定半径的播放器。所需位置是玩家尝试进入的下一个位置。但是由于有墙,因此他被放置在“速度”矢量上的“实际位置”,但更靠近上一个位置。我正在尝试找出如何检测到更近的位置? enter image description here

我的尝试

private void move(float deltaTime) {
    float step;
    beginMovementAltitude();
    if (playerComponent.isWalking())
        step = handleAcceleration(playerComponent.getSpeed() + playerComponent.getAcceleration());
    else step = handleDeacceleration(playerComponent.getSpeed(), playerComponent.getAcceleration());
    playerComponent.setSpeed(step);
    if (step == 0) return;
    takeStep(deltaTime, step, 0);
}
private void takeStep(float deltaTime, float step, int rotate) {
    Vector3 position = playerComponent.getCamera().position;
    float x = position.x;
    float y = position.y;
    int radius = playerComponent.getRadius();
    auxEnvelope.init(x, x + radius, y, y + radius);
    List<Line> nearbyLines = lines.query(auxEnvelope);
    float theta;
    int numberOfIntersections = 0;
    float angleToMove = 0;
    Gdx.app.log(step + "", "");
    for (Line line : nearbyLines) {
        VertexElement src = line.getSrc();
        VertexElement dst = line.getDst();
        auxVector3.set(playerComponent.getCamera().direction);
        auxVector3.rotate(Vector3.Z, rotate);
        float nextX = x + (step * deltaTime) * (auxVector3.x);
        float nextY = y + (step * deltaTime) * playerComponent.getCamera().direction.y;
        float dis = Intersector.distanceLinePoint(src.getX(), src.getY(), dst.getX(), dst.getY(), nextX, nextY);
        boolean bodyIntersection = dis <= 0.5f;
        auxVector21.set(src.getX(), src.getY());
        auxVector22.set(dst.getX(), dst.getY());
        auxVector23.set(nextX, nextY);
        if (bodyIntersection) {
            numberOfIntersections++;
            if (numberOfIntersections > 1) {
                return;
            }
            theta = auxVector22.sub(auxVector21).nor().angle();
            float angle = (float) (180.0 / MathUtils.PI * MathUtils.atan2(auxVector23.y - position.y, auxVector23.x - position.x));
            if (angle < 0) angle += 360;
            float diff = (theta > angle) ? theta - angle : angle - theta;
            if (step < 0) step *=-1;
            angleToMove = (diff > 90) ? theta + 180 : theta;
        }
    }
    if (numberOfIntersections == 0) {
        moveCameraByWalking(deltaTime, step, rotate);
    } else {
        moveCameraInDirection(deltaTime, step, angleToMove);
    }
}

1 个答案:

答案 0 :(得分:1)

这个想法是找到物体中心的路径和根据圆的半径移动的线的交点,看到那张图片。

this is the idea

首先,您需要在该行上找到一个normal。怎么做,取决于线的定义方式,如果由两点定义,则公式为

nx = ay - by
ny = bx - ax

如果这条线是由规范方程式定义的,那么如果我没记错的话, x y 的系数就定义了法线。

找到法线后,我们需要对其进行归一化-将坐标除以矢量长度,将长度设置为1。设为 n

然后,我们将起点,期望点和随机选择的点在线投影到 n ,将其视为radius vectors

向量 a 对向量 b 的投影是

project (a, b) = scalar_product (a, b) / length (b)**2 * b

,但是由于 b n ,其长度等于1,所以我们将不进行除法运算,并且我们也只想查找结果的长度,所以我们不乘以 b 。因此,我们仅对上述三个点中的每个点用 n 计算scalar product,得到三个数字,让 s 为起点 d < / em>表示期望的点, l 表示在线上的选定点。

然后我们应按圆的半径修改 l

if      (s < d) l -= r;
else if (s > d) l += r;

如果 s = d ,则您的对象沿直线平行移动,因此直线不会阻碍其移动。这是极不可能的情况,但应该处理。

这也很重要,如果 l 最初是在 s d 之间,但经过修改之后不再是这种情况,这是一种特殊情况您可能要处理(例如限制对象移动)

corner case

此外,您应该计算(d-s)/(l-s)

如果结果大于或等于1,则对象将不会到达该行。
如果结果在0到1之间,则该线会阻碍运动,并且结果指示对象将完成的路径的一部分。 0.5表示对象将中途停止。
如果结果为负,则表示该线位于对象的后面,不会阻碍运动。

请注意,当使用浮点数时,结果将不会十分精确,这就是我们处理这种特殊情况的原因。如果您想完全避免这种情况的发生,请组织循环并尝试近似值,直到达到所需的精度为止。