Dynamic variables(動的変数)

From Resonite Wiki
This page is a translated version of the page Dynamic variables and the translation is 35% complete.

Dynamic variables(略してdyn varsまたはdynvars)は、任意のスコープ内のデータをSlot階層の下に、任意の名前をつけて格納できるデータストレージシステムです。これは、[1]に似ています。これらは、多くの可動部分を持つ大規模なシステムでよく使用されますが、オブジェクト上で変更可能な簡単な「グローバル」値としても便利です。

Dynamic Variablesの使い方に関するクイックスタートガイドについては、「How To Use Dynamic Variables」ページが説明書として役立ちます。

概要

Dynamic variableは、「dynamic variable space(動的変数空間)」と「dynamic variable(動的変数)」の2つの部分で管理されます。dynamic variable(動的変数)はdynamic variable space(動的変数空間)の下に存在し、動的に作成、変更、破棄することができます。

変数空間(Variable spaces)

スロットにDynamicVariableSpaceを追加すると、そのスロットとすべての子要素が指定された変数空間の一部になります。ただし、これはこのスロット内のすべての動的変数が必ずしも「空間に属している」ことを意味するわけではありません。詳細は#Bindingで説明しています。

スロットは複数の空間(spaces)に同時に属することがあります。 子スロットの空間は、親スロットの空間と階層的にネストされるわけではないため、変数空間(variable spaces)の名前は一意にすることを推奨します。

動的変数(Dynamic variables)

動的変数(dynamic variable)を作成するには、変数空間(variable space)の一部であるスロットに以下のいずれかのコンポーネントをアタッチしてください:

動的変数(Dynamic variable)は名前と値を持ちます。変数の名前は変数空間内での一意の識別子であり、値はダイナミック変数の値を表します。

動的フィールド(Dynamic Fields)

既存のIFieldを動的変数(Dynamic Variables)に「変換」することが可能です。これには以下のいずれかのコンポーネントを使います:

コンポーネントをアタッチしてフィールドをTargetFieldに変換するためにドラッグすると、その指し示したフィールドは他の動的変数(Dynamic Variables)と同じように操作できるようになる

命名制限

動的変数名および動的変数空間の名前には、記号、句読点、空白文字を含めてはいけません。ただし、ピリオド(.)、アンダースコア(_)、スペース( )、およびハイフン(-)は例外です。使用できない文字かどうかを確認するには、記号(Is Symbol)句読点(Is Punctuation)空白文字(Is White Space)の各ProtoFluxノードを使い、前述の例外に注意してください。

バインディング

動的変数(dynamic variable)が特定の空間に関連付けられる過程を「バインディング(binding)」と呼びます。動的変数コンポーネントは、スロット階層を上に向かってたどりながら、現在のスロットを含めて、適用可能な変数空間を探します。もしも動的変数がバインドできる動的変数空間を見つけられなかった場合、その変数はコンポーネント自身の外側からはアクセスできず、単なる ValueField のようなものになってしまいます。動的変数は、このバインディングの過程を、コンポーネントのどこかが変更されるたびに繰り返します。

直接バインディングと間接バインディング

動的変数(dynamic variable)を作成する際、ダイナミック変数コンポーネントのVariableNameは以下の2つの形式のいずれかです:VariableNameまたはVariableSpaceName/VariableName。前者は「間接バインディング」(indirect binding)を表し、後者は「直接バインディング」(direct binding)を表します。

もし動的変数(dynamic variable)が間接的にバインドされている場合、OnlyDirectBindingTrueに設定されていない最初の動的変数空間(dynamic variable space)にバインドされます。 直接バインドされている場合は、VariableSpaceNameに一致する最初の動的変数空間にバインドされます。

バインディング例

In the following setup:

└─ Foo - Variable Space "test"
   └─ Bar - Variable Space "test2"
      └─ Baz - Dynamic Variable "test/var"

The dynamic variable test/var will bind to the variable space test. If the variable was instead named var, it will bind to the variable space test2. If the variable was named var and test2 has OnlyDirectBinding enabled, it would bind to test.

Binding delay warning

As of the time of writing, there exist a few instances where created variables do not instantly bind/rebind to a dynamic variable space, requiring a Delay Updates of 2 or more updates. These include:

If you find dynamic variables to be behaving weirdly, and you are doing any of these operations, try adding a delay of 2 or more updates between such operations.

Interfacing

A variable space can be interfaced with any slot that resides within the variable space, even if the slot is not part of the hierarchy containing the exact dynamic variable being interfaced with.

Reading dynvars

In ProtoFlux, the Read Dynamic Variable node and Dynamic Variable Input nodes exist to read dynamic variables from a slot.

The Read Dynamic Variable node takes in a Source Slot and a Path variable name, and is marked as ContinuouslyChanging. The Dynamic Variable Input node uses a Global for Path and binds to spaces in the slot hierarchy it exists in. Therefore, when reading constant-name dynamic variables within the same dynamic variable space of the slot the node exists in, the Dynamic Variable Input node is preferred. When reading dynamic variables from a space outside the node's slot hierarchy, the Read Dynamic Variable node must be used.

It is also possible to read dynamic variables by sourcing the Value or Reference field of the component directly. This is not recommended, as if the dynamic variable ever gets deleted and remade, the source will break, removing half the functionality of a dynamic variable.

Driving from dynvars

By using a DynamicValueVariableDriver or DynamicReferenceVariableDriver component, fields can be driven using the value of a dynamic variable. This is recommended over traditional methods of driving by virtue of its flexibility and compactness.

Writing to dynvars

Dynamic variables should be written to via the Write Dynamic Variable or Write Or Create Dynamic Variable ProtoFlux nodes. The former will write to existing dynamic variables, while the latter will create the dynamic variable if it doesn't exist already.

Driving dynvars

Driving dynamic variables must be done with caution. If a dynamic variable is being driven, it is crucial that all instances of the same dynamic variable are driven by the same value. Otherwise, clients will fight over which value is the "true" value of the dynamic variable and cause inconsistent behavior. The other caution relates to how driving is essentially a local write each frame, which is expanded on in #Writing/Driving_Delay_Warning.

Writing/driving delay warning

If dynamic variables are written to via sourcing the component field and writing, it will incur a delay of 1 frame before the value is propagated to any read nodes. As such, it is highly not recommended to directly write to dynamic variable components, and always recommended to use the proper ProtoFlux nodes.

When driving the value of a dynamic variable, it is essentially a local write to the component every frame. As such, the 1 frame of delay will apply there too. There is currently no good way to avoid this.

Best practices

Even though dynamic variables allow for a wide array of freedom, there are a few practices generally considered to be favorable when working with dynamic variables:

  • It is highly recommended to have only one instance of a dynamic variable (dynamic variable component with the same name and bound to the same space) at any given time.
    • There aren't any huge problems with having multiple dynamic variable instances if none or all of the instances are being driven, but it allows for cleaner organization of variables.
  • Using variable names that directly bind allows for a clearer overview of what space the variables should be bound to. Indirectly binding variable names are more suited for variables that are dynamically created and/or destroyed as part of an object's function.
    • Using OnlyDirectBinding on a DynamicVariableSpace strictly enforces this behavior, which can prevent misbindings and catch errors earlier.
  • Dynamic variable spaces are not nested. If a system is complex enough, or if a DynamicVariableSpace is being shared by multiple objects, using periods (.) to pseudo-isolate objects or systems from one another is encouraged.
    • e.g. an avatar and all of its features could contain the dynamic variables User/Avatar.Systems.Grabbable.Enabled, User/Avatar.Blendshapes.Blep.MaxClamp, User/Avatar.Systems.Flight.Drag, etc.


Default spaces

As of the time of writing, there are three dynamic spaces that exist "by default":

  • World, which exists under the Root of any created world. This space is marked as OnlyDirectBinding.
    • Useful for things that should globally affect the world or broadcast information throughout the world. Example items that use this space include BeatLink and the Redprint manager.
  • User, which exists under every User's User Root Slot. This space is OnlyDirectBinding.
    • Useful for systems that affect avatars, as outside objects can rely on a standardized space being available for each user to read and write variables on.
  • Dash, which exists under the slot containing the UserspaceRadiantDash in userspace.

Example usage

One contained system

Say you are working on a large and complicated system. In lieu of trying to manage "global" settings by sourcing ValueField components, you can use dynamic variables to help assign names to settings or other useful values. This is usually accomplished by placing a DynamicVariableSpace on the root of your object (or root of your "library") and having a specialized dynamic variable slot for containing the dynamic variables. Depending on how many dynamic variables are used, it may be beneficial to delegate them to multiple slots.

A screenshot showing a simple setup like this is shown below. Note the use of Dynamic Variable Input nodes for performance sake.

Controlling another system

Say that you want to make an external controller for a system on an avatar. This system uses the default User dynamic variable space and namespaces itself with User/CoolAvatarSystem..

The simplest way to get a slot to interface with this system can be obtained by getting the User in some fashion and plugging it into the User Root Slot node. Reading with this system should be done with the Read Dynamic Variable node, as the controller is not part of the dynamic variable space on the user.

From there, it's simply a matter of knowing the right variable names and types (e.g. User/CoolAvatarSystem.Enabled being a bool). Plugging the previously obtained slot into the Source of a Read Dynamic Variable of the matching type and path will get you the current value, while using a Write Dynamic Variable will allow you to write to it.

See also