确定是从两个角度顺时针或逆时针转动

我在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; } // 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; } } 

UPDATE

我最终使用这样的方法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坐标系的变幻莫测+它与我自己的交互方式。

技术#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的回答更直接。)

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

 Vector3 crossProduct = Vector3.Cross(red, blue) if (crossProduct.z > 0) // Turn Right else // Turn Left 

我搜索了一下是否有一个超短的答案。 没找到一个。 似乎技术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; 

很容易。 假设你有2个角度的alpha和beta,你想告诉从alpha到beta的最短动作是顺时针或逆时针。 你应该做的是:将alpha设置为0并添加你给beta的alpha相同的偏移量。 现在如果β高于180-逆时针运动,否则,运动顺时针。 例如:alpha为10度,beta为350度。 所以-10度偏移到两个角度设置α为0,β为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