模拟器、倍速与慢放
模型的 main_c 定义“一次物理子步”,模拟器定义“一游戏刻执行哪些子步、执行几次,以及什么时候同步”。把这两个职责混在一起,最容易造成重复重力、倍速失真和落地抖动。
模拟器状态
text
simulator {
global_sim_mod
global_inv_dt
global_set_inv_dt
global_rate
tick_funcs[]
sync_funcs[]
}| 字段 | 含义 |
|---|---|
global_rate | 每个 Minecraft tick 执行的模拟子步数 |
global_inv_dt | 时间拆分分母 |
global_set_inv_dt | 在完整周期边界切换到的新分母 |
global_sim_mod | 当前拆分相位 |
tick_funcs | 每个子步运行的回调 |
sync_funcs | 所有子步结束后每游戏刻运行一次的回调 |
模拟时间倍率为:
text
global_rate / global_inv_dt三种常用设置
rate | inv_dt | 效果 | 注意 |
|---|---|---|---|
| 1 | 1 | 正常 1× | 默认 |
| 4 | 1 | 每 tick 四子步,4× | 成本约四倍 |
| 1 | 4 | 四个游戏刻完成一轮,0.25× | 需要 slow key/mov 契约 |
| 0 | 任意 | 暂停模拟子步 | sync_funcs 仍可能运行 |
global_rate=0 与 _stop 不同:前者保留调度但不进入 main_loop,后者清除计划函数。
每轮为什么先恢复常量
不同模拟器可以有不同重力、摩擦和反弹参数,但底层假玩家是共享的。正确顺序是:
mcfunction
function vve:_consts
# 再覆盖本模拟器的环境参数
scoreboard players set vve_gravity int 120若不先恢复默认常量,前一个模拟器的参数会泄漏到下一个环境。一个模拟器完成 _get → _consts → main → _store 前,不要嵌套另一个模拟器。
运动拆解
“拆解”指把完整帧的职责分开调度:
main_force:更新物理与响应;main_sync:把结果同步给世界/显示;sync_funcs:把昂贵但只需每游戏刻一次的显示或聚合工作移出高速子步。
例如 4× 子步时,物理可能执行四次,但 display 更新只需一次。前提是所用模型确实提供对应分段入口,且两段合起来与完整帧语义一致。
运动慢放
VVE 的慢放不是“每四 tick 才突然跑一次完整 main”。这种做法会让画面阶跃、碰撞点跨越更远。源码中的 main_slow_key 与 main_slow_mov 将一个物理周期分成:
- 关键步:在周期边界执行介质探测、力学结算和新的关键状态;
- 运动步:在中间 tick 按拆分时间推进运动,使显示连续。
vve:sim_slow 只保存附着在外部实体上的 vve_rate 等慢放状态;它本身不遍历或调度物理对象。调度仍由模拟器或业务函数负责。
只切换已完成周期的分母
global_set_inv_dt 用于把新拆分数延迟到相位回到边界时生效。若在半个周期中直接改 global_inv_dt,可能跳相位、重复关键步或漏掉结算。
暂停、单步与页面播放
在本站视窗中,Play/Pause/Step 控制的是 Sandbox 20 TPS 模拟。tickFunction 会串行加入每个沙盒 tick;它不会与 cell 命令并发修改世界。页面隐藏时自动暂停,卡顿时最多补算有限 tick,避免后台无限追赶。
选择调度策略
- 普通对象少:每 tick 直接
main_c,最容易验证。 - 需要高速稳定性:增加
global_rate做子步,但同步移出子步。 - 需要慢镜头:使用模型提供的 slow key/mov,不要简单跳帧。
- 多区域/多规则:使用多个模拟器,但每个对象只能属于一个活动调度路径。
- 远处/静止对象:在业务层跳过主程序,见性能、静体与子系统。