[动画] 玩家动画模式:本地移动带动所有玩家播放行走、城镇缺 TN/TW、帧时长写死 #544

Closed
opened 2026-10-02 05:22:02 +00:00 by troytt · 1 comment
Owner

现象

  1. 本地玩家一移动,画面里其他站着不动的玩家(mode NU/TN)也跟着播放行走动画。
  2. 本地玩家在城镇里站立和行走时用的是 NU / WL,没有用城镇专用的 TN / TW 动画。
  3. 玩家动画的帧时长写死在代码里:移动 65 ms/帧,站立 140 ms/帧。不是从 1.13c 数据算出来的。

1.13c 依据

  • 玩家模式(PlrMode.txt):0 DT, 1 NU, 2 WL, 3 RN, 4 GH, 5 TN, 6 TW, 7 A1, 8 A2, 9 BL, 10 SC, …, 17 DD。
    • 仓库里 src/client/scene/world-renderer.ts 的 PLAYER_MODE_TO_CLIP 已经按这个编号映射了 5 → 'tn'、6 → 'tw'。
  • 每个单位的动画模式是它自己的状态(服务器包或该单位自己的路径状态),不会被另一个单位是否在移动影响。

代码现状(main@ff1f8e4)

  • world-renderer.ts L409-L415,drawSceneUnitDrawable 的玩家分支:
    const isMoving = Boolean(ctx.playerMoving) || unit.mode === 2 || unit.mode === 3 || unit.mode === 6
    const requestedClipKey =
      Boolean(ctx.playerMoving) && (unit.mode === 1 || unit.mode === 5) ? (… 'tw' : 'wl') : …
    
    • ctx.playerMoving 来自 main.ts L891 的 session.predictor.getSnapshot().moving,只表示本地玩家是否在移动。
    • 但它被套用到了所有 unitKind === 'player' 的绘制对象上(现象 1)。
  • online-session.ts L1213:本地玩家的 mode 是 predSnap.moving ? (predSnap.running ? 3 : 2) : 1,在城镇里也永远不会是 5/6(现象 2)。
  • world-renderer.ts L435-L436:玩家帧时长 Math.floor(nowMs / 65) / Math.floor(nowMs / 140)(现象 3)。
    • 同一文件里同类的写死常数还有:怪物 75/130 ms、飞弹 50 ms、覆盖层 60 ms。

待确认(不要猜常数)

  • 1.13c 决定玩家城镇模式的位置(D2Game / D2Common 里设置 TN/TW 的函数):
    • 城镇里跑步是 RN 还是另有处理?
    • 进出城镇时 mode 怎么切换?
    • 服务器包(0x0D 带的 mode 字节目前被解码器丢弃;还有 0x0F / 0x10 / 0x15 等)里的 mode 和客户端本地推断谁优先?
  • 动画帧推进的数据源和公式:
    • AnimData.d2 的每方向帧数和速度。
    • PlrMode.txt / MonMode.txt 的相关列。
    • 移动动画的帧率是否和移动速度(0x7F / 走跑速度)挂钩。
    • 需要按 D2Common / D2Client 的实际函数确认后再实现。

期望修复

  • 玩家绘制只看该单位自己的 mode。删掉 ctx.playerMoving 对其他玩家的影响;本地玩家的移动状态通过它自己的 drawable mode 表达。
  • 本地玩家在城镇里按 1.13c 规则使用 TN / TW(以及确认后的跑步模式)。
  • 帧时长改为由 1.13c 数据驱动,缺数据时直接报错,不要静默回退到写死常数。
  • 测试:
    • 远端玩家 NU + 本地玩家移动时,远端仍然是 nu clip。
    • 城镇里本地玩家站立和行走分别是 tn / tw clip。
    • 帧推进和 AnimData 黄金值一致。

相关

  • 在排查 “移动时动画朝向与运动方向不一致” 时发现,按约定单独立项。
    • 朝向修复在 branch fix/unit-facing(commit 0712b80,尚未合并到 main),只修了朝向,没有动上面这些逻辑。
    • 该分支合并后,上面的行号会有少量偏移(online-session.ts 的 mode 那一行会移到 L1258 附近)。
  • 参考:#3、#31、#145
## 现象 1. 本地玩家一移动,画面里其他站着不动的玩家(mode NU/TN)也跟着播放行走动画。 2. 本地玩家在城镇里站立和行走时用的是 NU / WL,没有用城镇专用的 TN / TW 动画。 3. 玩家动画的帧时长写死在代码里:移动 65 ms/帧,站立 140 ms/帧。不是从 1.13c 数据算出来的。 ## 1.13c 依据 - **玩家模式**(`PlrMode.txt`):`0 DT, 1 NU, 2 WL, 3 RN, 4 GH, 5 TN, 6 TW, 7 A1, 8 A2, 9 BL, 10 SC, …, 17 DD`。 - 仓库里 `src/client/scene/world-renderer.ts` 的 `PLAYER_MODE_TO_CLIP` 已经按这个编号映射了 `5 → 'tn'`、`6 → 'tw'`。 - 每个单位的动画模式是它自己的状态(服务器包或该单位自己的路径状态),不会被另一个单位是否在移动影响。 ## 代码现状(main@ff1f8e4) - [`world-renderer.ts` L409-L415](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/ff1f8e4cbec3e0236fa8d7ef632410191fa53f55/src/client/scene/world-renderer.ts#L409-L415),`drawSceneUnitDrawable` 的玩家分支: ```ts const isMoving = Boolean(ctx.playerMoving) || unit.mode === 2 || unit.mode === 3 || unit.mode === 6 const requestedClipKey = Boolean(ctx.playerMoving) && (unit.mode === 1 || unit.mode === 5) ? (… 'tw' : 'wl') : … ``` - `ctx.playerMoving` 来自 [`main.ts` L891](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/ff1f8e4cbec3e0236fa8d7ef632410191fa53f55/src/client/main.ts#L891) 的 `session.predictor.getSnapshot().moving`,只表示**本地**玩家是否在移动。 - 但它被套用到了**所有** `unitKind === 'player'` 的绘制对象上(现象 1)。 - [`online-session.ts` L1213](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/ff1f8e4cbec3e0236fa8d7ef632410191fa53f55/src/client/session/online-session.ts#L1213):本地玩家的 mode 是 `predSnap.moving ? (predSnap.running ? 3 : 2) : 1`,在城镇里也永远不会是 5/6(现象 2)。 - [`world-renderer.ts` L435-L436](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/ff1f8e4cbec3e0236fa8d7ef632410191fa53f55/src/client/scene/world-renderer.ts#L435-L436):玩家帧时长 `Math.floor(nowMs / 65)` / `Math.floor(nowMs / 140)`(现象 3)。 - 同一文件里同类的写死常数还有:怪物 75/130 ms、飞弹 50 ms、覆盖层 60 ms。 ## 待确认(不要猜常数) - 1.13c 决定玩家城镇模式的位置(D2Game / D2Common 里设置 TN/TW 的函数): - 城镇里跑步是 RN 还是另有处理? - 进出城镇时 mode 怎么切换? - 服务器包(`0x0D` 带的 mode 字节目前被解码器丢弃;还有 `0x0F` / `0x10` / `0x15` 等)里的 mode 和客户端本地推断谁优先? - 动画帧推进的数据源和公式: - `AnimData.d2` 的每方向帧数和速度。 - `PlrMode.txt` / `MonMode.txt` 的相关列。 - 移动动画的帧率是否和移动速度(`0x7F` / 走跑速度)挂钩。 - 需要按 D2Common / D2Client 的实际函数确认后再实现。 ## 期望修复 - 玩家绘制只看该单位自己的 `mode`。删掉 `ctx.playerMoving` 对其他玩家的影响;本地玩家的移动状态通过它自己的 drawable `mode` 表达。 - 本地玩家在城镇里按 1.13c 规则使用 TN / TW(以及确认后的跑步模式)。 - 帧时长改为由 1.13c 数据驱动,缺数据时直接报错,不要静默回退到写死常数。 - 测试: - 远端玩家 NU + 本地玩家移动时,远端仍然是 `nu` clip。 - 城镇里本地玩家站立和行走分别是 `tn` / `tw` clip。 - 帧推进和 AnimData 黄金值一致。 ## 相关 - 在排查 “移动时动画朝向与运动方向不一致” 时发现,按约定单独立项。 - 朝向修复在 branch `fix/unit-facing`(commit `0712b80`,尚未合并到 main),只修了朝向,没有动上面这些逻辑。 - 该分支合并后,上面的行号会有少量偏移(`online-session.ts` 的 mode 那一行会移到 L1258 附近)。 - 参考:#3、#31、#145
Author
Owner

更新(2026-10-02):fix/unit-facing 已合并到 main(8fcd223)。正文里的 0712b80 现在是 dd775c8。

本 issue 的问题在 main@8fcd223 上仍然存在。更新后的行号:

同一区域还有一处问题。这是读代码确认的,没有改:

  • 本地玩家死亡时 selfMode = 12。
  • 按 PlrMode.txt(plr-mode.ts),玩家的 12 是 KK(Kick)。死亡应该是 DT(0)或 DD(17)。12 是 MonMode.txt 里的 DD。
  • PLAYER_MODE_TO_CLIP 里没有 12,于是回落到 nu;又因为 isCorpse,取的是最后一帧。结果是死亡的本地玩家画成站立姿势。
**更新(2026-10-02)**:`fix/unit-facing` 已合并到 main(`8fcd223`)。正文里的 `0712b80` 现在是 [`dd775c8`](https://git.projectdiablo2.cn/troytt/diablo2-web/commit/dd775c8156ad9ef2e0b30605f7c33523027bbd4f)。 本 issue 的问题在 main@`8fcd223` 上仍然存在。更新后的行号: - [`world-renderer.ts` L410-L417](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/8fcd2238cc70a054a2051845d247a3a3bc0024cd/src/client/scene/world-renderer.ts#L410-L417):`isMoving` 和 `requestedClipKey` 仍然取全局的 `ctx.playerMoving`。所以本地玩家一移动,所有玩家都切到 WL/TW。 - 本地玩家的 mode 现在由 [`online-session.ts` L1325-L1335](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/8fcd2238cc70a054a2051845d247a3a3bc0024cd/src/client/session/online-session.ts#L1325-L1335) 的 `selfMode` 决定(`8dac79e` 引入)。 同一区域还有一处问题。这是读代码确认的,没有改: - 本地玩家死亡时 `selfMode = 12`。 - 按 `PlrMode.txt`([`plr-mode.ts`](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/8fcd2238cc70a054a2051845d247a3a3bc0024cd/src/common/units/plr-mode.ts)),玩家的 12 是 **KK(Kick)**。死亡应该是 DT(0)或 DD(17)。12 是 `MonMode.txt` 里的 DD。 - `PLAYER_MODE_TO_CLIP` 里没有 12,于是回落到 `nu`;又因为 `isCorpse`,取的是最后一帧。结果是死亡的本地玩家画成站立姿势。
Sign in to join this conversation.
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: troytt/diablo2-web#544
No description provided.