4

我在 XNA 中制作游戏。我有敌人和玩家。敌人应该逐渐转向玩家。他们应该确定是否需要顺时针或逆时针转动,以较短者为准。

通过使用 Atan2,我得到了敌人当前面对的角度和它应该面对的角度(敌人和玩家之间的线的角度)作为弧度。

我得到了一些奇怪的行为。让我们在下面的场景中说。敌人可能会完全转向错误的方向。

在此处输入图像描述

我的代码(如下)越来越长,但我仍然遇到问题。这段代码是敌人类更新方法的一部分。这一定是游戏中需要克服的常见问题。有没有办法解决这个问题?

            //this bit is just in case enemy has rotated more than 360 degrees (gets set back to 0)
            if (Math.Abs(_blocklist[0]._floor.Revolutions) >= 2)
            {
                _blocklist[0]._floor.Rotation = 0.0f;
            }

            //enemy rotation in radians          
            float blockroat = _blocklist[0]._floor.Rotation;
            // vector to player - vector to enemy
            _vectToPlayer = playerpos - _blocklist[0].Centre
            angletoplayer = (float)(Math.Atan2(_vectToPlayer.Y, _vectToPlayer.X));
            diff = blockroat - angletoplayer;

            if (diff < -Math.PI)
            {
                diff += (float) Math.PI;
                diff = -diff;
            }
            else if (diff > Math.PI)
            {
                diff -= (float)Math.PI;
                diff = -diff;
            }

            // if enemy angle if off by a certain amount
            if (Math.Abs(diff) >_maxturn)
            {
                if (diff < 0)
                {
                    //turn clockwise
                    _blocklist[0]._floor.Rotation += _maxturn;
                }
                else
                {
                     //turn anti clockwise
                    _blocklist[0]._floor.Rotation -= _maxturn;
                }
            }

更新

我最终使用了这样的方法 2.. 效果很好。它也比我以前的代码整洁得多

            //enemy rotation in radians from farseer (red line)
            float brot = _blocklist[0]._floor.Rotation + ((float)Math.PI/2);
            //vector from enemy to player (blue line)
            Vector2 _vectToPlayer = playerpos - _blocklist[0].Centre;
            //cross product of 2d vectors
            cross = (_vectToPlayer.X * (float)Math.Sin(brot)) - ((float)Math.Cos(brot) * _vectToPlayer.Y);

            //tolerance for how closely enemy must point towards player
            if (Math.Abs(cross) > 5)
            {
                if (cross > 0)
                {
                    //turn anticlockwise
                    _blocklist[0]._floor.Rotation -= _npcstats.maxturnspeed;
                }
                else
                {
                    //turn clockwise
                    _blocklist[0]._floor.Rotation += _npcstats.maxturnspeed;
                } 
            }

我认为我以前的代码或多或少完全按照建议的方法 1。但我无法让它工作。我把它归结为 Farseers 坐标系的变幻莫测 + 它如何与我自己的交互。

4

4 回答 4

11

技术#1:

您正在使用我不熟悉的约定。在你的约定中,东是 0,北是 -π/2,西是 π 和 -π,南是 π/2。所有角度都在 -π 和 π 之间。

通常,字符朝东的角度为零,北为 π/2,西为 π,正南为 3π/2。所有角度都在 0 到 2π 之间。

让我们假设正常的约定而不是您的约定。首先让您的红色和蓝色矢量角度在正常约定中正确;你如何做到这一点取决于你。

从两个角度减去红色向量的角度。现在我们在原点有一个面向正东的家伙。

现在标准化新的蓝色角度;如果小于 0,则加 2π。如果大于 2π,则减去 2π。这样做直到它在 0 和 2π 之间。

现在我们有两个角度;新的红色向量的角度为零,新的蓝色向量的角度在 0 到 2π 之间。

如果新蓝色向量的角度小于 π,则原点处的字符需要向左转。如果它大于 π 则右转。

技术#2:

在蓝色和红色向量上取一个非零点,比如(bx, by)(rx, ry)。现在计算bx * ry - by * rx. 如果为正,则向右转,如果为负,则向左转。如果为零,则它们要么直接面向,要么直接背对;在这种情况下,您必须通过其他方式弄清楚您所处的情况。(这本质上是 Jacek 更直接的回答。)

于 2013-05-17T16:56:07.233 回答
5

如果你有蓝色和红色向量作为 Vector3,你可以这样做:

Vector3 crossProduct = Vector3.Cross(red, blue)
if (crossProduct.z > 0)
    // Turn Right
else
    // Turn Left
于 2013-05-17T17:36:00.147 回答
1

我搜索以查找是否有超短答案。一个都没找到。似乎技术 1 很受欢迎。但是我已经实现了它,所以你在这里:

//ccw = 1, cw = -1
//refineAngle = normallize angle to range [-PI, PI]

currentAngle = refineAngle(currentAngle);
targetAngle =  refineAngle(targetAngle);

if(targetAngle < 0)
    targetAngle += (PI *2);
if(currentAngle < 0)
    currentAngle += (PI *2);
if(targetAngle < currentAngle)
    targetAngle += (PI *2);

if(targetAngle - currentAngle <= PI)
    return 1;
else
    return -1;
于 2014-03-05T21:12:13.023 回答
1

很容易。假设您有 2 个角度 alpha 和 beta,并且您想告诉从 alpha 到 beta 的最短运动是顺时针还是逆时针。你应该做的是:将 alpha 设置为 0,并将你给 alpha 的相同偏移量添加到 beta。现在如果 beta 高于 180- 运动是逆时针的,否则,运动是顺时针的。例如:alpha 是 10 度,beta 是 350 度。因此,两个角度的 -10 度偏移将 alpha 设置为 0,将 beta 设置为 340,并且逆时针移动。

def direction_by_2_angles(alpha, beta)
  # true clockwize, false not clockwize
  delta = 360 - alpha
  beta = beta + delta
  beta = beta % 360
  beta < 180 ? true : false
end
于 2018-05-10T20:56:23.770 回答