性能、静体与局部子系统
VVE 的成本近似由“活动物体数 × 每物体碰撞点数 × 每点介质候选查询 × 每 tick 子步数”决定。优化的第一原则是减少没有信息增益的工作,而不是把所有常量调小。
成本从哪里来
| 成本项 | 典型放大因素 |
|---|---|
| 实例遍历 | 活动物体数量 |
| 碰撞点换算 | 每物体采样点数 |
| 方块/实体介质探测 | 搜索范围、候选实体数、探测入口 |
| 子步 | global_rate |
| 显示同步 | display 数量、乘客树、每 tick NBT 更新 |
| 调试绘图/输出 | 粒子、tellraw、trace 事件数 |
降频不是简单跳过帧
对远处或低优先级对象,可以降低物理更新频率,但必须决定被跳过的时间如何表示:
- 每 N tick 执行一次原步长:物理时间变慢;
- 每 N tick 执行一次放大步长:可能穿透和数值不稳定;
- 使用 slow key/mov:保持中间运动连续,但仍需模型支持;
- 只降低显示同步:物理保持准确,画面更新变少。
因此“每 4 tick 跑一次 main_c”不是通用优化配方。它改变了模拟语义。
静体优化应放在业务层
当前 VVE 3.0.1 没有一个可对所有模型直接启用的完整 sleep/wake API。业务模块可以实现自己的静体状态:
text
连续 N 帧满足:
|v| < v_threshold
|ω| < w_threshold
支撑接触稳定
外部冲量队列为空
→ 标记 sleeping,停止完整 main_c唤醒条件至少包括:
- 收到外部冲量/力偶矩;
- 支撑介质移动、销毁或材质变化;
- 被玩家/脚本显式操纵;
- 附近动态实体可能发生碰撞;
- 模拟器常量或时间尺度发生不兼容变化。
只凭“速度恰好为零一帧”休眠会把碰撞顶点、抛物线顶点也错误冻结。使用连续帧滞回:进入休眠阈值小,唤醒阈值大,避免临界抖动。
局部子物理系统
“局部子系统”不是当前源码里一个现成模块,而是一种组合策略:按空间/玩法边界把对象分组,每组有自己的候选实体、模拟器状态和预算。
示例:
text
arena_a
simulator_a
tag=vve_region_a
只查询 a 区域的实体介质
arena_b
simulator_b
tag=vve_region_b
可使用不同重力/摩擦设计约束:
- 一个活动实例同一时刻只属于一个模拟器;
- 跨区迁移要在完整帧边界执行;
- 两个区域若允许互撞,必须定义边界交互,不能双方都忽略对方;
- 各模拟器每轮先
vve:_consts,再覆盖自己的常量; - 实体查询尽量使用区域 tag 与合理距离,而不是全世界扫描。
实用优化顺序
- 关闭正式环境中的 GELib 粒子与逐帧打印。
- 确认没有重复调度。
- 选择更轻模型:能用 point 就不使用刚体,能用 block 就不使用 cubox。
- 减少碰撞点,保留支撑和形状关键点。
- 缩小实体介质候选范围;普通物体不要无条件用
_detect_large。 - 把 display 同步放到
sync_funcs,高速子步中只算物理。 - 对确实稳定的对象做业务 sleep/wake。
- 最后才考虑区域降频或多模拟器分区。
建立性能预算
为目标场景固定基准,例如:
text
版本:26.2
对象:64 block + 16 cublock
碰撞点:8/对象
子步:1
场景:平地 + 8 个实体介质
要求:20 TPS;单 tick 命令数 p95 < 预算;无积压同时记录“活动/休眠对象数、探测调用数、候选实体数、命令数、tick 时长”。只有 FPS 不能区分 GPU 画面压力和数据包 tick 压力。