VVE 的心智模型
VVE 最容易被误解成“一组能让 display entity 运动的函数”。更准确的理解是:它是一套以共享临时计算区为核心、用代码模板生成业务模块、用碰撞点协议连接运动模型与环境模型的运行时。
四层结构
MOT 预设模板(构建期)
↓ 生成
业务模块(函数 + 字段 + 接口)
↓ 创建 / 调度
模块实例(实体上的持久状态)
↕ _get / _store
共享临时对象(score + storage 运算区)模板:构建代码
memory_storage/vve_block_1.0 这类目录属于 MOT 预设。它们在开发期产生 _class、_new、_get、main_c 等业务模块文件。模板不是运行时“类”,也不会在游戏里自动继承。
模板可以叠加:骨架模板先生成主体,测试、声音、骰子等功能模板再添加或覆盖同路径文件。它更像图层合成,而不是 Kotlin/Java 的类继承。
模块:共享行为
函数前缀界定一个模块,例如:
data/vve_examples/function/dice_6/
↳ vve_examples:dice_6/模块拥有初始化、数据结构转换、实例生命周期与主程序。一个模块可创建多个实例,它们共享函数代码但不共享实体上的持久状态。
实例:持久状态
一个 item_display、marker 或其他根实体保存某个具体物体的中心、速度、姿态等字段。实例跨 tick 存在。VVE 通过 tag 选择一类实例,例如:
execute as @e[tag=dice_6] run function vve_examples:dice_6/main_c临时对象:唯一计算工作台
VVE 把当前对象的字段读到一组共享假玩家与 storage,执行高频计算,最后写回。这样数学函数不必为每个实体反复读写 NBT,但代价是:
- 同一套临时对象不能同时代表两个实例;
_get → 计算 → _store必须连续完成;- 不能在保存前嵌套另一个会覆盖相同临时字段的模型;
- 外部回调必须清楚自己会不会改写
vve:io或公共假玩家。
两个正交分类维度
“质点、刚体、介质”不是三个互斥的类。
| 维度 | 问题 | VVE 选择 |
|---|---|---|
| 运动职责 | 对象怎样保存和推进状态? | 质点 / 刚体 |
| 环境职责 | 别人的碰撞点在这里得到什么响应? | 方块介质 / 实体介质 |
因此 cublock 可以同时是刚体和实体介质:它自己用八个点探测世界,别人也能把它探测为盒体。block 只是刚体,仍会撞地,但其他刚体不会自动撞到它。
碰撞点是协议边界
运动模型不需要知道水、斜面或另一个盒体具体怎样计算;介质也不需要知道碰撞点来自骰子还是载具。双方只交换:
cpoint {
center: (c_x, c_y, c_z)
velocity: (c_vx, c_vy, c_vz)
c_mass
}介质读取它并返回位移、冲量、摩擦、接触法向、附着层等响应。物体再决定如何把响应应用到自己的平动和转动。
公开接口如何识别
VVE 源码的约定是:
| 名称 | 典型含义 | 对外使用 |
|---|---|---|
init | 初始化模块 | 是 |
_class, _new, _get 等 | 明确的 API/扩展点 | 是 |
main* | 一轮物理或其中一个阶段 | 是,但要理解调度契约 |
tick* | 实例遍历或计划入口 | 是 |
| 其他无下划线辅助函数 | 内部实现 | 默认否 |
下划线在这里不是“私有”,恰好相反,通常标识外部接口。一个内部文件即使技术上能 function 调用,也不代表 VVE 对其输入输出和版本稳定性做了承诺。
建立正确问题意识
遇到异常时,按层询问:
- 调度层:这个对象今天到底执行了几次主程序?
- 数据层:临时对象来自哪个实例,计算后写回了吗?
- 运动层:位置/姿态预测是否合理?
- 探测层:碰撞点在哪里,使用了哪个介质入口?
- 响应层:位移、冲量、摩擦和规整是否重复应用?
- 显示层:物理状态正确,只是 transformation 或插值不正确吗?
这套顺序比直接盯着 display entity 猜原因可靠得多。