Skip to content

数据的完整生命周期

VVE 中最关键的不是某个公式,而是四种数据位置之间何时复制。混淆 _proj_get,或忘记 _store,都会让“看起来执行成功”的命令没有持久效果。

四种数据位置

位置典型路径生命周期用途
预设数据模板storage <ns>:class <module>_plate初始化后长期存在创建同类实例的默认值
I/O 输入输出storage <ns>:io input/result一次接口调用在模块边界交换结构化数据
共享临时对象x int, vx int, quat_x int一次连续计算高性能数学工作区
实例对象当前执行实体的 scores/NBT实例存活期保存每个物体自己的状态

路径 A:构造一个实例

text
plate → io.input → _proj → 临时对象
                         ↓ 修改 / anchor / impulse
                  _model → io.result → io.input → _new → 实例

以可实例化模块 example:block 为例:

mcfunction
# 1. 复制默认 plate
data modify storage example:io input set from storage example:class block_plate

# 2. 投射到共享临时对象
function example:block/_proj

# 3. 在临时对象上设置位置、姿态或初速度
execute as @e[tag=math_marker,limit=1] positioned 0 100 0 rotated 30 0 run function vve:object/_anchor_to

# 4. 重新建模成结构化数据
function example:block/_model
data modify storage example:io input set from storage example:io result

# 5. 在当前执行位置创建实例
execute positioned 0 100 0 run function example:block/_new

_proj 不创建实体;_model 不写回任何现有实例;_new 才消耗 io input 创建实例。

路径 B:更新一个实例

text
实例 → _get → 临时对象 → 计算 → _store → 同一实例
mcfunction
execute as @e[tag=example_block,limit=1] run function example:block/_get
scoreboard players add vy int 500
execute as @e[tag=example_block,limit=1] run function example:block/_store

这里 _store 必须仍以原实例为执行者。不要依赖 @e[tag=...,limit=1] 在复杂场景里两次都选中相同对象;更稳妥的写法是在同一个 execute as 调用链中进入一个封装函数:

mcfunction
execute as @e[tag=example_block] run function example:block/_accelerate
mcfunction
# example:block/_accelerate
function example:block/_get
scoreboard players add vy int 500
function example:block/_store

_class 只定义未来实例

_class 通常执行 _zero,设置默认字段,再 _model 写入 plate:

mcfunction
function example:block/_zero
scoreboard players set a int 2500
scoreboard players set mass int 17
scoreboard players set inertia int 500
function example:block/_model
data modify storage example:class block_plate set from storage example:io result

重新运行 _class 会改变以后创建的实例,不会自动迁移已经存在的实体。需要升级存量实例时,应编写显式 migration:逐实例 _get、补字段、校验、_store

精度缩放是数据契约的一部分

scoreboard 只保存整数。字段名旁常用 MOT 标注:

text
<x,int,1w>       # 10000 表示 1
<angular_x,int,100w> # 1000000 表示 1
<inertia,int,100>    # 100 表示 1

storage 与 scoreboard 之间的 execute store ... <scale> 必须与字段契约一致。错误的 scale 不一定报错,只会制造速度爆炸、几乎不动或惯量异常。

共享状态的安全边界

以下写法危险:

mcfunction
function a:object/_get
function b:object/main   # 覆盖了同一批临时字段
function a:object/_store # 把 b 的状态写进 a

正确做法是让每个对象完整完成事务:

text
a: _get → all calculations → _sync → _store
b: _get → all calculations → _sync → _store

VVE 本身按命令顺序串行,但“串行”不等于“可以任意嵌套”。模拟器回调、材质回调和自定义工具尤其要明确会改写哪些共享字段。

数据调试清单

当实例没有变化:

  • _get 的执行者是不是目标实例?
  • 修改发生在临时对象还是另一个 storage?
  • 是否调用对应模块的 _store
  • _sync_motion_store 是否都需要?前者更新世界表现,后者保存计算状态。
  • scale 是否一致?
  • 是否在中间调用了另一个覆盖临时对象的函数?

VVE 由小豆 8593 开发;本站是面向学习与查阅的增强文档。