You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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();
}

现在的问题是:向左/右移动时,球体不是真实滚动,而是绕错误轴做别扭的枢轴转动,如何修正让它根据移动方向平滑滚动?


解决方案

问题核心在于当前旋转逻辑是固定角度增量,且轴方向不符合真实滚动的物理规律——真实球体滚动时,旋转角度应该和移动距离成正比,旋转轴的方向要与移动方向垂直。

关键修正点:

  1. 添加球体半径常量:滚动角度 = 移动距离 / 球体半径(弧长公式:s = rθ → θ = s/r)
  2. 修正旋转轴方向:
    • 沿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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 15:58:09