检测与线的碰撞并限制移动
Detect collision with lines and limit movement
我正在 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);
}
}
想法是找到对象中心路径与圆半径移动的线的交点,参见该图。
首先,你需要找到一个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。所以我们只计算 scalar product 和 n 对于上述三个点中的每一个,得到三个数字,让 s 作为起点的结果, d 为想要的点, l 为线上选择的点.
那么我们应该将l修改为圆的半径:
if (s < d) l -= r;
else if (s > d) l += r;
如果s = d,你的物体沿着直线平行移动,所以直线不能阻碍它的运动。这是极不可能的情况,但应该处理。
此外,这很重要,如果 l 最初在 s 和 d 之间,但之后在此期间不再进行修改,这是您可能想要处理的特殊情况(例如限制对象移动)
此外,您应该计算 (d - s) / (l - s)。
如果结果大于或等于1,对象将不会到达直线。
如果结果介于 0 和 1 之间,则线条会阻碍移动,结果表示对象将完成的部分路径。 0.5 表示对象将在中途停止。
如果结果为负,则表示线在物体后面,不会阻碍运动。
请注意,当使用浮点数时,结果不会非常精确,这就是我们处理这种特殊情况的原因。如果您想完全防止这种情况发生,请组织循环并尝试近似,直到达到所需的精度。
我正在 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);
}
}
想法是找到对象中心路径与圆半径移动的线的交点,参见该图。
首先,你需要找到一个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。所以我们只计算 scalar product 和 n 对于上述三个点中的每一个,得到三个数字,让 s 作为起点的结果, d 为想要的点, l 为线上选择的点.
那么我们应该将l修改为圆的半径:
if (s < d) l -= r;
else if (s > d) l += r;
如果s = d,你的物体沿着直线平行移动,所以直线不能阻碍它的运动。这是极不可能的情况,但应该处理。
此外,这很重要,如果 l 最初在 s 和 d 之间,但之后在此期间不再进行修改,这是您可能想要处理的特殊情况(例如限制对象移动)
此外,您应该计算 (d - s) / (l - s)。
如果结果大于或等于1,对象将不会到达直线。
如果结果介于 0 和 1 之间,则线条会阻碍移动,结果表示对象将完成的部分路径。 0.5 表示对象将在中途停止。
如果结果为负,则表示线在物体后面,不会阻碍运动。
请注意,当使用浮点数时,结果不会非常精确,这就是我们处理这种特殊情况的原因。如果您想完全防止这种情况发生,请组织循环并尝试近似,直到达到所需的精度。