对于那些在这些问题上比我自己(数学)有更多经验的人,我有一个XNA问题。
背景:我有一个实现边界类的游戏,它只托管2个Vector2对象,一个起点和一个终点。当前的实现通过假设边界总是垂直或水平来粗略地处理冲突检测,即如果start.x和end.x是相同的检查我不是试图传递x等。
理想情况下,我想要实现的是一个接受两个Vector2参数的方法。第一个是当前位置,第二个是请求的位置(我想将其移动到假设没有异议)。该方法也将接受边界对象。这个方法应该做的是告诉我,如果我要在这一举动中跨越边界。这可能是一个布尔或理想的东西,代表我可以实际移动多远。
这种空洞的方法可能比我能说得更好。
/// <summary>
/// Checks the move.
/// </summary>
/// <param name="current">The current.</param>
/// <param name="requested">The requested.</param>
/// <param name="boundry">The boundry.</param>
/// <returns></returns>
public bool CheckMove(Vector2 current, Vector2 requested, Boundry boundry)
{
//return a bool that indicated if the suggested move will cross the boundry.
return true;
}
答案 0 :(得分:2)
我已经提出了一个答案,但这是另一个,类似但又不同的答案:
我会使用点线距离的变体,再次从Wolfram中取出! http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
如果取公式14,但取下分子中的绝对值(| |),则得到一个有符号的距离。我从来没有听说过签名的距离概念,所以不要指望这个术语是准确的。好的,所以如果原点和目的地的签名距离不同,则意味着原点和目的地位于该线的两侧:
public bool CheckMove(Vector2 current, Vector2 requested, Boundry boundry)
{
// Assuming you got 2 points in boundry.
float signedDistanceCurrent = GetSignedDistance(current, boundry.Vector1, boundry.Vector2);
float signedDistanceRequested = GetSignedDistance(current, boundry.Vector1, boundry.Vector2);
return signedDistanceRequested * signedDistanceCurrent < 0;
}
public float GetSignedDistance(Vector2 point0, Vector2 point1, Vector2 point2)
{
// Equation 14 without the |s.
return ((point2.X-point1.X)*(point1.Y-point0.Y)-(point2.X-point0.X)*(point2.Y-point1.Y))/((point2.X-point1.X)^2+(point2.Y-point1.Y)^2)^(0.5F);
}
顺便说一句,我更喜欢我之前的答案,但这取决于你。
编辑:顺便说一下,用这种方法,你也可以知道碰撞发生的时间:如果你这样做:
float timeFractionToCollision = -signedDistanceCurrent / (signedDistanceRequested - signedDistanceCurrent);
你将获得发生碰撞的时间段的时间分数的一小部分。
答案 1 :(得分:2)
经过一段时间的研究,并且发现了谷歌的正确条款,我有一个解决方案,我刚刚在我的测试床上构建了它,并且它运行良好。
我已经在网上找到了一个例子并对其进行了测试,然后对其进行了更改以满足此问题的需求。应该注意的是,自从测试之后我做了更改,因为我在这台笔记本电脑上只有一台无法运行XNA应用程序的虚拟机,因此我相信它应该可以运行,但可能会有错误。
我们需要做的就是传递一个我们所在的向量,一个我们想要的向量和边界(它实际上是一个由起始和结束向量组成的线)。该方法将告诉我们两条线(一条是当前位置和请求位置之间的路径,另一条是边界)是否交叉。如果他们做交叉参数将作为额外的奖励将告诉我们在哪里。
这是一个非常有用的系统,用于创建强大的碰撞检测。
/// <summary>
/// Based on the 2d line intersection method from "comp.graphics.algorithms Frequently Asked Questions"
/// </summary>
/// <param name="currentLocation">The current location.</param>
/// <param name="requestedLocation">The requested location.</param>
/// <param name="boundary">The boundary.</param>
/// <param name="collisionVector">The collision vector.</param>
/// <returns></returns>
public static bool Intersects(Vector2 currentLocation, Vector2 requestedLocation, Boundary boundary, ref Vector2 collisionVector)
{
float q = (currentLocation.Y - boundary.Start.Y) * (boundary.End.X - boundary.Start.X) - (currentLocation.X - boundary.Start.X) * (boundary.End.Y - boundary.Start.Y);
float d = (requestedLocation.X - currentLocation.X) * (boundary.End.Y - boundary.Start.Y) - (requestedLocation.Y - currentLocation.Y) * (boundary.End.X - boundary.Start.X);
if (d == 0)
{
return false;
}
float r = q / d;
q = (currentLocation.Y - boundary.Start.Y) * (requestedLocation.X - currentLocation.X) - (currentLocation.X - boundary.Start.X) * (requestedLocation.Y - currentLocation.Y);
float s = q / d;
if (r < 0 || r > 1 || s < 0 || s > 1)
{
return false;
}
collisionVector.X = currentLocation.X + (int)(0.5f + r * (requestedLocation.X - currentLocation.X));
collisionVector.Y = currentLocation.Y + (int)(0.5f + r * (requestedLocation.Y - currentLocation.Y));
return true;
}
答案 2 :(得分:0)
我认为你想要的是找到它们的角度,所以只做X / Y并比较,如果角度相同,它们是平行的。
像这样:float radians = (float)Math.Atan2(vector.X, vector.Y);
答案 3 :(得分:0)
我认为你要做的是2D光线盒交叉(带有轴对齐的盒子)。您可以找到许多在线算法,包括以下算法:http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm
答案 4 :(得分:0)
为了满足您的所有数学需求,我建议您先试用MathWorld.Wolfram.Com。这是一篇关于Line-Line Intersection的文章:http://mathworld.wolfram.com/Line-LineIntersection.html
你感兴趣的那部分,包括第四个等式。
使用它,您将获得交叉点坐标。然后,您可以检查在该移动过程中是否会越过该坐标。对于此检查,您可以执行以下操作:
public bool CheckMove(Vector2 current, Vector2 requested, Boundry boundry)
{
// If your object doesn't move, it can't hit anything!
if (current.X == requested.X && current.Y == requested.Y) { return false; }
Vector2 impactPoint = /* Your code to get the impact
point from the link above */;
Vector2 movement = requested - current;
float timeToImpact = 0;
// We checked for object movement already, so it's either moving in X, Y or both.
// I choose to check for X first, but you could check for Y.
if (movement.X != 0)
{
timeToImpact = (impactPoint.X - current.X) / movement.X;
}
else
{
timeToImpact = (impactPoint.Y - current.Y) / movement.Y;
}
// I don't consider a collision at 0 to be a collision since I think
// it had to be resolved the period before.
return timeToImpact <= 1 && timeToImpact > 0;
}