我在XNA中制作游戏。 我有敌人和球员。 敌人应该逐渐转向玩家。他们应该确定是否需要顺时针或逆时针转动,以较短者为准。
通过使用Atan2,我得到了敌人目前所面对的角度以及它应该面对的角度(敌人和玩家之间的直线角度)作为弧度。
虽然我得到了一些奇怪的行为。让我们在下面的场景中说。敌人可能会朝错误的方向转过来。
我的代码(下面)不断变长,我仍然遇到问题。此代码是敌人类Update方法的一部分。 这必须是游戏中需要克服的常见问题。有没有办法解决这个问题?
//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坐标系的变幻莫测+它与我自己的交互方式。
答案 0 :(得分:10)
您正在使用我不熟悉的惯例。在你的惯例中,东是0,北是-π/ 2,西是π和-π,南是π/ 2。所有角度都在-π和π之间。
通常,朝东的角度为零,北为π/ 2,西为π,正南为3π/ 2。所有角度都在0到2π之间。
让我们假设正常的约定,而不是你的约定。首先在正常约定中使红色和蓝色矢量角度正确;你如何做到这一点取决于你。
从两个角度减去红色矢量的角度。现在我们有一个面向正东方的人。
现在规范化新的蓝色角度;如果小于0,则加2π。如果它大于2π,则减去2π。这样做直到介于0和2π之间。
现在我们有两个角度;新红色矢量的角度为零,新蓝色矢量的角度在0到2π之间。
如果新蓝色矢量的角度小于π,则原点处的字符需要向左转。如果它大于π则向右转。
在蓝色和红色矢量上取一个非零点,比如(bx, by)
和(rx, ry)
。现在计算bx * ry - by * rx
。如果是正数,则右转,如果是负数,则向左转。如果它为零,那么他们要么直接朝向或直接离开;在这种情况下,你必须通过其他方式找出你所处的情况。 (这主要是Jacek的回答更直接。)
答案 1 :(得分:5)
如果你有蓝色和红色矢量作为Vector3,你可以这样做:
Vector3 crossProduct = Vector3.Cross(red, blue)
if (crossProduct.z > 0)
// Turn Right
else
// Turn Left
答案 2 :(得分:0)
我搜索了一下是否有超短答案。没找到一个。似乎技术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;