我目前正在从头开始用 Java 开发一个塔防游戏,我正在重新设计子弹碰撞。为此,我需要计算给定点和线段之间的距离。我已经编写了一个函数来做到这一点,但它通常会在不应该提供的情况下给出与其中一个点的距离。
线段类:
public class LineSegment {
public Vector2DK p;
public Vector2DK q;
/**
* Creates a line segment between the points P and Q
* @param p Point P
* @param q Point Q
*/
public LineSegment(Vector2DK p, Vector2DK q) {
this.p = p;
this.q = q;
}
/**
* Calculates the distance to point R
* @param r Point R
* @return Distance
*/
public double distanceFrom(Vector2DK r) {
// after trying what feels like everything I might have gotten a little confused(stupid errors ahead)
Vector2DP differenceVector = r.subtract(p).toPolar();
Vector2DP differenceVector2 = r.subtract(q).toPolar();
double alpha = differenceVector.getAngle() - q.subtract(p).getAngle();
double beta = differenceVector2.getAngle() - q.subtract(p).getAngle();
if (alpha < Math.PI / 2 && alpha > -Math.PI / 2) {
return p.distanceTo(r);
} else if (beta > Math.PI / 2 || beta < -Math.PI / 2) {
return q.distanceTo(r);
}
return Math.abs(differenceVector.getMagnitude() * Math.sin(alpha));
}
public static void main(String[] args) {
// a main method for testing
// I already added a case where the bullet of the tower flew right thru the enemy(but I probably got the wrong tick, so the values are probably not good for testing)
Vector2DK linePoint0 = new Vector2DK(339.9354152826523, 374.7835775987438);
Vector2DK linePoint1 = new Vector2DK(317.79869321314067, 388.4287303893724);
Vector2DK point = new Vector2DK(290.0194200000002, 400.0);
LineSegment lineSegment = new LineSegment(linePoint0, linePoint1);
Line line = new Line(linePoint0, linePoint1);
System.out.println(lineSegment.distanceFrom(point));
}
}
我的两个向量类:
public class Vector2DK implements Vector2D {
private double x;
private double y;
public Vector2DK(double x, double y) {
this.x = x;
this.y = y;
}
public Vector2DK(Vector2D vector) {
Vector2DK vector2DK = vector.toKartesian();
x = vector2DK.x;
y = vector2DK.y;
}
public void copyToSelf(Vector2DK vector) {
x = vector.x;
y = vector.y;
}
@Override
public String toString() {
return String.format("Vector2DK(%s, %s)", x, y);
}
@Override
public Vector2DK toKartesian() {
return new Vector2DK(x, y);
}
@Override
public Vector2DP toPolar() {
return new Vector2DP(getMagnitude(), getAngle());
}
@Override
public double getAngle() {
return Math.atan2(y, x);
}
@Override
public double getMagnitude() {
return Math.sqrt(getMagnitudeSquare());
}
@Override
public double getX() {
return x;
}
@Override
public double getY() {
return y;
}
@Override
public void setAngle(double angle) {
Vector2DP vector = toPolar();
vector.setAngle(angle);
copyToSelf(vector.toKartesian());
}
@Override
public void setMagnitude(double magnitude) {
Vector2DP vector = toPolar();
vector.setAngle(magnitude);
copyToSelf(vector.toKartesian());
}
@Override
public void setX(double x) {
this.x = x;
}
@Override
public void setY(double y) {
this.y = y;
}
@Override
public double getMagnitudeSquare() {
return x * x + y * y;
}
@Override
public double distanceTo(Vector2D vector) {
return Math.sqrt(MathFunc.square(x - vector.getX()) + MathFunc.square(y - vector.getY()));
}
@Override
public void normalize() {
double magnitude = getMagnitude();
x /= magnitude;
y /= magnitude;
}
@Override
public void reverse() {
x = -x;
y = -y;
}
@Override
public Vector2D add(Vector2D vector) {
return new Vector2DK(x + vector.getX(), y + vector.getY());
}
@Override
public Vector2D subtract(Vector2D vector) {
return new Vector2DK(x - vector.getX(), y - vector.getY());
}
@Override
public Vector2D scale(double value) {
return new Vector2DK(x * value, y * value);
}
@Override
public void addWith(Vector2D vector) {
x += vector.getX();
y += vector.getY();
}
@Override
public void subtractWith(Vector2D vector) {
x -= vector.getX();
y -= vector.getY();
}
@Override
public void scaleWith(double value) {
x *= value;
y *= value;
}
@Override
public double dotProduct(Vector2D vector) {
return x * vector.getX() + y * vector.getY();
}
}
和:
public class Vector2DP implements Vector2D {
private double magnitude;
private double angle;
public Vector2DP(double magnitude, double angle) {
this.magnitude = magnitude;
this.angle = angle;
}
public Vector2DP(Vector2DP vector) {
Vector2DP vector2DP = vector.toPolar();
this.magnitude = vector.magnitude;
this.angle = vector.angle;
}
public void copyToSelf(Vector2DP vector) {
magnitude = vector.magnitude;
angle = vector.angle;
}
@Override
public String toString() {
return String.format("Vector2DP(%su, %s°)", magnitude, Math.toDegrees(angle));
}
@Override
public Vector2DK toKartesian() {
return new Vector2DK(getX(), getY());
}
@Override
public Vector2DP toPolar() {
return new Vector2DP(magnitude, angle);
}
@Override
public double getAngle() {
return angle;
}
@Override
public double getMagnitude() {
return magnitude;
}
@Override
public double getX() {
return Math.cos(angle) * magnitude;
}
@Override
public double getY() {
return Math.sin(angle) * magnitude;
}
@Override
public void setAngle(double angle) {
this.angle = angle;
}
@Override
public void setMagnitude(double magnitude) {
this.magnitude = magnitude;
}
@Override
public void setX(double x) {
Vector2DK vector = toKartesian();
vector.setX(x);
copyToSelf(vector.toPolar());
}
@Override
public void setY(double y) {
Vector2DK vector = toKartesian();
vector.setY(y);
copyToSelf(vector.toPolar());
}
@Override
public double getMagnitudeSquare() {
return magnitude * magnitude;
}
@Override
public double distanceTo(Vector2D vector) {
return subtract(vector).getAngle();
}
@Override
public void normalize() {
magnitude = 1;
}
@Override
public void reverse() {
angle += angle < Math.PI ? Math.PI : -Math.PI;
}
@Override
public Vector2D add(Vector2D vector) {
return toKartesian().add(vector).toPolar();
}
@Override
public Vector2D subtract(Vector2D vector) {
return toKartesian().add(vector).toPolar();
}
@Override
public Vector2D scale(double value) {
return new Vector2DP(magnitude * value, angle);
}
@Override
public void addWith(Vector2D vector) {
copyToSelf((Vector2DP) add(vector));
}
@Override
public void subtractWith(Vector2D vector) {
copyToSelf((Vector2DP) subtract(vector));
}
@Override
public void scaleWith(double value) {
magnitude *= value;
}
@Override
public double dotProduct(Vector2D vector) {
Vector2DP vector2DP = vector.toPolar();
return magnitude * vector2DP.magnitude * Math.cos(angle - vector2DP.angle);
}
}
答案 0 :(得分:-1)
考虑下一个 Python 代码(希望它易于翻译)。
首先函数检查段向量和从段的开始和结束到点的向量的点积。
如果点在直线上的投影位于线段之外,点积的符号揭示了这一事实,我们将距离返回到最近的线段末端(图中点 A、B、C、D)
否则我们返回投影长度(formula from wiki)(图片上的 E 点)
hypot
给出向量长度(就像你的 getMagnitude
)
import math
def pt_seg_dist(px, py, qx, qy, rx, ry):
qpx = qx - px
qpy = qy - py
rpx = rx - px
rpy = ry - py
if rpx * qpx + rpy * qpy <= 0:
return math.hypot(rpx, rpy)
rqx = rx - qx
rqy = ry - qy
if rqx * qpx + rqy * qpy >= 0:
return math.hypot(rqx, rqy)
return abs(rpx * qpy - rpy * qpx) / math.hypot(qpx, qpy)
print(pt_seg_dist(0, 0, 1, 0, 0.5, 2))
print(pt_seg_dist(0, 0, 1, 0, -2, 2))
print(pt_seg_dist(0, 0, 1, 0, 3, -2))
>>>
2.0
2.8284271247461903
2.8284271247461903