Skip to main content

Dynamic Variables(动态变量)

Dynamic Variables (动态变量)

Dynamic Variables(动态变量),通常简称为 dyn varsdynvars,是一种数据存储系统。它允许你在 Slot(插槽)层级结构下存储任意名称的、具有作用域的数据,类似于编程中的 关联数组(键值对)。

它们通常用于具有许多移动部件的大型系统中,但也可以作为简单的“全局”值使用,方便在对象上进行更改。

概览 (Overview)

动态变量的管理分为两部分:Dynamic Variable Spaces(动态变量空间)和 Dynamic Variables(动态变量)。动态变量存在于动态变量空间之下,并且可以动态地创建、修改和销毁。

变量空间 (Variable spaces)

通过向 Slot 添加 DynamicVariableSpace 组件,该 Slot 及其所有子节点都会成为该命名变量空间的一部分。但这并不意味着该 Slot 下的每个动态变量都自动“连接”到这个空间(详见绑定部分)。

注意:

一个 Slot 可以同时属于多个空间。子 Slot 下的空间不会以任何形式与父级空间嵌套。这意味着建议为变量空间使用唯一的名称。

动态变量 (Dynamic variables)

要创建动态变量,请将以下任一组件附加到属于变量空间的 Slot 上:

动态变量拥有名称 (Name)值 (Value)。变量名是其在变量空间内的唯一标识符。

动态字段 (Dynamic fields)

可以将组件上现有的 IField “转换”为动态变量。使用以下组件:

附加组件并将要转换的字段拖入 TargetField 后,该字段就可以像其他动态变量一样进行交互。

命名限制 (Naming restrictions)

动态变量和空间的名称不得包含符号、标点或空白字符,但以下字符除外:句点 (.)、下划线 (_)、空格 ( ) 和连字符 (-)。

绑定 (Binding)

动态变量与给定空间关联的过程称为绑定。动态变量组件会沿 Slot 层级向上遍历(包括其当前 Slot),寻找可绑定的适用变量空间。如果找不到,该变量将无法在组件外部访问(退化为普通的 ValueField)。每当组件的任何部分发生变化时,都会重新进行此绑定过程。

直接绑定 vs 间接绑定

动态变量组件的 VariableName 可以是以下两种形式之一:

  • VariableName(仅变量名):代表间接绑定。它会绑定到第一个 OnlyDirectBinding 属性为 False 的变量空间。
  • VariableSpaceName/VariableName(空间名/变量名):代表直接绑定。它会绑定到第一个名称匹配 VariableSpaceName 的变量空间。

绑定示例

└─ Foo - 变量空间 "test"
  └─ Bar - 变量空间 "test2"
    └─ Baz - 动态变量 "test/var"

在此结构中,动态变量 test/var 将绑定到变量空间 test。如果变量名为 var,它将绑定到 test2。如果变量名为 vartest2 开启了 OnlyDirectBinding(仅直接绑定),它将跳过 test2 并绑定到 test

绑定延迟警告:

某些情况下,创建的变量不会立即绑定/重新绑定,需要 Delay Updates(延迟更新)2帧或更多帧。这些情况包括:

  • 创建新的动态引用变量 (DynamicReferenceVariable)。
  • 删除动态变量。
  • 更改动态变量或空间的名称。
  • 在没有组件更新的情况下更改空间结构(如复制包含空间的 Slot 或重新设定父级)。

如果遇到异常行为,请尝试在操作之间增加 2 次以上的更新延迟。

交互 (Interfacing)

读取动态变量

  • Read Dynamic Variable:输入 Source (Slot) 和 Path (变量名)。适用于读取不在当前层级内的变量。
  • Dynamic Variable Input:使用全局变量作为 Path,并绑定到其所在的层级。在读取同一空间内的常量名称变量时,首选此节点(性能更好)。

通过动态变量驱动

使用 DynamicValueVariableDriverDynamicReferenceVariableDriver 组件,可以使用动态变量的值来驱动其他字段。相比传统的驱动方法,这种方式更灵活、紧凑。

写入动态变量

应使用 Write Dynamic VariableWrite Or Create Dynamic Variable 节点。后者在变量不存在时会自动创建它。

写入/驱动延迟警告:

  • 如果通过直接引用组件字段进行写入(而非使用 Write 节点),会导致 1 帧的延迟,读取节点才能获取新值。强烈建议使用 ProtoFlux 节点写入。
  • 驱动动态变量的值时(Driver),本质上是每帧对组件进行本地写入。因此,这里也存在 1 帧的延迟。目前无法避免。

另外,如果是驱动变量,必须确保所有该变量的实例都由同一个值驱动,否则会导致客户端之间的数据冲突(Fighting)。

最佳实践 (Best practices)

  • 建议在任何时候只保留一个动态变量实例(同名且绑定到同空间的组件)。
  • 推荐使用直接绑定Space/Var),这能清晰地表明变量所属的空间。
  • 对于复杂的系统,建议使用点号(.)来伪造命名空间以隔离系统。
    例如:User/Avatar.Systems.Grabbable.Enabled

默认空间 (Default spaces)

目前存在三个“默认”存在的动态空间:

  • World:存在于任何世界的 Root 下。属性为 OnlyDirectBinding。用于全局影响世界的功能(如 BeatLink)。
  • User:存在于每个 UserUser Root Slot 下。属性为 OnlyDirectBinding。用于影响 Avatar 的系统。
  • Dash:存在于用户空间 (Userspace) 的仪表盘 Slot 下。

示例用法

包含的系统

在制作大型系统时,不要通过 Source 引用 ValueField,而是使用动态变量。通常在对象根部放置一个 DynamicVariableSpace,并用专门的 Slot 存放变量。
(图示:展示了一个内部系统的设置,使用了 Dynamic Variable Input 节点以优化性能。)

image.png

控制另一个系统

假设你要为 Avatar 上的系统制作一个外部控制器(该系统使用 User 空间,命名空间为 User/CoolAvatarSystem.)。

你可以获取 User 并将其插入 User Root Slot 节点,然后将其连接到 Read Dynamic VariableSource 输入端。输入正确的路径(如 User/CoolAvatarSystem.Enabled)即可读取或写入该值。

参见 (See also)