WPF:沿路径查找元素

时间:2009-02-18 01:07:37

标签: wpf math 2d

我没有标记这个问题已回答。
由于Bounty Time-Limit

,当前接受的答案自动被接受

参考我目前正在制作的this programming game

从上面的链接可以看出,我目前正在制作一款用户可编程机器人在竞技场中自主战斗的游戏。


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

alt text http://img21.imageshack.us/img21/7839/robotdetectionrg5.jpg

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

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

我知道炮塔的标题(它将面向当前的方式),并且我需要找到它的路径中是否有任何机器人(并且路径应该以'视点'的方式定义,在上面的图像中以红色“三角形”的形式描绘。我希望图像能够使我想要表达的内容更加清晰。

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


[UPDATE]

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

alt text http://img12.imageshack.us/img12/7416/examplebattle2.png

在上面的场景中,Bot 1正在检查他是否能看到Bot 2.以下是详细信息(根据Waylon Flinn's answer):

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,但从图像中可以看出,这是不可能的,因为它们面向不同的方向。

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

9 个答案:

答案 0 :(得分:3)

机器人之间的角度是arctan(x距离,y距离)(大多数平台提供这个2参数arctan为你做角度调整。然后你只需要检查这个角度是否小于某个数字远离当前的标题。

编辑:对于模糊的回答感到抱歉,但这里有一些关于更新的说明:

  • 坐标将在笛卡尔系统中,而不是窗口系统。这会导致一些不同的数字

    方向:3.3&lt; - 这是不正确的,bot1看起来像是面向4或其他东西。 191度大约是时钟的8:30位置,几乎直接指向坦克2.难怪系统返回“可见”!

    方位角:cy为-31(31个单位以下),而不是31个。

通过这些更改,您应该得到正确的结果。

答案 1 :(得分:1)

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

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

答案 2 :(得分: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 Framework,因为它是根据游戏设计创建的。但是,它不使用WPF。

这假定:

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

免责声明:未对此进行测试甚至检查以进行编译,并根据需要进行调整。

答案 3 :(得分: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)This article似乎有FOV计算的东西。

3)作为AI毕业生......你试过神经网络,你可以训练他们识别机器人是否在射程和有效目标。这将否定任何可怕的复杂和复杂的数学!您可以在机器人坐标中有一个多层感知器[1][2],并且目标会在最后进行协调并接受一个很好的火灾/禁火决定。警告:我觉得有必要告诉你,这种方法不是最容易实现的,并且在出错时会非常令人沮丧。由于这种形式的算法的(simle)非确定性,调试可能是一种痛苦。另外,您需要某种形式的学习,包括Back Propogation(带训练案例)或遗传算法(另一个完美的复杂过程)!鉴于我的选择,我会使用3号,但不是每个人都这样!

答案 4 :(得分: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);

答案 5 :(得分: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(角度)

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

3)或表达另一种方式(在一些代数操作之后):

angle = acos((a * b)/ | a || b |)

angle是两个向量a和b之间的角度,所以这个信息可以告诉你一个坦克是否可以看到另一个。

| A |是矢量a的大小,根据毕达哥拉斯定理,只是sqrt(ax * ax + ay * ay),同样适用于| b |。

现在问题来了,你怎么找到一个* b(点积b)来找到角度。

4)救援来了。原来,点积也可以表示如下:

a * b = ax * bx + ay * by

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

如果角度小于FOV的一半,则可以看到有问题的水箱。否则就不是。

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

根据我们的示例数字:

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

| A | = sqrt(3 * 3 + 4 * 4)

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

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

(在将其与您的FOV进行比较之前,请务必将得到的角度转换为度数或弧度)

答案 6 :(得分:0)

看看你的两个问题,我认为你可以使用提供的数学解决这个问题,然后你必须解决许多其他关于碰撞检测,发射子弹等的问题。这些都是非常重要的解决,特别是如果你的机器人不正方形。我建议查看物理引擎 - 在Codeplex上farseer是一个很好的WPF示例,但这使得它成为比高中开发任务更大的项目。

我获得高分的最佳建议,做一些非常简单的事情,不要提供出色的东西。

答案 7 :(得分:0)

你的炮塔真的有这么宽的射击模式吗?子弹所采取的路径将是一条直线,并且在行进时不会变大。你应该在炮塔的方向上有一个简单的矢量来代表炮塔杀戮区。每个坦克都有一个代表其脆弱区域的边界圆。然后,您可以按照光线跟踪的方式进行操作。一个简单的光线/圆形交叉点。请查看文档Intersection of Linear and Circular Components in 2D的第3部分。

答案 8 :(得分:0)

您的更新问题似乎来自orientationazimuth的不同“零”方向:0 orientation似乎意味着“直线上升”,但是{{1} 0“直接右”。