Adapt Android mobile controls and native client UI

This commit is contained in:
shen
2026-09-28 19:39:13 -07:00
parent a1ae69aa7a
commit 3c52ced02e
180 changed files with 688 additions and 23782 deletions
-73
View File
@@ -1,73 +0,0 @@
# 渲染回归入口
第一批用例针对 R1 场景装配与 R2 角色选择,2026-09-08 开始执行。未宣称 40250 像素一致或最终包通过。
在仓库根目录运行:
本地 macOS 修改 C++ 后先执行 `bash script/rendering_test.sh`:它构建并同步 Godot 实际加载的 debug 路径,重签名后运行 CPU 绕序回归与选角截图。仅构建 Release 不会更新本地 Godot 的 debug 扩展。可传 `--hair=1001`;截图与哈希报告在 `build/rendering/selection-<timestamp>-<pid>/`。`--job=0..7` 与 `--main-part=0` 可复现无装备的八种族选择矩阵;`MTGODOT_GPUSKIN=1` 验证 GPU 路径。`--no-hair` 和 `--solid-body` 仅用于隔离诊断,其画面不能当作普通角色验收。
```bash
godot --headless --path project --script main_character_sync_test.gd
godot --headless --path project --script gamescene_test.gd
godot --path project --script gamescene_visual_test.gd
godot --path project --script gamescene_visual_test.gd -- --c1
```
- `main_character_sync_test.gd` 使用可控模型工厂,覆盖晚到实体/缺 race、网络重入、重复事件、移动不被回滚、失败节流与重试上限、VID 变化导致旧构建失效。通过标志是 `failures=0`,不能只看 Godot 退出码。
- `gamescene_test.gd` 使用不存在的地图,预期有加载错误日志;它只验证无地图装配,不能当作画面正常证据。
- `gamescene_visual_test.gd` 使用真实资产和实际 GameScene,通过 FakeClient 延迟提供 race=0 主角数据,**不发**主角信号。无账号和服务器写入。
- 默认先加载 A1;`--c1` 随后提供 C1 服务器位置,用来验证晚到坐标能纠正地图选择。
- `gamescene_visual_test.gd` 除位置/地图/表面数外还断言 `feet_gap`:把动画冻结在 0.5 s
后取全部 MeshInstance3D 世界包围盒的最低点,减去该处地形高度,要求 |gap| ≤ 0.03 m
(R2-01 悬空修复的数值守卫;A1 与 C1 实测均为 0.001373 m)。报告里同时记录
`feet_y` / `feet_gap` / `feet_gap_max`。
- 视觉用例输出 `build/rendering/gamescene-<timestamp>/game.png` 与 `report.json`。可用环境变量 `MT_RENDER_OUTPUT` 指定独立目录;复用路径会覆盖同名报告,建议每次新目录。
- 报告包含位置、相机、地图加载计数、模型表面数、引擎、关键脚本/扩展/模型路径哈希。资产依赖尚未穷举;这只是 R0 部分基线。
- 截图尺寸为 1280×720,角色待机固定在 0.5 秒。地形/环境动态因素尚未完全冻结,因此暂不设置像素误差硬阈值。
- 测试不配置学习技能,截图中的空快捷槽不代表技能图标验证成功。
## 不启动游戏的模型渲染回归(model_render_test.gd)
```bash
godot --path project --script model_render_test.gd # 八职业,判定
godot --path project --script model_render_test.gd -- --race 0
godot --path project --script model_render_test.gd -- --bless # 重建基线
```
无地图、无网络、无服务器:直接用 `ui/player_view.gd` 离线装配一个角色,`wait`
动画冻结在 0.5 s,固定灯光(平行光 `(-42,155,0)` energy 1.1 + 环境色
`(0.6,0.62,0.68)` energy 0.8)、洋红背景、512×512。与 `char_bench_test.gd` 的区别
是它对像素算指标并断言,退出码即结论;bench 仍然只产图供人看。
指标(每个 race)与判定方式:
| 指标 | 含义 | 判定 |
| --- | --- | --- |
| `feet_gap_m` | 最低蒙皮顶点到 y=0 | 绝对,\|gap\| ≤ 0.03 m(实测 −0.0007…0.0030) |
| `silhouette_px` | 正面剪影像素数 | 绝对 ≥ 3000;且对基线 ±3% |
| `head_cover` | 近景机位下发型盖住头部像素的比例(只统计 no_hair 里属于人体的像素) | 对基线 ±0.03 |
| `hair_mean_lum` | 只画发型时发型像素平均亮度 | 对基线 ±0.006 |
| `hair_mean_lum_ctrl` | 同上,但换回 R2-02 修复前的 `StandardMaterial3D(roughness=1)` | 负对照:必须比生产值高 ≥ 0.008 |
| `hair_lum_p99` / `hair_bright_frac` | 亮度 99 分位 / 超过 0.20 的像素占比 | 仅记录,不判定 |
为什么用平均亮度而不是「有多少像素炸白」:发色本身差别极大(武士近黑
`mean 0.061`,修罗银白 `mean 0.260`),任何跨职业的绝对亮度上限都不成立。平均
亮度对「整片发型被额外高光抬亮」敏感,基准差异由按职业记录的基线吸收。
负对照不是摆设:它每次都真的用旧材质再渲一遍,保证这条指标现在仍分得开好坏
(例如有人把灯光调暗到两边都测不出高光时会直接失败)。八职业实测旧材质相对
新材质的抬升是 +0.0109 … +0.0371,最小值 0.0109 给阈值 0.006/0.008 留了余量。
已验证它确实能抓到目标缺陷:把 `Metin2Model::_load_hair()` 临时改回
`StandardMaterial3D(roughness=1)` 重新编译后,**八个职业全部失败**
(`hair_mean_lum` 偏离基线 0.0109…0.0371);改回 `mtgodot::make_material` 后
`PASS`。
基线在 `test/rendering/model_baseline.json`,含引擎版本。改模型、改发型材质或改
这套灯光都会让基线失效,需要人工看过渲染结果再 `--bless`,并在
`docs/RENDERING-REPAIR-PLAN.md` 的执行记录里写清重建原因。
覆盖边界:只有离线单角色装配。不含地图、地形接触、真实光照昼夜、装备叠加、
40250 像素比对,也不能替代 `gamescene_visual_test.gd` 与导出包验收。
已知后续:脸部/脚底近景和区域差异器、完整资产哈希、四职业 GR2/GLB 对照、技能状态与图标用例、真实联机及普通 macOS `.app` 验收。
-1
View File
@@ -1 +0,0 @@
.godot/
-31
View File
@@ -1,31 +0,0 @@
# Particle shader lifetime reproducer
Independent Godot project: no game assets, autoloads, GDExtension, network,
EffectPlayer, or particles emitter. Eight short-lived ParticleProcessMaterials
cycle through three emission shapes. Weak references must all be dead before
exit; a RenderingServer synchronization also runs before quitting.
Export this project with the installed macOS template using `--export-release`
and `--export-debug` into separate directories under the repository's `build/`.
Run each exported executable and preserve both stdout and stderr until process
exit. A run is clean only if `ISOLATED_PARTICLE_EXIT alive=0` is present and no
shader/RID error follows. The success marker alone is not a clean-exit verdict.
2026-09-10, Godot 4.7.1 official, Apple M4/Metal:
- Release: zero surviving materials, three ParticlesShaderRD/Shader RID leaks.
- Debug: zero surviving materials, no shader/RID leak.
Logs: `build/rendering/particle-isolated-release.log` and
`build/rendering/particle-isolated-debug.log`. Path overrides (`--main-pack`,
external `--script`) are not supported by the installed release template;
export a standalone app instead. This project contains no copyrighted artwork
and may be used as a minimal upstream reproduction if separately authorized.
2026-09-11: an unmodified same-revision arm64 release build using
`debug_symbols=yes optimize=speed lto=none vulkan=no` exits cleanly in this
project and in two complete game package checks. Rebuild it with
`bash script/build_macos_release_engine.sh arm64` from the repository root.
This is a verified alternative build path, not proof of a specific compiler
bug or a source fix to the installed official template. Intel and Vulkan are
outside this result.
@@ -1,9 +0,0 @@
[preset.0]
name="macOS"
platform="macOS"
runnable=true
export_filter="all_resources"
include_filter=""
exclude_filter=""
[preset.0.options]
application/bundle_identifier="org.metin2.particleprobe"
-17
View File
@@ -1,17 +0,0 @@
extends Node
var refs: Array[WeakRef] = []
func _ready() -> void: call_deferred("run")
func run() -> void:
for i in 8: create_material(i)
for i in 10: await get_tree().process_frame
RenderingServer.force_sync()
var alive := 0
for reference in refs:
if reference.get_ref() != null: alive += 1
print("ISOLATED_PARTICLE_EXIT alive=", alive)
get_tree().quit(1 if alive else 0)
func create_material(index: int) -> void:
var material := ParticleProcessMaterial.new()
material.emission_shape = index % 3
material.get_rid()
refs.append(weakref(material))
-1
View File
@@ -1 +0,0 @@
uid://b3vjaypwq6yx6
-4
View File
@@ -1,4 +0,0 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://main.gd" id="1"]
[node name="Probe" type="Node"]
script = ExtResource("1")
@@ -1,9 +0,0 @@
config_version=5
[application]
config/name="Particle lifetime isolation"
run/main_scene="res://main.tscn"
[display]
window/size/viewport_width=320
window/size/viewport_height=240
[rendering]
textures/vram_compression/import_etc2_astc=true