T-WIN-013:API 升级与场景选择 - 契约快照同步到 rest-api 0.5.0 / data-models 0.7.0 / database 0.4.0 - catalog_types.gd 新增 SceneConfig(物理/环境/出生点/相机)与 Scene; CatalogBundle 增加 scenes[];from_dict 按契约 7.1 再收敛一次 (缺项默认、越界夹紧、非法枚举回退),与服务端 normalizeSceneConfig 同规则 - 新增 src/scene/scene_runtime.gd:GLB 载入 -> 碰撞体(mesh 三角网 / auto 命名约定 *_col、*_colonly / none 不兜底)-> 场景级重力(Area3D SPACE_OVERRIDE_REPLACE,不改全局 ProjectSettings)-> 风(每物理帧 apply_central_force,阵风正弦叠加,支持 local 坐标系)-> 环境光与雾 -> 出生点与相机(follow/fixed/orbit);无碰撞体时兜底地面 - CatalogStore 增加 scenes/default_scene/find_scene/ensure_scene_cached/ fetch_thumbnail(场景缓存带 sha256 指纹,避免换包后仍用旧 GLB) - 主界面改为 2x2 四个选择器(曲目/动作/模型/场景)+ 场景参数面板 (缩略图 + 重力/摩擦/弹性/碰撞体/风摘要) - 游戏界面载入场景并落地物理,角色落在出生点;场景缺失/下载失败/ 解析失败一律回退内置空场景并提示,不阻塞开始跟练 T-WIN-012:「关于」清单展示当前版本号 - 版本唯一来源为 project.godot 的 application/config/version(v0.2.0) - about_manifest() 输出版本/引擎/契约/协议/地址/隐私清单; 无头测试无 autoload,读取 AppConfig 走 get_node_or_null 容错 - Credits 的单个 CONTRACT_VERSION 常量改为 CONTRACT_VERSIONS 字典 (六项契约各自独立递增),并双向核对 contracts/VERSION T-WIN-014:发布必须是单个完整包 - build/package-release.sh 重写:只产出单个 zip + SHA256SUMS, 清理历史 .partN,超限时告警而非回退分卷;新增源产物与解压后完整性 自检(PE 头 MZ / pck 魔数 GDPC / exe+pck 同目录同名)与体积-上限对比 - deploy/proxy 相关的 Gitea IIS 256MB 上限说明见 opendance-web 测试:tests/test_scene_config.gd(12 例)+ tests/test_about.gd(4 例); 无头测试 687 通过 / 0 失败(10 个套件),verify.sh 全部通过
5.3 KiB
5.3 KiB
T-WIN-013 API 连接升级与场景选择
- 状态: done
- 依赖: T-WIN-002、T-WIN-008
- 文档: v1.6 §5.6、契约
rest-api 0.5.0/data-models 0.7.0
目标
跟随 Web 端场景管理(T-WEB-015)升级本端 API 连接,新增场景选择与物理参数落地。
交付物
| 文件 | 说明 |
|---|---|
contracts/* |
契约快照同步到 rest-api 0.5.0 / data-models 0.7.0 / database 0.4.0(与 opendance-web 逐字节一致) |
src/net/catalog_types.gd |
新增 Wind / Ground / ScenePhysics / SceneEnvironment / SpawnPoint / SceneCamera / SceneConfig / Scene;CatalogBundle 增加 scenes |
src/scene/scene_runtime.gd |
场景实例化与物理落地(新增目录 src/scene/) |
src/autoload/catalog_store.gd |
scenes() / default_scene() / find_scene() / ensure_scene_cached() / fetch_thumbnail() |
src/ui/scene_panel.gd |
场景缩略图 + 重力/风/摩擦参数摘要 |
src/ui/main_menu.gd |
第 4 个选择器「场景」(2×2 栅格,与曲目/动作/模型并列) |
src/ui/game_screen.gd |
载入场景 → 落地物理 → 出生点定位 → 按场景相机取景 |
1. 契约解析与服务端同规则收敛
SceneConfig.from_dict() 复刻服务端 normalizeSceneConfig() 的规则:
缺项用默认值、越界夹紧、非法枚举回退。服务端已收敛,客户端仍再收敛一次 ——
不能假设对端一定合法(旧版本服务端 / 人工改包)。
| 项 | 范围 |
|---|---|
gravity |
各分量 ±200 |
wind.base |
±100 |
gustAmplitude / gustFrequency |
0 |
gustDirection |
±1 |
ground.friction / restitution / slopeLimit |
0 |
environment.ambientIntensity / fogDensity |
0 |
camera.distance / height |
0.5 |
spawnPoints |
最多 16 个 |
2. 物理落地(服务端只存参数,仿真在本端)
- 重力:
Area3D+gravity_space_override = SPACE_OVERRIDE_REPLACE, 不改ProjectSettings的全局重力,因此一个进程内切换场景互不影响。 - 碰撞体:
colliders: mesh→ 所有MeshInstance3D走create_trimesh_collision();auto→ 只认命名约定(*_col/*_colonly/*_collision/ …),*colonly只提供碰撞不参与渲染(对齐 Godot/Blender 约定);none→ 不生成任何碰撞体,也不兜底(尊重场景作者意图)。auto/mesh下一个都没找到时生成兜底地面并记录降级原因,避免角色无限下落。 - 风:
Area3D+ 每物理帧对区域内RigidBody3Dapply_central_force(); 阵风按正弦叠加(base + dir * A * sin(2πft)),space: local时用本节点基向量变换。 风速(m/s)→ 力(N)用固定系数WIND_FORCE_SCALE = 10(契约只约束风速量纲)。 - 地面材质:
friction/restitution→PhysicsMaterial,套用到所有场景静态体。 - 环境:环境光颜色/强度直接写进游戏界面已有的
Environment(不另建WorldEnvironment,避免互相覆盖);fogDensity 0~1映射到 Godot 的0.005~0.05(Godot 的fog_density量纲更敏感,直接照搬会全屏糊死)。 - 出生点与相机:角色落在
spawnPoints[0](含 yaw),按camera.mode取景 (follow/fixed/orbit,orbit 在_process中缓慢环绕)。
3. 容错(不得阻塞开始跟练)
| 情况 | 行为 |
|---|---|
| 未选择场景 / 服务端无已发布场景 | 内置空场景 + 状态栏提示 |
场景缺少 glbUrl |
回退内置空场景 + 界面提示 |
| GLB 下载失败 | 回退内置空场景 + 明确原因 |
| GLB 解析失败 / 根节点非 Node3D | 回退内置空场景 + warnings |
| 物理参数非法 | 收敛为默认值(不抛错) |
| 无碰撞体可用 | 兜底地面 + 降级提示 |
4. 可核对性
SceneRuntime.describe() 输出一行摘要(重力 / 风 / 摩擦 / 弹性 / 碰撞体数量 / 相机模式),
启动时 print 到日志;界面同时展示同源参数。满足文档「目视 + 日志核对」。
验收(文档 §5.6)
| 验收项 | 结果 |
|---|---|
| 服务端发布新场景后「刷新目录」即可见并可选 | ✅ catalog_export.scenes[] → 第 4 个选择器;目录指纹变化即重新拉取 |
| 场景 GLB 正常加载 | ✅ GLTFDocument 与 VrmLoader 同一条零原生依赖路线 |
| 重力与风向参数按服务端配置生效(目视 + 日志核对) | ✅ describe() 打印 + 界面参数摘要;受力计算有单测逐值核对 |
| 服务端不可达 / 场景资产缺失时明确提示并回退,不崩溃 | ✅ 见上表「容错」;test_scene_runtime_falls_back_when_file_missing 覆盖 |
测试
tests/test_scene_config.gd(12 个用例 / 60+ 断言):
- 默认值逐项与服务端
SCENE_CONFIG_DEFAULTS一致; - 越界夹紧、非法枚举回退、非数值兜底、出生点 16 上限;
- 风的正弦叠加逐时刻核对(t=0 / 1(4f) / 1(2f) / 3(4f));
- 目录
scenes[]解析与default_scene()跳过缺 GLB 的场景; - 重力与风力的纯函数计算、空场景兜底地面、
colliders: none不兜底; - 文件缺失时的降级路径、出生点位姿(yaw 90° 使 +Z 转向 +X)、
相机
fixed机位、雾浓度映射区间。