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 全部通过
114 lines
4.3 KiB
GDScript
114 lines
4.3 KiB
GDScript
class_name AboutScreen
|
||
extends Control
|
||
|
||
## T-WIN-010:关于页面 + 开源声明(可滚动)
|
||
## T-WIN-012:关于清单展示当前版本号(文档 §11.1:Windows 端在「关于」清单)
|
||
##
|
||
## 文档 §8.3 要求 Windows 客户端与 Android App 的「关于」页展示同款声明。
|
||
##
|
||
## 版本号**唯一来源**是 project.godot 的 application/config/version
|
||
## (与 project.yaml 的 version、导出产物名 opendance-windows-vX.Y.Z.zip、Git tag 一致),
|
||
## 本文件不再手写一份(文档 §11.2 第 1 条硬性要求)。
|
||
|
||
const UiKit = preload("res://src/ui/ui_kit.gd")
|
||
const Credits = preload("res://src/ui/credits.gd")
|
||
|
||
var _on_back: Callable = Callable()
|
||
|
||
|
||
func _init(on_back: Callable = Callable()) -> void:
|
||
_on_back = on_back
|
||
|
||
|
||
## 当前运行版本(构建期由 project.godot 注入,运行期读取)
|
||
static func app_version() -> String:
|
||
return str(ProjectSettings.get_setting("application/config/version", "0.0.0"))
|
||
|
||
|
||
## 取 AppConfig 自动加载实例。测试在 `--script` 模式下没有 autoload,
|
||
## 因此这里必须容错返回 null,而不是直接引用 AppConfig 造成解析期错误。
|
||
static func _app_config() -> Node:
|
||
var loop := Engine.get_main_loop()
|
||
if loop is SceneTree:
|
||
return (loop as SceneTree).root.get_node_or_null("AppConfig")
|
||
return null
|
||
|
||
|
||
static func _config_call(method: String, fallback: String) -> String:
|
||
var cfg := _app_config()
|
||
if cfg != null and cfg.has_method(method):
|
||
return str(cfg.call(method))
|
||
return fallback
|
||
|
||
|
||
static func _config_property(name: String, fallback: String) -> String:
|
||
var cfg := _app_config()
|
||
if cfg != null:
|
||
var value: Variant = cfg.get(name)
|
||
if value != null:
|
||
return str(value)
|
||
return fallback
|
||
|
||
|
||
## 「关于」清单的一行行内容:版本 / 引擎 / 契约 / 运行地址 / 隐私
|
||
static func about_manifest() -> Array:
|
||
return [
|
||
{"label": "客户端版本", "value": "v%s" % app_version()},
|
||
{"label": "运行引擎", "value": "Godot %s" % str(Engine.get_version_info().get("string", "?"))},
|
||
{"label": "契约版本", "value": Credits.summary()},
|
||
{"label": "接收协议", "value": "websocket-keypoints v%s(33 点 / 239 字节帧)" % Credits.version_of("websocket-keypoints")},
|
||
{"label": "接收地址", "value": _config_call("listen_url", "ws://0.0.0.0:8765/pose")},
|
||
{"label": "服务端地址", "value": _config_property("api_base_url", "http://127.0.0.1:3000")},
|
||
{"label": "隐私说明", "value": "仅接收关键点,不接收、不存储任何视频画面"},
|
||
]
|
||
|
||
|
||
func _ready() -> void:
|
||
set_anchors_preset(Control.PRESET_FULL_RECT)
|
||
|
||
var root := UiKit.vbox(10)
|
||
root.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||
root.offset_left = 24
|
||
root.offset_top = 18
|
||
root.offset_right = -24
|
||
root.offset_bottom = -18
|
||
add_child(root)
|
||
|
||
var header := UiKit.hbox(12)
|
||
header.add_child(UiKit.title("关于 OpenDance Studio"))
|
||
header.add_child(UiKit.expander())
|
||
var back := UiKit.button("返回主菜单", func(): if _on_back.is_valid(): _on_back.call())
|
||
header.add_child(back)
|
||
root.add_child(header)
|
||
|
||
var info := UiKit.panel()
|
||
var info_box := UiKit.vbox(4)
|
||
info_box.add_child(UiKit.heading("清单"))
|
||
for item in about_manifest():
|
||
var row := UiKit.hbox(8)
|
||
var key := UiKit.label("%s:" % str(item["label"]), UiKit.FONT_SMALL, UiKit.COLOR_MUTED)
|
||
key.custom_minimum_size = Vector2(96, 0)
|
||
row.add_child(key)
|
||
var value := UiKit.label(str(item["value"]), UiKit.FONT_SMALL)
|
||
value.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
value.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
row.add_child(value)
|
||
info_box.add_child(row)
|
||
info_box.add_child(UiKit.spacer(4))
|
||
info_box.add_child(UiKit.muted("摄像头跳舞评分系统:接收 Android 辅助 App 通过 WebSocket 推送的 33 点骨骼关键点,驱动 VRM 角色并实时 DTW 评分。"))
|
||
info.add_child(info_box)
|
||
root.add_child(info)
|
||
|
||
var credits_panel := UiKit.panel()
|
||
var credits_box := UiKit.vbox(4)
|
||
credits_box.add_child(UiKit.heading("开源项目声明"))
|
||
credits_box.add_child(UiKit.muted("共 %d 项;与 contracts/open-source-credits.md 及 THIRD_PARTY.md 保持一致。" % Credits.entry_count()))
|
||
var text := UiKit.rich_text()
|
||
text.bbcode_enabled = true
|
||
text.text = Credits.to_bbcode()
|
||
text.custom_minimum_size = Vector2(0, 320)
|
||
credits_box.add_child(text)
|
||
credits_panel.add_child(credits_box)
|
||
credits_panel.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||
root.add_child(credits_panel)
|