【问题标题】:Detect collision with lines and limit movement检测与线的碰撞并限制移动
【发布时间】:2019-04-15 23:05:39
【问题描述】:

我正在用 Java 中的 libGDX 制作游戏。我正在尝试进行碰撞检测。正如您在图像中看到的,我有一条线,它是一堵墙和一个具有指定半径的玩家。期望的位置是玩家试图进入的下一个位置。但是因为有一面墙,所以他被放置在速度矢量上的实际位置,但更接近上一个位置。我试图弄清楚如何检测到更近的位置?

我的尝试:

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

【问题讨论】:

  • 你尝试了什么,你的错误是什么?
  • 在您提供的链接中,我没有看到将播放器置于“固定”位置的相关部分。我一开始尝试的是检查下一个位置是否与墙的距离小于半径。如果属实,我不会让玩家移动到那里。如果玩家的速度是固定的,这种行为很好。但是在我的游戏中,速度会根据输入加速和减速 - 所以如果速度高,玩家不会移动,但速度低时它会移动(在靠近墙壁减速时会产生一些“慢跑”移动效果)
  • 您能否提供您在问题中描述的此尝试的代码?
  • 是的,已发布到问题中
  • 在第一种方法中似乎有很多行重复,这是实际代码吗?我猜不是,因为这不会编译(重复的变量名)。同样适用于第二种方法

标签: java libgdx 2d collision-detection


【解决方案1】:

这个想法是找到对象中心的路径和圆的半径移动的线的交点,看那张图。

首先,您需要找到一个normal 到该行。怎么做,要看线是怎么定义的,如果是两点定义的,公式是

nx = ay - by
ny = bx - ax

如果这条线是由规范方程定义的,那么 xy 处的系数定义正常,如果我没记错的话。

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

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

向量a到向量b的投影是

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

但是由于b是长度等于1的n,所以我们不会应用除法,而且我们只想找到结果的长度,我们不乘以b。所以我们只用 n 计算scalar product 对于上述三个点中的每一个,得到三个数字,让 s 作为起点的结果,d 为所需的点,l 为线上的选定点。

那么我们应该用圆的半径来修改l

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

如果s = d,你的物体会沿着直线平行移动,所以直线不会阻碍它的移动。这是极不可能的情况,但应该处理。

另外,这很重要,如果 l 最初在 sd 之间,但经过修改后不再介于两者之间,这是一种特殊情况您可能需要处理(例如限制对象移动)

另外,你应该计算 (d - s) / (l - s)

如果结果大于或等于 1,则对象将不会到达该线。
如果结果介于 0 和 1 之间,则该线会阻碍移动,结果指示对象将完成的部分路径。 0.5 表示对象将中途停止。
如果结果是否定的,则表示该线在物体后面并且不会阻碍移动。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-03
    • 2021-12-28
    相关资源
    最近更新 更多