我实现了追逐一个旋转的星球的鱼雷物体。 具体来说,它会转向每个更新的星球。 最初我的工具是:
void move() { vector3<float> to_target = target - get_position(); to_target.normalize(); position += (to_target * speed); }
这对完美的鱼雷来说是一个坚实的球体。 现在我的鱼雷实际上是一个模型,它有一个向前的向量,所以使用这个方法看起来很奇怪,因为它实际上并没有转向,而是向着跳跃。 所以我修改了一下得到了,
double get_rotation_angle(vector3<float> u, vector3<float> v) const { u.normalize(); v.normalize(); double cosine_theta = u.dot(v); // domain of arccosine is [-1, 1] if (cosine_theta > 1) { cosine_theta = 1; } if (cosine_theta < -1) { cosine_theta = -1; } return math3d::to_degree(acos(cosine_theta)); } vector3<float> get_rotation_axis(vector3<float> u, vector3<float> v) const { u.normalize(); v.normalize(); // fix linear case if (u == v || u == -v) { v[0] += 0.05; v[1] += 0.0; v[2] += 0.05; v.normalize(); } vector3<float> axis = u.cross(v); return axis.normal(); } void turn_to_face() { vector3<float> to_target = (target - position); vector3<float> axis = get_rotation_axis(get_forward(), to_target); double angle = get_rotation_angle(get_forward(), to_target); double distance = math3d::distance(position, target); gl_matrix_mode(GL_MODELVIEW); gl_push_matrix(); { gl_load_identity(); gl_translate_f(position.get_x(), position.get_y(), position.get_z()); gl_rotate_f(angle, axis.get_x(), axis.get_y(), axis.get_z()); gl_get_float_v(GL_MODELVIEW_MATRIX, OM); } gl_pop_matrix(); move(); } void move() { vector3<float> to_target = target - get_position(); to_target.normalize(); position += (get_forward() * speed); }
逻辑很简单,我通过叉积来find旋转轴,由点积旋转的角度,然后向目标位置每次更新。 不幸的是,由于旋转发生得太快而总是来回转动,所以看起来极有可能。 鱼雷的正向vector来自ModelView
matrix,第三列A
:
MODELVIEW MATRIX -------------------------------------------------- RUAT -------------------------------------------------- 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 --------------------------------------------------
任何建议或想法将不胜感激。
编辑
在我目前的实施中,我没有forward
vector,唯一的vector是position
。 OM
是我的定位matrix。 另外,在任何对象更新之前,我都会调用glu_look_at
,这就是为什么我必须存储OM
所有对象转换,这样才能更轻松地检查碰撞。 简而言之,
vector3<float> get_forward()
:返回第三列。
vector3<float> get_position()
:返回vector3<float> get_position()
列。
vector3<float> get_up_vector()
:返回第二列。
而在我的绘画例程中,
void draw() { gl_push_matrix(); { // camera is set here ... // ... gl_mult_matrix_f(OM); // draw } gl_pop_matrix(); }
目前看起来你正在将鱼雷的旋转(在模型视图matrix中)设置为错误的角度。 你计算的角度是在鱼雷面临的向量( forward
)和to_target
。 这对于确定要转多远是有用的,但是用正确的方向显示鱼雷需要从单位matrix转向forward
。
每次打电话给turn_to_face
,不要重新创建一个matrix,而要维护一个matrix,而且每次只需转换和旋转matrix可能会更容易一些。
matrix44<float> position_and_rotation; // Or whatever class you have. ... void turn_to_face() { ... position_and_rotation.rotate(axis, angle); glMultMatrixf(position_and_rotation.data()); // Where data() returns a raw // pointer to the underlying // data of the matrix. ... } void move() { position_and_rotation.translate(get_forward() * speed); }
我上面写的是接近正确的,但它仍然是错误的。 它不会把你的鱼雷转向地球,它会直接将它转向地球,只需一个电话就可以turn_to_face
。 要逐渐将其转化,您需要定义一个rotational_speed
。
position_and_rotation.rotate(axis, min(rotational_speed, angle));
这意味着您在打电话给turn_to_face
过程中不会超过rotational_speed
,也会阻止您不断地超出正确的方向。
你需要确保你总是朝着你所面对的方向前进。 目前我无法看到任何forward
更新的地方。
在get_rotation_angle
您正在对两个向量进行归一化,这意味着点积的范围已经是[-1,1](您不需要手动钳制到该范围)。
to_target
中的turn_to_face
函数中的move
函数和distance
都没有使用,所以摆脱了他们的:)
正如Gary已经提到的那样,你需要把鱼雷的局部前向向量计算(不一定是身份matrix的第三行,取决于你的模型是如何创建的)。 如果鱼雷的本地前进向量不同,请在那里改变。
你的代码中有4件事改变了:
to_target
forward
并引入forward_torpedo_local
gl_load_identity()
,它会弄乱你的相机设置 move()
代码move()
了turn_to_face()
,因为它只是一行代码 这里是代码:
void turn_to_face() { vector3<float> forward_torpedo_local(0.0, 0.0, 1.0); vector3<float> forward = (target - position); forward.normalize(); position += (forward * speed); vector3<float> axis = get_rotation_axis(forward_torpedo_local, forward); double angle = get_rotation_angle(forward_torpedo_local, forward); gl_matrix_mode(GL_MODELVIEW); gl_push_matrix(); { gl_translate_f(position.get_x(), position.get_y(), position.get_z()); gl_rotate_f(angle, axis.get_x(), axis.get_y(), axis.get_z()); } gl_pop_matrix(); }
在调用get_rotation_axis和get_rotation_angle之前,应该对vector进行归一化处理,而不是在这些函数内部进行归一化处理,但是这只是一个次要的优化。