6

我尚未将此问题标记为已回答。
由于赏金时间限制,当前接受的答案被自动接受


参考我目前正在构建的这个编程游戏。

正如您从上面的链接中看到的那样,我目前正在构建一个用户可编程机器人在竞技场中自主战斗的游戏。


现在,我需要一种方法来检测机器人是否在特定角度检测到另一个机器人(取决于炮塔可能面向的位置):

替代文字 http://img21.imageshack.us/img21/7839/robotdetectionrg5.jpg

从上图中可以看出,我已经绘制了一种坦克的视角,现在我需要在游戏中模拟它,以检查其中的每个点以查看是否有另一个机器人在视野中。

机器人只是在战斗竞技场(另一幅画布)上不断翻译的画布。

我知道炮塔的朝向(它当前面对的方向),因此,我需要找到它的路径中是否有任何机器人(并且路径应该以“视点”方式定义,如上面的图片以红色“三角形”的形式出现。我希望这张图片能更清楚地表达我想要表达的意思。

我希望有人可以指导我解决这个问题所涉及的数学。


[更新]

我已经尝试了您告诉我的计算,但它无法正常工作,因为从图像中可以看出, bot1 不应该能够看到 Bot2 。这是一个例子:

替代文字 http://img12.imageshack.us/img12/7416/examplebattle2.png

在上述场景中,Bot 1 正在检查他是否可以看到 Bot 2。以下是详细信息(根据Waylon Flinn 的回答):

angleOfSight = 0.69813170079773179 //in radians (40 degrees)
orientation = 3.3 //Bot1's current heading (191 degrees)

x1 = 518 //Bot1's Center X
y1 = 277 //Bot1's Center Y

x2 = 276 //Bot2's Center X
y2 = 308 //Bot2's Center Y

cx = x2 - x1 = 276 - 518 = -242
cy = y2 - y1 = 308 - 277 =  31

azimuth = Math.Atan2(cy, cx) = 3.0141873380511295

canHit = (azimuth < orientation + angleOfSight/2) && (azimuth > orientation - angleOfSight/2)
       = (3.0141873380511295 < 3.3 + 0.349065850398865895) && (3.0141873380511295 > 3.3 - 0.349065850398865895)
       = true

根据上面的计算,Bot1 可以看到 Bot2,但是从图片中可以看出,这是不可能的,因为它们面向不同的方向。

我在上述计算中做错了什么?

4

9 回答 9

3

机器人之间的角度是 arctan(x-distance, y-distance) (大多数平台都提供了这个 2-argument arctan 来为你进行角度调整。然后你只需要检查这个角度是否小于距离当前标题。


编辑 2020:这是基于问题中更新的示例代码和现已删除的 imageshack 图像的更完整分析。

  1. Atan2:求两点间夹角的关键函数是atan2。这需要向量的 Y 坐标和 X 坐标,并返回该向量与正 X 轴之间的角度。该值将始终包裹在 -Pi 和 Pi 之间。

  2. Heading vs Orientation: atan2和一般你所有的数学函数,在“数学标准坐标系”中工作,这意味着“0”的角度对应于正东,角度逆时针增加。因此,给出的“数学角度”是Pi / 2atan2(1, 0)“从正东逆时针90度”的方向,它与点(x=0,y=1)相匹配。“航向”是一种导航概念,表示方向是从正北顺时针方向的角度。

    • 分析:在现已删除的imageshack图像中,您的191度“航向”对应于南-南-西方向。这实际上是-101 度或-1.76 度的三角“方向”。因此,更新代码中的第一个问题是将“标题”和“方向”混为一谈。orientation_degrees = 90 - heading_degrees您可以通过或从前者获得后者orientation_radians = Math.PI / 2 - heading_radians,或者您可以在数学坐标系而不是航海航向坐标系中指定输入方向。
  3. 检查一个角度是否位于其他两个向量之间:检查一个向量是否位于其他两个向量之间并不像检查数字角度值是否介于两者之间那么简单,因为角度在 Pi/-Pi 处缠绕的方式。

    • 分析:在你的例子中,方向是3.3,视图的右边缘是方向2.95,视图的左边缘是3.65。计算出的方位角是 3.0141873380511295,恰好是正确的(它确实介于两者之间)。但是,对于像 -3 这样的方位角值,这将失败,这应该计算为“命中”。有关解决方案,请参阅计算一个角度是否介于两个角度之间
于 2009-02-18T01:12:06.247 回答
1

计算每个机器人相对于当前机器人的相对角度和距离。如果角度在当前航向的某个阈值内并且在最大视野范围内,那么它可以看到它。

唯一棘手的事情是处理角度从 2pi 弧度变为 0 的边界情况。

于 2009-02-18T01:12:13.347 回答
1

在你的机器人类中这样的东西(C# 代码):

/// <summary>
/// Check to see if another bot is visible from this bot's point of view.
/// </summary>
/// <param name="other">The other bot to look for.</param>
/// <returns>True iff <paramref name="other"/> is visible for this bot with the current turret angle.</returns>
private bool Sees(Bot other)
{
    // Get the actual angle of the tangent between the bots.
    var actualAngle = Math.Atan2(this.X - other.X, this.Y - other.Y) * 180/Math.PI + 360;

    // Compare that angle to a the turret angle +/- the field of vision.
    var minVisibleAngle = (actualAngle - (FOV_ANGLE / 2) + 360);
    var maxVisibleAngle = (actualAngle + (FOV_ANGLE / 2) + 360); 
    if (this.TurretAngle >= minVisibleAngle && this.TurretAngle <= maxVisibleAngle)
    {
        return true;
    }
    return false;
}

笔记:

  • +360 用于将任何负角度强制为其相应的正值,并将角度 0 的边界情况转移到更容易进行范围测试的地方。
  • 这可能仅使用弧度角是可行的,但我认为它们很脏且难以阅读:/
  • 有关详细信息,请参阅Math.Atan2文档。
  • 我强烈建议研究XNA 框架,因为它是在考虑游戏设计的情况下创建的。但是,它不使用 WPF。

这假设:

  • 没有阻碍视线的障碍物
  • Bot 类具有 X 和 Y 属性
  • X 和 Y 属性位于机器人的中心。
  • Bot 类 TurretAngle 属性,表示炮塔相对于 x 轴的正角度,逆时针方向。
  • Bot 类有一个名为 FOV_ANGLE 的静态常量角度,表示炮塔的视野。

免责声明:这没有经过测试,甚至没有检查编译,根据需要进行调整。

于 2009-03-06T20:50:42.493 回答
1

实施类似的东西后的一些建议(很久以前!):

以下假设您正在遍历战场上的所有机器人(这不是一个特别好的做法,但可以快速轻松地让某些东西工作!)

1)它更容易检查机器人是否在范围内,然后如果它当前可以在 FOV 中看到,例如

int range = Math.sqrt( Math.abs(my.Location.X - bots.Location.X)^2 + 
            Math.abs(my.Location.Y - bots.Location.Y)^2 );

if (range < maxRange)
{
    // check for FOV
}

这确保了它可以潜在地缩短大量 FOV 检查并加快运行模拟的过程。需要注意的是,您可以在这里设置一些随机性以使其更有趣,这样在一定距离后,看到的机会与机器人的范围成线性比例。

2)这篇文章似乎有FOV计算的东西。

3) 作为一名 AI 毕业生......你尝试过神经网络,你可以训练他们识别机器人是否在范围内和有效目标。这将否定任何极其复杂和令人费解的数学!你可以有一个多层感知器[1][2]输入机器人坐标和目标坐标,最后得到一个不错的开火/不开火决定。警告:我觉得有义务告诉你,这种方法并不是最容易实现的,而且当它出错时可能会非常令人沮丧。由于这种算法的(简单)不确定性,调试可能会很痛苦。另外,您将需要某种形式的学习反向传播(带有训练案例)或遗传算法(另一个需要完善的复杂过程)!如果可以选择,我会使用 3 号,但它不适合所有人!

于 2009-03-10T10:32:51.790 回答
1

这将告诉您 canvas2 的中心是否可以被 canvas1 击中。如果你想考虑 canvas2 的宽度,它会变得更复杂一些。简而言之,您必须进行两次检查,一次检查 canvas2 的每个相关角,而不是检查中心。

/// assumming canvas1 is firing on canvas2

// positions of canvas1 and canvas2, respectively
// (you're probably tracking these in your Tank objects)
int x1, y1, x2, y2;

// orientation of canvas1 (angle)
// (you're probably tracking this in your Tank objects, too)
double orientation;
// angle available for firing
// (ditto, Tank object)
double angleOfSight;

// vector from canvas1 to canvas2
int cx, cy;
// angle of vector between canvas1 and canvas2
double azimuth;
// can canvas1 hit the center of canvas2?
bool canHit;

// find the vector from canvas1 to canvas2
cx = x2 - x1;
cy = y2 - y1;

// calculate the angle of the vector
azimuth = Math.Atan2(cy, cx);
// correct for Atan range (-pi, pi)
if(azimuth < 0) azimuth += 2*Math.PI;

// determine if canvas1 can hit canvas2
// can eliminate the and (&&) with Math.Abs but this seems more instructive
canHit = (azimuth < orientation + angleOfSight) &&
    (azimuth > orientation - angleOfSight);
于 2009-03-11T00:43:27.923 回答
1

使用向量数学中称为点积的概念可以很容易地实现它。

http://en.wikipedia.org/wiki/Dot_product

它可能看起来令人生畏,但并没有那么糟糕。这是处理您的 FOV 问题的最正确方法,其美妙之处在于无论您处理 2D 还是 3D(此时您知道解决方案是正确的),相同的数学运算都有效。

(注意:如果有什么不清楚的地方,可以在评论区提问,我会补上缺失的链接。)

脚步:

1)你需要两个向量,一个是主坦克的航向向量。您需要的另一个向量来自相关油箱和主油箱的位置。

对于我们的讨论,让我们假设主坦克的航向矢量是(ax,ay),主坦克位置和目标坦克之间的矢量是(bx,by)。例如,如果主油箱在位置 (20, 30),目标油箱在 (45, 62),则向量 b = (45 - 20, 62 - 30) = (25, 32)。

同样,为了讨论的目的,让我们假设主坦克的航向矢量是 (3,4)。

这里的主要目标是找到这两个向量之间的角度,点积可以帮助你得到它。

2)点积定义为

a * b = |a||b| cos(角度)

读作 a (点积) b 因为 a 和 b 不是数字,它们是向量。

3)或以另一种方式表达(经过一些代数操作):

角度 = acos((a * b) / |a||b|)

角度是两个向量 a 和 b 之间的角度,因此仅此信息就可以告诉您一个坦克是否可以看到另一个坦克。

|一个| 是向量 a 的大小,根据毕达哥拉斯定理,它只是 sqrt(ax * ax + ay * ay),|b| 也是如此。

现在问题来了,如何找出a * b(点积b)以求角度。

4) 救援来了。事实证明,点积也可以表示如下:

a * b = ax * bx + ay * 通过

所以角度 = acos((ax * bx + ay * by) / |a||b|)

如果角度小于 FOV 的一半,则说明有问题的坦克在视野中。否则不是。

所以使用上面的示例数字:

根据我们的示例数字:

a = (3, 4) b = (25, 32)

|一个| = sqrt(3 * 3 + 4 * 4)

|b| = sqrt(25 * 25 + 32 * 32)

角度 = acos((20 * 25 + 30 * 32) /|a||b|

(在将其与您的 FOV 进行比较之前,请务必根据需要将结果角度转换为度数或弧度)

于 2009-03-11T05:04:21.400 回答
0

查看您的两个问题,我认为您可以使用提供的数学来解决此问题,然后您必须解决有关碰撞检测,发射子弹等的许多其他问题。这些解决起来并不容易,尤其是如果您的机器人不是正方形。我建议查看物理引擎 - codeplex 上的farseer是一个很好的 WPF 示例,但这使它成为一个比高中开发任务更大的项目。

我得到的最好的建议是高分,把简单的事情做得非常好,不要部分提供出色的东西。

于 2009-02-18T01:17:59.040 回答
0

你的炮塔真的有那么宽的射击模式吗?子弹所走的路径将是一条直线,并且它不会随着行进而变大。你应该有一个指向炮塔方向的简单矢量,代表炮塔杀伤区。每个坦克都有一个代表其脆弱区域的边界圆圈。然后,您可以按照他们对光线追踪的方式进行操作。一个简单的射线/圆相交。查看文档Intersection of Linear and Circular Components in 2D的第 3 节。

于 2009-03-05T05:00:58.540 回答
0

您更新的问题似乎来自 and 的不同“零”方向orientationazimuth0 的一个orientation似乎意味着“笔直向上”,但azimuth0 的一个“笔直向右”。

于 2009-03-12T02:31:56.967 回答