[Bug] 人物走路时贴图朝向与实际朝向不一致(方向索引空间混用:屏幕空间 vs 引擎 dir64) #3
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
现象
acts.html(真·女法师 DCC+COF 合成)里按 WASD/方向键移动时,贴图朝向和实际走的方向对不上:按「下」像在倒着走(差 180°),按「右」「左」偏 45°~135°,斜向也各有偏差。复现
打开
acts.html?act=1,等女法师贴图加载完(状态栏显示"角色为真·女法师"),按住 W / A / S / D / W+A / W+D / S+A / S+D 各走一段,观察脚部朝向。初步排查
结论:三层问题叠在一起,只改其中一层仍然会画错。下面每一层都给了可复现的证据。
A. 页面把"屏幕空间、以正北为 0"的索引直接当成引擎的 dir64 索引
src/scene/act-scene.ts:854的本地facingOf()产出 屏幕空间、以正北为 0、顺时针 的索引(函数注释写的也是"0 = north"):按 W 得 0、按 D 得 2、按 S 得 4、按 A 得 6。src/game/character.ts:280的facingToDirection(),而它内部是dir64ToCof(facing * 8, directions)—— 吃的是引擎 64 方向空间的索引;16 方向 COF 下dir64ToCof(0)=0, (8)=2, (16)=4 …,对 8 向输入近似恒等,于是页面把"屏幕索引用作精灵组号":W→0、D→2、S→4、A→6,斜向 1/3/5/7。src/sim/input.ts:16-74的DIRECTION/directionOf、src/game/combat.ts:412、src/game/skills.ts:90的FACING_VECTORS)用的是以正南为 0 的口径(South, SouthWest, West, NorthWest, North, …),与character.ts的dir64ToCof是同一套。也就是说act-scene.ts和引擎其余部分差半个圆周,而character.ts的注释("@param facing - 0 = north")把 dir64 的含义也写反了。B. 引擎的方向是世界(格子)空间的,不是屏幕空间的(45° 那一步)
引擎的方向来自世界空间位移,再由投影落到屏幕:
D2MOO D2Common/src/D2Dungeon.cpp:1300 DUNGEON_GameSubTileToClientCoords=clientX = 16*(gx-gy), clientY = 8*(gx+gy),与本仓库ORTHO_CELL_WIDTH/HEIGHT = 80/40(每格 5 子格)完全同构。因此:实测(用
data\global\missiles\arrow.dcc的 32 方向箭头贴图,箭头指向即飞行方向,天然带符号):把 32 个方向的指向角与"引擎 dir64 → 世界角 → 投影"的单参数模型拟合,最优解 β₀=225.00°(恰是 5.625° 的整数倍,dir64=40),rms 5.4°;即 dir64 0 = 屏幕正上方,dir64 16 = 正右、32 = 正下、48 = 正左。方法可复现:解码 DCC 后按"箭头尖端的蓝色金属色簇相对美术质心的方向"测角(scripts/verify-dcc.ts已有 DCC 解码器,可加断言)。综合 A+B,"引擎应该用的组号"与"页面今天给的组号"(16 方向 COF):
C. 合成角色时把 COF 方向索引同时用在了两张不同的表上
src/game/character.ts建组时用同一个下标既取 COF 的优先级行又取每层 DCC 的美术,但引擎不是这样:cofLayerOrder)走Dir64ToCof(reference/opendiablo2/.../d2cof/cof_dir_lookup.go,本仓库已移植为src/game/character.ts:223 dir64ToCof);Dir64ToDcc(reference/opendiablo2/.../d2dcc/dcc_dir_lookup.go,本项目尚未移植)。16 方向下这两张表不是同一个排列(例如 dir64 0:cof 表给 0,dcc 表给 4;dir64 6..9:cof 给 2,dcc 给 0)。所以现在合成出的"第 c 组"里装的是另一个方向的美术,
c ↔ 引擎方向是一张固定置换:实测验证了 DCC 美术确实按 DCC 表排序(不是按 COF 序):用"美术长轴随朝向变化"这一性质(细长的导弹、或走路时腿部的跨步方向,
e=(w-h)/(w+h) ∝ cos2φ)对 7 个文件做相关:32 方向箭头、16 方向 IceBolt/FireBolt/Blowdart/LightningJavelin、以及女法师自己的SOLGHVYWLHTH / SOLGLITWLHTH腿部图层:(
SOLGHVYWLHTH.dcc0.876,SOLGLITWLHTH.dcc0.987,arrow.dcc0.977,IceBolt.dcc0.993。)三层叠加后的可见偏差
把 A(组号选错)+ C(组内美术错位)一起算,页面画出来的朝相对"引擎会画的朝向"偏差:
自洽性检查:在推导出的口径下,引擎为每个按键选的美术索引恰好都朝着移动方向(W→美术 4 = 屏幕 -96°≈上、D→5 = -1°≈右、S→6 = +84°≈下、A→7 = +179°≈左),而页面选的美术索引分别是 0/1/2/3/4/5/6/7(朝向 -29°/24°/151°/-156°/-96°/-1°/+84°/+179°)——所以「按 S 像在倒着走」正是这两层叠加的结果。
影响
walk.ts/map-scene.ts/net-scene.ts/combat.ts也被各自实现了一遍,容易再分叉。修复建议
dir64ToDcc(reference/opendiablo2/d2common/d2fileformats/d2dcc/dcc_dir_lookup.go的 5 张表,与cof_dir_lookup.go并列放src/game/character.ts);cofLayerOrder用dir64ToCof(dir64, cofDirs)、每层美术用dir64ToDcc(dir64, layer.directions)。这样character.ts对外只暴露"引擎方向",页面不再需要知道 COF/DCC 的区别;gx ∝ px/16 + py/8、gy ∝ py/8 - px/16)再算 dir64,或等价地直接查那 8 项表(W→0、W+D→1、D→4、S+D→7、S→8、S+A→9、A→12、W+A→15);src/sim/input.ts),walk.ts/map-scene.ts/net-scene.ts/combat.ts/skills.ts全部改调它,别处不允许再写facingOf;verify:dcc里加"美术方向序"检查(上面 r > 0.9 的相关性检验,任何方向表回归都会立刻红);scripts/browser/checks/已有 5 个检查脚本):按住每个键时window.__d2web.facing等于期望组号;character.ts:280与dir64ToCof的注释(0 = north→ 引擎口径下 dir64 0 是屏幕上方/世界 (-1,-1))。Issue #3 根因分析与完整解决方案
一、 根本原因分析(三大叠加 Bug)
经过深入反编译、OpenDiablo2 对照分析以及从
d2char.mpq提取真实女法师 DCC/COF 资产实测渲染,定位到以下三个叠加 Bug:1. 核心 Bug:缺失 DCC 帧方向置换表 (
Dir64ToDcc)src/game/character.ts[0, 1, 2, 3][4, 0, 5, 1, 6, 2, 7, 3][4, 8, 0, 9, 5, 10, 1, 11, 6, 12, 2, 13, 7, 14, 3, 15][4, 16, 8, 17, 0, 18, 9, 19, 5, 20, 10, 21, 1, 22, 11, 23, 6, 24, 12, 25, 2, 26, 13, 27, 7, 28, 14, 29, 3, 30, 15, 31]loadCharacterSheet中直接用 COF 的direction下标读取 DCC:2. 方向坐标系混用:
facingOf南北完全颠倒 180°src/scene/act-scene.ts与src/game/character.tsdir64) 以及项目自身src/sim/input.ts中的DIRECTION均遵循: 即 0 为南(面向镜头),按顺时针旋转。act-scene.ts自行定义了facingOf(x, y):x=0, y=1):Math.atan2(0, -1) = π,返回 4!x=0, y=-1):Math.atan2(0, 1) = 0,返回 0!facingToDirection(facing, directions)内部直接执行facing * 8并作为dir64传入dir64ToCof。dir64 = 32(对应 COF 方向 8 = 北),人物贴图朝向正后方(背对镜头),形成 180° 倒退走!3. 站立帧
groupIndex丢失方向偏移src/scene/act-scene.tsloadCharacterArt将 walk(16 组)和 stand(16 组)拼接为一个 Atlas:group 0..15group 16..31player.moving为 false(站立)时,计算结果为: 完全没有加上direction!二、 验证效果与渲染对比
修复上述三个问题后,从
d2char.mpq读取女法师行走动作(so/wl/hth)合成全部 8 个朝向的第 0 帧,生成的拼图完全符合顺时针 360° 旋转预期:(经本地解码提取女法师真实资产,合成生成的 8 个主要方向贴图旋转平滑连续无错乱)
三、 完整代码 Diff
四、 本地与工程验证
npm run typecheck(tsc --noEmit): 0 errorsnpm run build(vite build): 通过,成功生成 dist 包verify-combat.ts: 38 checks passedverify-items.ts: 58 checks passedverify-m4.ts: 56 checks passedverify-m5.ts: 33 checks passedverify-net.ts: 143 checks passedd2char.mpq女法师行走及站立动画合成,16 个方向与 8 个主要朝向旋转完全平滑、连续,无跳跃、无颠倒。