基于Jo Engine的世嘉土星平台球体旋转异常问题求助
问题描述
我正在用面向世嘉土星的C语言引擎Jo Engine制作一个手柄操控的低模球体demo,玩法类似《Marble Madness》。目前球体移动时的旋转效果不对,不是真实的滚动,而是绕错误轴的生硬枢轴转动。
当前代码如下:
#include <jo/jo.h> #include "sphere.h" jo_camera cam; jo_img_8bits floor_image; jo_palette image_pal; jo_pos3Df sphere_position = {0, 50, 0}; // 初始化球体位置 jo_pos3Df pos; jo_rot3Df rot; jo_rot3Df sphere_rotation = {0.0f, 0.0f, 0.0f}; // 初始化球体旋转角度 void my_draw(void) { jo_printf(12, 1, "*Simple 3D demo*"); jo_3d_camera_look_at(&cam); jo_3d_push_matrix(); { jo_3d_translate_matrixf(sphere_position.x, sphere_position.y, sphere_position.z); jo_3d_rotate_matrix_rad(sphere_rotation.rx, sphere_rotation.ry, sphere_rotation.rz); display_sphere_mesh(); jo_3d_set_mesh_texture(&Meshsphere, 0); } jo_3d_pop_matrix(); } void my_gamepad(void) { const float movement_speed = 1.0f; const float rotation_speed = JO_DEG_TO_RAD(5.0f); const float DIAGONAL_FACTOR = 0.707f; //(1/sqrt(2)) float delta_x = 0.0f; float delta_z = 0.0f; float delta_rx = 0.0f; float delta_ry = 0.0f; switch (jo_get_input_direction_pressed(0)) { case LEFT: delta_x = -movement_speed; delta_ry = -rotation_speed; break; case RIGHT: delta_x = movement_speed; delta_ry = rotation_speed; break; case UP: delta_z = movement_speed; delta_rx = -rotation_speed; break; case DOWN: delta_z = -movement_speed; delta_rx = rotation_speed; break; case UP_LEFT: delta_x = -movement_speed * DIAGONAL_FACTOR; delta_z = movement_speed * DIAGONAL_FACTOR; delta_ry = -rotation_speed * DIAGONAL_FACTOR; delta_rx = -rotation_speed * DIAGONAL_FACTOR; break; case UP_RIGHT: delta_x = movement_speed * DIAGONAL_FACTOR; delta_z = movement_speed * DIAGONAL_FACTOR; delta_ry = rotation_speed * DIAGONAL_FACTOR; delta_rx = -rotation_speed * DIAGONAL_FACTOR; break; case DOWN_LEFT: delta_x = -movement_speed * DIAGONAL_FACTOR; delta_z = -movement_speed * DIAGONAL_FACTOR; delta_ry = -rotation_speed * DIAGONAL_FACTOR; delta_rx = rotation_speed * DIAGONAL_FACTOR; break; case DOWN_RIGHT: delta_x = movement_speed * DIAGONAL_FACTOR; delta_z = -movement_speed * DIAGONAL_FACTOR; delta_ry = rotation_speed * DIAGONAL_FACTOR; delta_rx = rotation_speed * DIAGONAL_FACTOR; break; case NONE: return; } sphere_position.x += delta_x; sphere_position.z += delta_z; sphere_rotation.rx += delta_rx; sphere_rotation.ry += delta_ry; } void load_floor_texture(void) { pos.x = 800.0; pos.y = 800.0; pos.z = -35.0; rot.rx = JO_DEG_TO_RAD(90.0); rot.ry = JO_DEG_TO_RAD(0.0); rot.rz = JO_DEG_TO_RAD(0.0); jo_core_tv_off(); jo_enable_background_3d_plane(JO_COLOR_Black); jo_tga_8bits_loader(&floor_image, JO_ROOT_DIR, "FLOOR.TGA", 0); jo_background_3d_plane_a_img(&floor_image, image_pal.id, true, true); jo_core_tv_on(); } jo_palette *my_tga_palette_handling(void) { jo_create_palette(&image_pal); return (&image_pal); } void draw_3d_planes(void) { jo_3d_push_matrix(); { jo_3d_rotate_matrix_rad(rot.rx, rot.ry, rot.rz); jo_3d_translate_matrixf(pos.x, pos.y, pos.z); jo_background_3d_plane_a_draw(true); } jo_3d_pop_matrix(); } void jo_main(void) { jo_core_init(JO_COLOR_Black); jo_set_tga_palette_handling(my_tga_palette_handling); jo_sprite_add_tga(JO_ROOT_DIR, "ROCK.TGA", JO_COLOR_Transparent); load_floor_texture(); jo_3d_camera_init(&cam); jo_core_add_callback(my_draw); jo_core_add_callback(my_gamepad); jo_core_add_callback(draw_3d_planes); jo_core_run(); }
现在的问题是:向左/右移动时,球体不是真实滚动,而是绕错误轴做别扭的枢轴转动,如何修正让它根据移动方向平滑滚动?
解决方案
问题核心在于当前旋转逻辑是固定角度增量,且轴方向不符合真实滚动的物理规律——真实球体滚动时,旋转角度应该和移动距离成正比,旋转轴的方向要与移动方向垂直。
关键修正点:
- 添加球体半径常量:滚动角度 = 移动距离 / 球体半径(弧长公式:
s = rθ→θ = s/r) - 修正旋转轴方向:
- 沿X轴左右移动时,球体应绕Z轴旋转(而非Y轴)
- 沿Z轴前后移动时,球体绕X轴旋转(方向要对应移动方向)
- 对角线移动时,自动计算合成的旋转轴和角度
修改后的代码(重点是my_gamepad函数):
#include <jo/jo.h> #include "sphere.h" jo_camera cam; jo_img_8bits floor_image; jo_palette image_pal; jo_pos3Df sphere_position = {0, 50, 0}; jo_pos3Df pos; jo_rot3Df rot; jo_rot3Df sphere_rotation = {0.0f, 0.0f, 0.0f}; const float SPHERE_RADIUS = 25.0f; // 根据你的球体模型尺寸调整该值 const float MOVEMENT_SPEED = 1.0f; const float DIAGONAL_FACTOR = 0.7071f; // 精确的1/sqrt(2) void my_draw(void) { jo_printf(12, 1, "*Simple 3D demo*"); jo_3d_camera_look_at(&cam); jo_3d_push_matrix(); { jo_3d_translate_matrixf(sphere_position.x, sphere_position.y, sphere_position.z); jo_3d_rotate_matrix_rad(sphere_rotation.rx, sphere_rotation.ry, sphere_rotation.rz); display_sphere_mesh(); jo_3d_set_mesh_texture(&Meshsphere, 0); } jo_3d_pop_matrix(); } void my_gamepad(void) { float delta_x = 0.0f; float delta_z = 0.0f; switch (jo_get_input_direction_pressed(0)) { case LEFT: delta_x = -MOVEMENT_SPEED; break; case RIGHT: delta_x = MOVEMENT_SPEED; break; case UP: delta_z = MOVEMENT_SPEED; break; case DOWN: delta_z = -MOVEMENT_SPEED; break; case UP_LEFT: delta_x = -MOVEMENT_SPEED * DIAGONAL_FACTOR; delta_z = MOVEMENT_SPEED * DIAGONAL_FACTOR; break; case UP_RIGHT: delta_x = MOVEMENT_SPEED * DIAGONAL_FACTOR; delta_z = MOVEMENT_SPEED * DIAGONAL_FACTOR; break; case DOWN_LEFT: delta_x = -MOVEMENT_SPEED * DIAGONAL_FACTOR; delta_z = -MOVEMENT_SPEED * DIAGONAL_FACTOR; break; case DOWN_RIGHT: delta_x = MOVEMENT_SPEED * DIAGONAL_FACTOR; delta_z = -MOVEMENT_SPEED * DIAGONAL_FACTOR; break; case NONE: return; } // 更新球体位置 sphere_position.x += delta_x; sphere_position.z += delta_z; // 根据物理规律计算滚动旋转角度 float rot_x = -delta_z / SPHERE_RADIUS; // 沿Z轴移动 → 绕X轴旋转 float rot_z = delta_x / SPHERE_RADIUS; // 沿X轴移动 → 绕Z轴旋转 // 更新球体旋转角度 sphere_rotation.rx += rot_x; sphere_rotation.rz += rot_z; } // 其余函数保持不变 void load_floor_texture(void) { pos.x = 800.0; pos.y = 800.0; pos.z = -35.0; rot.rx = JO_DEG_TO_RAD(90.0); rot.ry = JO_DEG_TO_RAD(0.0); rot.rz = JO_DEG_TO_RAD(0.0); jo_core_tv_off(); jo_enable_background_3d_plane(JO_COLOR_Black); jo_tga_8bits_loader(&floor_image, JO_ROOT_DIR, "FLOOR.TGA", 0); jo_background_3d_plane_a_img(&floor_image, image_pal.id, true, true); jo_core_tv_on(); } jo_palette *my_tga_palette_handling(void) { jo_create_palette(&image_pal); return (&image_pal); } void draw_3d_planes(void) { jo_3d_push_matrix(); { jo_3d_rotate_matrix_rad(rot.rx, rot.ry, rot.rz); jo_3d_translate_matrixf(pos.x, pos.y, pos.z); jo_background_3d_plane_a_draw(true); } jo_3d_pop_matrix(); } void jo_main(void) { jo_core_init(JO_COLOR_Black); jo_set_tga_palette_handling(my_tga_palette_handling); jo_sprite_add_tga(JO_ROOT_DIR, "ROCK.TGA", JO_COLOR_Transparent); load_floor_texture(); jo_3d_camera_init(&cam); jo_core_add_callback(my_draw); jo_core_add_callback(my_gamepad); jo_core_add_callback(draw_3d_planes); jo_core_run(); }
说明:
- 球体半径调整:
SPHERE_RADIUS需要根据你实际的球体模型尺寸修改,确保滚动速度和移动速度视觉上匹配。 - 旋转方向微调:
rot_x = -delta_z / SPHERE_RADIUS中的负号是为了匹配默认视角下的滚动方向,若显示方向相反,可去掉负号。 - 物理正确性:这种计算方式严格遵循滚动的物理规律,移动距离和旋转角度完全对应,会呈现真实的滚动效果,而非生硬的枢轴转动。
内容的提问来源于stack exchange,提问作者celsowm
相关产品推荐
相关产品推荐

