見出し画像

Udonでライフゲームを作った

Udonとは

VRChatに実装されたビジュアルスクリプト言語です。
2019年12月現在ではオープンアルファで、「誰でも使ってもいいよ」「でも全部機能があるわけじゃないよ」「バグや変更いっぱいアルヨ」という段階です。
ここでライフゲームのコード(Noodleらしい)を貼ると12割の人がブラウザを閉じてしまうので、公式の動画を貼ります

ライフゲームとは

コンウェイという人が発見したセルオートマトンです。って書いてるとどんどん説明することが増えてしまうな……
ざっくりいうと、「セル」というものがたくさんあって、それらはシンプルな動作をするけど全体としてみると複雑な動作をするものです。
ライフゲームは特に有名なので検索するといろいろ出てくるのですが、順番に読むと5億年はかかってしまうので、一番安全なWikipediaの記事を置いておきます

できたもの

画像1

VRChatなので、もちろん行くことができます (行って楽しいかは人によります)

困ったこと

最初に書いたとおり、Udonはまだアルファ(ベータですらない)ので、ここに書くことは(ほとんどが)解決されると思います。アルファに突撃する物好きと備忘録のために……
また、ドキュメントとか全然読んでないので、それすでにアルヨ、っていうのが結構あるかもしれません

・コンパイルが長い
複雑なUdonを作ると長い。めちゃくちゃ長い(手元で20秒以上)。 おそらく、ノード一個一個をリフレクションで作ってそれを更にコンパイルしてるからだとは思います。まあエディタ上で動く時点でありがたすぎるので(今は)贅沢言わないでおきます

・型が分かりづらい
Udon打ってて出るエラーの8割が型エラーです。基本的には気をつければ解決できるのですが、Arrayは完全一致させないとダメとかSystem.ObjectとUnityEngine.Objectが分かりづらい(これはUnityが悪い)とかがトラップ多めポイントです。ThisUdonBehaviourとかも欲しいですね

・Variableの型が謎
上の項目と近いのですが、特別ハマったので別項目で。例えば、public UdonBehaviour[]を用意したとき、Get VariableはObjectで返ってきて(しょうがない)、中身はObject[]なのですが(これもしょうがない)、その要素がUdonBehaviourであると思ったら実はGameObjectでした。謎。
しょうがないのでGetComponentして使ってたら、何かを編集した拍子にUdonBehaviourになった!!!???完全に謎。
まあアルファなのであんまり気にしないことにします。
ちなみにSetVariableするとそのままの参照が入ります。Arrayを使いまわそうとしてバグらせないようにしましょう(一敗)

・剰余がない
自らを剰余だと思いこんでいるOpDivisionOpMulitiplicationOpSubtractionによって一命をとりとめました。まあこれは一瞬で実装されるでしょう

・イベントのマージができない
ライフゲームでは、セルの選択時と実行時に色を塗る必要があるのですが、ノードの繋ぎのみでは同じコードの実行というのができません。結局CustomEventでやったのですがそういう想定なのかな。InputSlot増やせばできる?(未検証)

・Subgraphが引数をとれない
これはUdon触らないと説明できないですね。Variableを作ってGetSetすればいけるのですが、カプセル化・モジュール化したい…… Eventをノードで受け取れるのは優秀

あと、おそらく変数のキャッシュタイミング周りで2つバグを踏みました。
明らかにおかしい挙動をする時はNodeを複製して試してみましょう (MeshRenderer.GetMaterialとObject[].Lengthで起こりました)

Udonはすごいぞ!!!

困ったことをいっぱい書いたので悪く見えるかもしれませんが、(コンパイル時間以外は)めちゃくちゃ良いです。プログラムを書ける人ならほとんどリファレンスを読まずに作れると思いますし、内部的には結構しんどいはずの引数の挙動も(バグを踏まなければ)とても素直です。型の問題も、配列を使ったり複雑なことをしたりしなければ、よくある使い方集なのが出てきて初心者でも困らないようになると思います。(ネットワーク同期をわかりやすくするのは根本的に無理かもしれませんが……)

最後にライフゲームのNoodleを貼って皆さんの目を破壊したいと思います。良いVRChatライフ(ゲーム)を!
※Udonはまだアルファなので一般の方にはオススメしません

画像2

1. 初期化コード。CellsをGetComponentsInChildrenで取得し、それぞれにNeighborsを設定する。ノードベースで三重ループを書くな!

画像4

2. マスター側の処理。InteractでIsActiveの切り替え、IsActiveなら輪番で計算して最後に更新。最初は1フレームで全ての計算と更新をしていたのですが、9×9ですでに激重でした。(現在は12×12を8フレーム)
計算はマスターから各CellにCustomEventを送っているのですが、Cell内のUpdateの方が早いかもしれない

画像4

3. セル側の処理。上から、近傍の計算、Interactで色変え、更新処理。素直なコードなのでマシに見える。Linq使わせてくれ~~~(強欲)

この記事が気に入ったらサポートをしてみませんか?