Dynamic Variables(动态变量)
Dynamic Variables (动态变量)
Dynamic Variables(动态变量),通常简称为 dyn vars 或 dynvars,是一种数据存储系统。它允许你在 Slot(插槽)层级结构下存储任意名称的、具有作用域的数据,类似于编程中的 关联数组(键值对)。
它们通常用于具有许多移动部件的大型系统中,但也可以作为简单的“全局”值使用,方便在对象上进行更改。
概览 (Overview)
动态变量的管理分为两部分:Dynamic Variable Spaces(动态变量空间)和 Dynamic Variables(动态变量)。动态变量存在于动态变量空间之下,并且可以动态地创建、修改和销毁。
变量空间 (Variable spaces)
通过向 Slot 添加 DynamicVariableSpace 组件,该 Slot 及其所有子节点都会成为该命名变量空间的一部分。但这并不意味着该 Slot 下的每个动态变量都自动“连接”到这个空间(详见绑定部分)。
注意:
一个 Slot 可以同时属于多个空间。子 Slot 下的空间不会以任何形式与父级空间嵌套。这意味着建议为变量空间使用唯一的名称。
动态变量 (Dynamic variables)
要创建动态变量,请将以下任一组件附加到属于变量空间的 Slot 上:
- DynamicValueVariable:用于处理值类型(如
int,string,float3等)。 - DynamicReferenceVariable:用于处理引用类型(如
Slot,User等)。 - DynamicTypeVariable:用于处理
Type类型。
动态变量拥有名称 (Name) 和 值 (Value)。变量名是其在变量空间内的唯一标识符。
动态字段 (Dynamic fields)
可以将组件上现有的 IField “转换”为动态变量。使用以下组件:
- DynamicField:用于值类型。
- DynamicReference:用于引用类型。
- DynamicTypeField:用于
Type类型。
附加组件并将要转换的字段拖入 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。如果变量名为 var 且 test2 开启了 OnlyDirectBinding(仅直接绑定),它将跳过 test2 并绑定到 test。
绑定延迟警告:
某些情况下,创建的变量不会立即绑定/重新绑定,需要 Delay Updates(延迟更新)2帧或更多帧。这些情况包括:
- 创建新的动态引用变量 (DynamicReferenceVariable)。
- 删除动态变量。
- 更改动态变量或空间的名称。
- 在没有组件更新的情况下更改空间结构(如复制包含空间的 Slot 或重新设定父级)。
如果遇到异常行为,请尝试在操作之间增加 2 次以上的更新延迟。
交互 (Interfacing)
读取动态变量
- Read Dynamic Variable:输入
Source(Slot) 和Path(变量名)。适用于读取不在当前层级内的变量。 - Dynamic Variable Input:使用全局变量作为
Path,并绑定到其所在的层级。在读取同一空间内的常量名称变量时,首选此节点(性能更好)。
通过动态变量驱动
使用 DynamicValueVariableDriver 或 DynamicReferenceVariableDriver 组件,可以使用动态变量的值来驱动其他字段。相比传统的驱动方法,这种方式更灵活、紧凑。
写入动态变量
应使用 Write Dynamic Variable 或 Write 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:存在于每个 User 的 User Root Slot 下。属性为OnlyDirectBinding。用于影响 Avatar 的系统。Dash:存在于用户空间 (Userspace) 的仪表盘 Slot 下。
示例用法
包含的系统
在制作大型系统时,不要通过 Source 引用 ValueField,而是使用动态变量。通常在对象根部放置一个 DynamicVariableSpace,并用专门的 Slot 存放变量。
(图示:展示了一个内部系统的设置,使用了 Dynamic Variable Input 节点以优化性能。)
控制另一个系统
假设你要为 Avatar 上的系统制作一个外部控制器(该系统使用 User 空间,命名空间为 User/CoolAvatarSystem.)。
你可以获取 User 并将其插入 User Root Slot 节点,然后将其连接到 Read Dynamic Variable 的 Source 输入端。输入正确的路径(如 User/CoolAvatarSystem.Enabled)即可读取或写入该值。
参见 (See also)
- Dynamic Impulses:ProtoFlux 脉冲的类似概念。
- Cloud Variables:存储在云端且随处可访问的变量类型。
