Skip to content

VRM 引导移动设计计划 ​

目标 ​

为 NEKO 增加桌宠式“目标选择→自动移动”功能,首要适配两类模型:

  1. 用户的“人物模型”(当前已知为 VRM 0.x,约 51 个 humanoid 骨骼;表情绑定较少)。
  2. YUI 使用的 VRM 模型(以运行时实际加载的文件为准,不把模型文件名或路径写死)。

两套模型都必须通过同一套交互和动作流程;模型差异通过 VRM 能力探测、朝向校准和动作兼容性结果处理。

已确定的操作规则 ​

  • 按住 F:进入目标选择模式。
  • F 模式下左键点击一次:选定一个目标位置并开始移动。
  • 松开左键:移动继续。
  • 松开 F:只退出目标选择模式,已经选定的移动继续执行。
  • 再次按住 F 并左键点击:替换目标,角色从当前位置转向新目标。
  • 到达目标:停止移动并恢复适用的待机动作。
  • 普通模式下的左键拖拽、右键旋转和滚轮缩放保持原有行为。

目标选择状态和移动状态必须分开维护。释放 F 不得清除 moveTarget。

当前实现基础 ​

  • static/vrm/vrm-interaction.js 已有 VRM 射线命中、拖拽平移、边界限制和每帧更新入口。
  • static/vrm/vrm-manager.js 已能访问当前 VRM,并提供位置设置接口。
  • static/vrm/vrm-animation.js 已能加载和混合 VRMA。
  • static/vrm/motion/player.js 已有动作播放所有权和动作生命周期管理。
  • 当前代码没有目标移动控制器、可行走场景地图或完整的走路动作状态机。

已执行部分(当前代码) ​

  • F 按下进入目标模式,画布左键一次选定屏幕平面目标;松开 F 不会取消已确认的移动。
  • 目标由相机反投影并经过现有可见边界限制,移动在 update(delta) 中按帧推进,抵达后吸附到目标并保存位置。
  • 移动中再次使用 F + 左键会替换目标;普通左键命中模型后拖拽会接管并取消自动移动。
  • 移动开始尝试播放 world-walk.vrma.gz,移动结束停止该动作并交还 NekoMotion 待机所有权;锁定、清理和模型销毁路径会释放动作占用。
  • 当前只播放循环 world-walk,尚未接入起步、停止和转向 clip 状态机;动作加载或骨骼不兼容时回退到纯位移。没有完整 NekoMotion 动作恢复运行时的预览页面只移动位置,保留原动作。到达转身使用共享帧调度,转身途中换目标沿用原恢复朝向。两个目标模型的兼容性仍需实测。

状态机 ​

text
普通模式
  └─ keydown F → 目标选择模式

目标选择模式
  ├─ 左键点击 → 保存目标 → 移动中
  └─ keyup F → 普通模式(移动不取消)

移动中
  ├─ F + 左键点击 → 替换目标
  ├─ 普通左键拖拽 → 用户接管并取消自动移动
  ├─ 到达阈值 → 停止、恢复待机
  └─ 切换/锁定/销毁 → 安全取消

建议的运行时字段:

js
targetMode       // F 是否按住
moveTarget       // 当前目标的世界坐标
isMoving         // 是否正在自动移动
movementToken    // 使旧目标或旧动画回调失效
movementMaxSpeed // 最大世界单位/秒,范围 0~0.9
movementVelocity // 当前速度,按加速度/刹车动态变化
movementFacing   // 是否由移动方向驱动身体朝向

目标坐标方案 ​

第一阶段不引入 Hanami 的房间、座位和寻路系统,使用当前桌宠画布的屏幕平面:

  1. 从画布事件取得点击坐标。
  2. 用相机反投影得到与屏幕平行的移动平面目标。
  3. 以模型脚底参考点对齐目标,避免“点哪里就把身体中心挪到哪里”。
  4. 通过现有边界函数限制可达区域。
  5. 记录目标一次;鼠标后续移动不改变目标。

屏幕平面允许上下移动,因此不能把世界坐标的脚底高度固定为常数。以后加入真实地面时,再改为地面射线求交和可行走地图。

移动控制 ​

  • 在 VRM 管理器已有的每帧更新中按 delta 推进位置,保证帧率无关;速度从 0 按加速度提升到最大值,接近目标时按刹车距离减速到 0,不使用固定速度。
  • 目标距离小于到达阈值时吸附并结束,避免抖动和越过目标。
  • 新目标从当前位置重新计算方向,不瞬移。
  • 移动期间让身体朝向移动方向;当移动结束时恢复当前模型的朝向策略。
  • 位置在新行程开始、到达结束及模型切换/销毁打断移动时保存;途中目标替换不追加中间快照,避免连续点选堆积保存队列。模型切换前同步捕获旧模型最终姿态,异步保存仍归属旧模型,不能每帧写配置。
  • 页面失焦时清除 targetMode,保留并继续执行已确认的移动;模型切换、锁定和销毁时取消移动。

动作资源 ​

已导入 static/vrm/animation/ 的 Hanami/Overte 移动资源:

  • world-walk.vrma.gz

来源和许可记录在 static/vrm/animation/HANAMI_VRMA_NOTICE.md。该动作保持在移动域,不加入普通聊天动作候选。Hanami 的目录说明 world- 域包含起步、步行、停止和转向动作;动作文件为 VRMA humanoid clip,模型无须专门绑定即可尝试播放,但仍必须对两个目标模型实测。

后续完整状态机的动作播放策略(尚未接入) ​

当前仓库只保留 world-walk.vrma.gz。下列起步、停止和转向资源已移除,后续实现前需重新导入并记录许可,再完成模型兼容性验证。

text
开始移动       → world-walk-start
开始持续移动   → world-walk
接近目标       → world-walk-stop-small(适用时)
正常停止       → world-walk-stop
方向变化       → world-turn-left / world-turn-right(需要时)
到达后         → 当前模型可用的 rest/idle

实现前必须检查:

  • 两个模型能否匹配 hips、腿、脚和躯干轨道。
  • VRM 0.x 与 VRM 1.0 的归一化骨骼处理是否一致。
  • world-walk 是否循环自然、是否含 hip translation;若动作本身含位移,必须决定由动画还是控制器负责根位移,不能双重移动。
  • 起步、循环、停止的相位衔接是否出现滑步、脚底穿透或突然跳姿势。
  • 两个模型的朝向基准是否相反,并分别记录校准值。

两个适配目标的专门验收 ​

人物模型 ​

  • 确认 VRM 0.x humanoid 映射完整性,重点检查 hips、left/rightUpperLeg、left/rightLowerLeg、left/rightFoot、spine、chest。
  • 由于表情绑定较少,移动验收重点放在身体动作、脚底接触、朝向和待机恢复;不把表情表现当作移动成功条件。
  • 检查当前模型的缩放、脚底高度和屏幕边界校准。

YUI VRM ​

  • 从 YUI 当前加载实例读取 VRM 元数据和 humanoid 映射,不假设与人物模型同版本或同朝向。
  • 单独记录模型原点、脚底高度、默认朝向、可匹配骨骼数和动作首尾姿态。
  • 验证 YUI 现有 idle、表情、口型和 cursor-follow 是否会覆盖移动动作的骨骼或姿态。

两个模型必须分别完成“静止播放、短距离移动、长距离移动、目标替换、停止恢复、普通拖拽接管”六组测试,不能只用其中一个模型通过后推定另一个兼容。

输入和窗口穿透 ​

  • 仅在画布或桌宠窗口拥有焦点时处理 F,输入框、文本框和 IME composing 状态不触发。
  • 目标模式开启时允许点击画布空白区域;退出目标模式后恢复原有鼠标穿透策略。
  • UI 按钮、聊天区域、右键和滚轮不应改变移动目标。
  • 监听器必须在 VRM 初始化、模型切换和销毁时成对注册/清理,防止重复触发和 F 键卡住。

实施顺序 ​

  1. 增加 F 目标模式和点击目标记录,不播放动作。(已完成)
  2. 增加屏幕平面目标计算、平滑位移、边界限制和到达阈值。(已完成)
  3. 接入普通拖拽抢占、锁定、失焦、切换和销毁清理。(已完成)
  4. 在两个模型上做骨骼轨道和脚底兼容性探测。
  5. 接入 world-walk-start → world-walk → world-walk-stop,处理动作所有权和待机恢复。(仅循环走路已接入;起步和停止仍待实现及模型实测)
  6. 增加转向和目标替换处理。
  7. 分别在 48911 页面和 Electron 桌宠窗口验证穿透、焦点和性能。
  8. 运行与交互模块和动作播放器相关的最小测试,并记录两个模型的验收结果。

完成标准 ​

  • F 只控制“能否继续选目标”,不会取消已开始的移动。
  • 两个目标模型都能选点、移动、改目标、到达和恢复待机。
  • 移动速度与帧率无关,停止不抖动、不漂移、不双重位移。
  • 走路动作与位移方向一致,脚底和身体不出现明显滑步。
  • 普通拖拽、旋转、缩放、口型、表情、待机和 YUI 现有交互无回归。
  • 锁定、失焦、切换模型和销毁后没有残留移动、动作或事件监听器。