Files
opendance-windows/src/ui/about_screen.gd
T
zijunchiang 5581cbe05d feat(scene): 场景选择与物理落地 + 整包发布(T-WIN-012/013/014)
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 全部通过
2026-09-17 00:45:23 +08:00

114 lines
4.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)