見出し画像

vvvv gamma入門 - 3

vvvv入門の3回目。
これまでの入門記事はマガジンにまとめております。

Patchの種類

前回はProcessを使用して自作ノードを作成しました。
今まで「パッチを作成する」と言う様に何気なくPatchという単語を使ってきましたが、Patchには以下の様な種類があります

  • Application Patch

  • Definitions Patch

  • DataType Patch

    • Process

    • Record

    • Class

    • Interface

    • Forward

PatchにはApplication、Definitions、DataTypeの3種類があり、さらにDataType PatchはProcess、Record、Class、Interface、Forwardの5種類があります。

Application Patchはvvvvを起動するとすぐに表示される、今までの作業で実際にノードを置いてきたものです。アプリケーションのエントリーポイントとなります。

Definitions Patchはその名の通り自作ノードの定義などを管理、整理するために使用します。取り合えず、入門用のサンプルパッチ程度の規模ではお世話になることはないかと思いますので、今はそういうものがあるのか~程度の理解で大丈夫です。

DataType Patchが今回の記事のメインであり、おそらくvvvvの使用中に最も頭を悩ますトピックです。

それぞれ実際の使用例と共に違いを説明していきます。

ProcessとRecord/Class

5種類の中からこの記事ではProcess、Record、Classについて説明します。
InterfaceとForwardについてはまた別の機会とします。

Process

Processは最も一般的なDataTypeです。
自作ノードを実装する場合、一番最初に採用を検討すべきDataTypeです。
前回の記事で使用した様に手早く自作ノードを実装することができます。

ただし、Record、Classと比べると以下の機能がありません。

  • 自身のインスタンスを出力しない。

  • Operationノードを作成できない。

  • Interfaceを実装できない。

これはどういうことなのか理解するには先にRecordとClassについて見てみるのが良いでしょう。
Interfaceについては別の記事で説明するので1つ目と2つ目について主に説明していきます。

RecordとClass

RecordとClassはとても似ていますが、違いはRecordがイミュータブルであるのにたいして、Classはミュータブルだということです。

プログラミングにおいてイミュータブル/ミュータブルを区別することの重要性は様々な書籍やインターネット上の資料等で学ぶことができるのでこの記事で詳しく説明することは割愛しますが、主に以下の2点がメリットとして挙げられます。

  • うっかり値が変更されてしまうことを防げる。

  • オブジェクトの変更の検出が容易になる。

RecordとClassを使い分けるために重要な概念なので、まだよく理解できていないけれど興味のある方は調べてみてください。
vvvvのドキュメントでも少し説明があります。

Process、Record、Classの比較

それでは3つのDataTypeを実際に使って違いを見てみます。

まずProcessを作ってみます。名前はMyProcessとします。
簡単のため2つのインプットを足し合わせて出力するだけのノードです。

MyProcessを作成

ノードブラウザでInputノードや+ノードを検索して配置します。
ただし何やら色がいつもと違います。これはInputノードの型を指定していないため、警告が表示されているからです。今までのサンプルでは自動で型を判定してくれることが殆どでしたが、この+ノードは色々な型を受け付けることができるため型が自動で判定できません。
vvvvで型を指定するにはノードのConfigureを使用します。
Inputノードを右クリックしてConfigureを選択します。

Configure

Typeというフィールドをダブルクリックして表示されたリストからFloat32を選択するか、直接入力します。

Float32を指定

Configureは型指定の他に、ノードの隠しピンを表示したりなどいろいろな用途で使用するので覚えておいてください。

例: +ノードのApplyピンを表示

ちなみに、Configureを覚えたところで1つTipsですが、コメントを書くのにもConfigureを使います。
StringのIOBoxを配置して右クリック > Configure(IOBOXの場合は、マウスの中ボタンクリックでも可能です)すると、String Typeというものがあるので、Commentを指定します。

String TypeをComment
Comment

改行はShift+Enterです。ちなみに自分の環境では日本語は文字化けしてしまいました。未対応なのかもしれません。

話がそれてしまいましたのでMyProcessの実装に戻ります。Configureで2つのInputノードに型定義が終わると警告が消えます。

MyProcess

親パッチに戻ってMyProcessノードを配置してみます。期待通り動作していることを確認します。

MyProcessノードを配置

同じようにしてRecordとClassも定義します。それぞれMyRecord、MyClassという名前にします。中身のノードはMyProcessと同様にします。
ただし、ちょっと面倒なのですが、ここではコピー&ペーストはしないでください。

MyRecord、MyClassの定義
MyRecordのノード

MyRecordでMyProcessと同様のノードを作成すると、MyProcessのノードと色が異なっているはずです。(コピー&ペーストしてしまうと同じ色になります。)
左端にあるRという■をクリックするとPatch Explorerというものが開きます。
Patch Explorerはパッチの細かな設定を行うものです。
ひとまず注目すべきはPropertiesOplationsです。
Propertyはパッチ内で任意のデータを保持するために使います。
Operationは処理をひとまとまりにしたもので関数の様なものです。
C#などのプログラミング言語で言うところのクラスに似ています。
ここでのOperationはMember Operationと呼ばれます。前回の記事で使用したOparationはStatic Operationと呼ばれるもので厳密には違いものです。

OperationにはCreate OperationUpdate Operationという特別なMember Operationがあります。
Create Operationはノードの作成時に一度だけ実行されます。コンストラクタのようなものです。
Update Operationは毎フレーム実行されます。

Processは毎フレーム処理を行うために使用するのでデフォルトでUpdate Operationがあります。
RecordとClassにはUpdate Operationはありませんが自分で追加することもできます。これについては後述します。

さらにPatch Explorerを見ていくと、RecordとClassは大体似た設定になっていますが、Processとはいくつかの点で異なっているのでよく見比べてみてください。
主には以下の点が異なります。

  • ProcessにはInterfaceがない。

  • Record、ClassにはUpdate Operationがない。

  • ProcessはProcess Nodeにチェックが入っているが、Record、Classにはチェックがない。

  • Record、ClassにはProcess Node内にState Outputがあるが、Processにはない。

Patch Explorerを見ているとわかると思いますが、実はDataTypeは後から変更することができます。
デフォルトの設定値が各DataTypeによって異なっているだけなのです。
なのでProcessを作ったとしてもそれをClassやRecordのように変更することができます。
ただし、よく理解する前にこの辺りの設定をいじっていると混乱してしまったりvvvvの動作が不安定になったりします。
期待通りに動かない場合、設定を見直すとともに、一度RecompileやRestart、vvvvの再起動なども試してみてください。

クラス名を右クリックするとRecompileが選択できます。

Recompile

左上のグレーの四角アイコンをクリックするとRestartなどのメニューが表示されます。

Restart

ではMyRecordとMyClassのノードを置いてみようと思います。
ノードブラウザーで探すと…

MyRecordを探す
Createを選択

Processとは異なり、MyRecordを選択するとCreateというノードが表示されました。MyClassも同様です。(Input、Outputも見やすさのために配置して繋いでいます。)

Createノードを配置

Processノードと違い、CreateというOperation名のノードが配置され、State Outputというものが出力されています。
こんな名前のOutputを実装した記憶はありません。一体State Outputとは何なのでしょうか?

State Outputの正体を説明するために、MyRecord、MyClassに新しくOperationを定義してみましょう。
MyRecordの中に入り、Operationの横の+を押してOperationを追加します。名前はMulとしました。

Mulを追加

ProcessやRecord、ClassはPropertyという変数を内部で保持することができるのでした。
Static Operationは状態を保持できませんでしたが、Member OperationはPropertyのおかげで状態を保持することができます。
試しにPropertyも追加してみます。Propertiesの横の+をクリックします。

Propertyを追加

追加したPropertyを使用してノードをこの様に組み替えてみます。
Propertyを使うにはPadノードを置き、その名前をProperty名と同じにします。(この場合はそのままProperty。)
足し算の結果をPropertyに保存します。
新しくInput3を設けて、Propertyの値と掛け合わせたものを出力します。
エラーが出ているのは今は気にしなくて大丈夫です。

ノードを編集

右の掛け算のノードの塊をMulオペレーションに割り当てます。掛け算のノードの塊を全て選択して右クリック > Assign > Mulを選択します。

Mul Operationに割り当てる

そうすると選択したノードがPatch ExplorerのMulと同じ色になります。

Mul Operationと同じ色になる

これでMyRecordのMulノードが使用できるようになっているので親パッチに戻って以下の様に配置します。

変更後の親パッチ

CreateノードのState OutputがMullノードに渡り、計算結果として30が出力されています。
Create Operationでは1+2が処理され、MyRecord内のPropertyに3が保持されます。
続いてCreate OperationのState OutputがMul Operationにわたり、Propertyで保持されている3に10が掛けられます。
もうお気づきかと思いますが、State Outputで出力されるのはMyRecordのインスタンスです。
State Outputというのは通常のOutputと区別するための用語
の様です。試しにCreateのOuptutを消すかOutput1などにリネームすると、State Outputは単にOutputと表示されます。

例: Create OperationのOutputをOutput1にリネームしたところ

同じことをMyClassにも行うと最終的に以下の様になります。

MyClassのMulノードを配置

若干見た目が異なっています。
MyRecordのMulの方はインプットとアウトプットの間に線がありませんが、MyClassの方のインプットとアウトプットは線でつながっています。
これはイミュータブルとミュータブルを表しています。
RecordのOperationはイミュータブルのため、入力されたインスタンスをコピーして処理を行った結果のインスタンスを出力します。入力されたインスタンスと出力されたインスタンスは別のものです。

それに対してClassはミュータブルなので入力されたインスタンスを使ってそのまま処理(その過程でインスタンスの状態を変更できる)を行い、そのインスタンスを出力します。

先程のノードの見た目の違いは、Recordはインプットとアウトプットの間が線で繋がっておらず、別インスタンスということを表しており、Classはの方はインプットとアウトプットが線でつながっているので同一インスタンスと言うことを表しています。

この様なRecord、Classの振る舞いを踏まえた後で改めてProcessを見てみると違いが良く分かります。

3つのDataType

Processはインスタンスを出力せず名前の通りシンプルに処理だけ行いOutputで定義された値を出力します。
RecordとClassはCreate Operationで作成したインスタンスを用いて処理をつなげていく使い方になります。インスタンスとして状態を保持しながら自分で定義したOperationノードを使いまわすことができます。

例: Mul Operationを何度でも使用できる

Help BrowserにあるProcess vs. Record vs. Classというサンプルパッチもとても分かりやすいので参考にしてみてください。

Process vs. Record vs. Class

ちなみにパッチを閉じるためにはCtrl+Wを押しますが、サンプルパッチを閉じる際に表示される保存するかどうかのダイアログは「いいえ」を選択することをお勧めします。理解のためにサンプルパッチをいろいろ編集してしまうかもしれませんが、保存してしまうと元の状態が見られなくなってしまいます。

いいえを選択

RecordとClassのProcess Node...!?

これでProcess、Record、Classの違いがはっきりしました。めでたしめでたし!
…となればよかったのでがvvvvの深淵なコンセプトはそれを許しません。
もう一つ説明を後回しにした重要機能があります。
それがこのProcess Nodeのチェックボックスです。

Process Nodeチェックボックス

Processでは既にチェックが入っています。
試しにClassのProcess Nodeにチェックを入れてみます。(Recordでも同じことが起きます。)
そしてノードブラウザでMyClassを検索すると…

MyClassのProcess Node

先程までは存在しなかったMyClassのProcessノードがあります!

MyClassのProcessノードを配置すると現状ではこの様になっています。Input、Outputは分かりやすさのため追加しています。

MyClassのProcess Nodeを配置

Inputの白い部分にマウスカーソルを置くとCreate Operationが実行されているのがわかります。

Create Operation

もう一度MyClassの中に入ってMul Operationのチェックをつけてみます。
このチェックボックスはProcess Nodeとして使用するときにそのOperationを使用するかどうかを設定するものです。

Mul Operationのチェックをつける

エラーが表示されると思いますが、これはOutputという名前が重複してしまったからです。
今までは、Create OperationとMul Operationは別のプロセスで実行されるものだったため、Ouputの名前が同じでも問題ありませんでしたが、Process NodeとしてCreate OperationとMul Operationを一緒に実行する設定になったため、Outputの名前が同じだと不都合が生じるようになりました。
なので、それぞれのOutputをリネームしてOutput1、Output2とします。

Outputをリネーム

親パッチに戻ってMyClassのProcess Nodeを見ると、Mul OperationのInputとOutputが追加されていることがわかります。Outputの名前が変わってしまったので、エラーがでています。エラーを解消するためにノードを繋ぎなおすとこの様になります。Input3には10を指定しました。

出力が変更された

Outputに計算結果(この場合は30)が表示されない場合はパッチをRestartしてみてください。ショートカットキーはF9です。
Create Operationはノード作成時に一回しか呼ばれないのでノードを設定などをする前に処理が終っており、予期せぬ状態になってしまっているためにそうなります。

F9でパッチをRestartすると計算結果が表示される

もう少しProcess Nodeの挙動を確認してみます。
MyClassの中でLFOノードを使ってみます。新しくAnimationというOperationを作成して、LFOノードの値を出力するだけのノードに割り当てます。出力はOutput3という名前にします。
Animation Operationのチェックも入れます。

Animation Operationを追加

これで親パッチに戻ってOutputの値を見てみます。LFOノードによって値が変わっているはず…と思いきや何も変化がありません。

沈黙のLFO

これはProcess NodeにはUpdate Oprationが必要なためです。LFOノードは代表的なProcess Nodeです。Process Nodeかどうかというのはノードの見た目で判断できます。Process Nodeには上下にラインが入っています。Process Nodeかどうかというのはパッチの動作を理解する上で重要になってくるので上下ラインのノード=Process Nodeと覚えておいてください。
そこでMyClassにUpdate Operationを追加してみます。Update Oprationのチェックも入れます。Updateは特別なOperationなので、自動でCreateの下に追加されます。色も灰色になります。

Update Operationを追加

Update Operationによって時が動き始めました。

LFOが動作している

MyClass Process NodeにUpdateというBoolean Inputが追加されたのに気付いたでしょうか。このBoolean Inputをトグルすることで対応するOperationを実行するかどうか決めることができます。UpdateをOffにするとLFOが止まります。

UpdateのBoolean Input

Process NodeにOperationに対応したBoolean Inputが出てくるかどうかというのはドキュメントに見当たらなかったため自力で探ってみましたが、どうやらInputもOutputもないOperationはBoolean Inputが設けられる、ということの様です。Update Operation以外のOperation(Animation Operationなど)に対してはBoolean Inputが無いのはそのためです。

もう一つ覚えておかなければいけないのは、Process Nodeにした場合に実行されるOperationの順序を指定できるということです。
Process Node以下のOperationをドラッグ&ドロップすることで実行順序を指定できます。
このサンプルパッチでは問題ありませんが、処理内容によっては実行順序によって結果が変わるものもあるはずです。(四則演算の順序で結果が変わる様な。)

実行順序の設定

Processの役割は…?

Record、ClassもProcessの様に使うことができることを見てきました。そこで気になるのが、
RecordもClassもProcess Nodeとして使えるのならProcessって要らないのでは?
という疑問…。

ドキュメントやサンプルパッチ調べても明確な答えは見つからなかったのですが、自分なりに結論付けたProcessを使うメリットは以下の2点です。

  • 作成の手間が少ない

  • 実装がシンプルになる

作成の手間が少ない

作成の手間が少ないというのは地味なメリットですが有用だと思います。
RecordやClassでProcess Nodeを作ろうとすればUpdate Operationを追加したり、Process Nodeにチェックを入れたりという作業が発生します。

実装がシンプルになる

こちらのメリットの方が実務レベルのコード量になっていった場合に効いてくるかと思います。
Processはインスタンスを返しませんので、Processの処理はProcessノードにデータが入ってから出るまでの間に限定されます。
RecordやClassをProcess Nodeとしてだけ使おうとしても、どこかでCreate Operationを使ってしまえばインスタンスがデータフローに乗ってしまいます。private修飾子のようなアクセス修飾子はvvvvには無いため、Record、ClassのOperationsを使用禁止にする方法はありません。

方針

これは好みの問題ですが、自分としては自作ノードを作りたくなったらまずはProcessを使用して実装します。
インスタンスが必要だったり、イミュータブルにしたかったり、Interfaceを実装したいなどの拡張が必要になった場合にRecordやClassの使用を検討します。
幸いPatch ExprorlerでDataTypeは簡単に変更することができるので、まずはProcessから初めて、後々RecordやClassに変更してもそこまで手間ではないはずです。

逆に…

じゃあRecordやClassをProcess Nodeに出来る必要は無くない?
という質問もありそうですが、いちいちCreateして一連のOperationノードを置いてというイディオムを実装するなら、ポンっとProcess Nodeだけ置いた方が楽だし見た目もすっきりするということはありそうです。
ここはアーティストの好みや処理内容によりケースバイケースなので、実装方法のバリエーションが複数あるのはありがたいかなと思います。

これでやっとvvvvの最も難解で最も役に立つ部分について説明することができました。今回の内容が理解できればこの後の学習曲線はうなぎ上りのはずです!(本当かな?)
今回はここまで。それではまた次の記事で。

もしこの記事があなたのお役に立てたなら幸いです。 よろしければサポートをお願いします。今後の制作資金にさせていただきます!