What I'm trying to do is make an object in 2D that is rotated a certain degrees, A degrees, for example, rotate to face the mouse, whose direction FROM that object is B degrees. This is in openGl.
I'm already able to instantly rotate the object, using the glRotatef function, but what I wan't to do is to be able to control the rotation over a certain number of seconds.
I'm using two methods which either increase, or decrease the rotation:
void GameObject::increaseRot(int millis) {
rotation += getRotDeg(millis);
}
void GameObject::decreaseRot(int millis) {
rotation -= getRotDeg(millis);
}
double GameObject::getRotDeg(int millis) {
double rot = 360 / this->rotSpeed;
rot = rot * millis / 1000.0;
return rot;
}
Millis comes from a timer which works properly, and so I can cause an object to rotate at a speed of 360 degrees every rotSpeed seconds.
Edit: I've found a solution on the internet, that seems to mostly work. Using the formula of that solution with my own code, the code is
shortest_angle=((((end - start) % 360) + 540) % 360) - 180;
/* check which way to rotate
* this part of the code appears to work fine, all it does is
* rotate a certain number of degrees, it's my code that I've been
* using the whole time
*/
if(rotateA < 0)
game.getPlayer().decreaseRot(deltaT);
else if(rotateA > 0)
game.getPlayer().increaseRot(deltaT);
However, the code still takes the longer route at certain values, and I can't figure out why . . .
The value I've noticed this happening are:
45 trying for 135
225 trying for 315
315 trying for 45
These are approximate values of course, any values around those areas will screw up. I've been thinking that it's something to do with the limits 90, 180, 270 and 360/0, but I can't figure out what the actual problem is.
答案 0 :(得分:0)
我找到并找出了这个问题的解决方案,对于其他可能也遇到同样问题的人来说。
使用&#34; user151496&#34;,Rotation Interpolation
中的代码 shortest_angle=((((end - start) % 360) + 540) % 360) - 180;
return shortest_angle * amount;
您可以更改它以应用您自己的自定义旋转速度,因为我使用了
等功能 if(rotateA < 0 && rotateA >= -180)
game.getPlayer().decreaseRot(deltaT);
else if (rotateA < 0 && rotateA <= -180) //for the crossing of the boundary
game.getPlayer().increaseRot(deltaT);
else if(rotateA > 0 && rotateA <= 180)
game.getPlayer().increaseRot(deltaT);
else if(rotateA > 0 && rotateA >= 180)
game.getPlayer().decreaseRot(deltaT); //for the crossing of the boundary
这一切都是检查shortest_angle是正还是负(rotateA)。然后它相应地顺时针旋转(减速)或逆时针旋转(增加旋转)。 但请注意两条额外的线,检查-180和180度的条件。这些是必要的 - 我发现很难用数字解释,但它与穿越0/360度线有关。
如果你没有那些额外条件,只要你必须越过这样的边界,旋转就会以更大的角度旋转。
答案 1 :(得分:0)
可以在不使用任何条件分支的情况下插入角度,如下所示:
template <typename T>
T lerp_fixed_degrees(T a, T b, T x) {
T d = wrap_degrees(b - a);
return wrap_degrees(a + std::copysign(std::fmin(std::abs(x), std::abs(d)), d));
}
a
计算b
范围[-180, 180)
与wrap_degrees(b - a)
之间的差异。函数wrap_degrees
计算[-180, 180)
范围内的等效角度(例如wrap_degrees(190) == -170
和wrap_degrees(-190) == 170
)。std::fmin(std::abs(x), std::abs(d))
确定步长和差异中较小的一个。这可以防止我们超越目标(如果您知道x
是正面的,则可以使用std::abs(x)
代替x
,但我选择了稳健性。)std::copysign
以正确的方向进行插值。[-180, 180)
范围内的等效角度。函数wrap_degrees
的实现如下:
template <typename T>
T wrap_degrees(T x) {
return fmod_floor(x + T(180), T(360)) - T(180);
}
180
添加到输入值。[0, 360)
将值包裹在fmod_floor
范围内。 fmod_floor
函数类似于std::fmod
,除了它生成 floored division 的其余部分,它总是与divisor具有相同的符号。这是我们想要的行为(例如fmod(-10, 360) == -10
而fmod_floor(-10, 360) == 350
)。[-180, 180)
(包含之前添加的180
来平衡),将包装后的值移回180
范围。 fmod_floor
函数实现如下:
template <typename T>
T fmod_floor(T a, T n) {
return std::fmod(std::fmod(a, n) + n, n);
}
(-n, n)
std::fmod
范围内。(0, 2n)
。[0, n)
std::fmod
范围内。这是一个快速演示:
double a = 90.;
double prev;
do {
std::cout << a << "\n";
prev = a;
a = lerp_fixed_degrees(a, -100., 45.);
} while (a != prev);
输出:
90
135
-180
-135
-100
您还可以使用0
到1
之类的值执行简单的线性插值,如下所示:
template <typename T>
T lerp_degrees(T a, T b, T t) {
return wrap_degrees(a + wrap_degrees(b - a) * t);
}
我认为这个问题非常明显。