neosでゲームを作るときに困ったことと解決策

こんにちは、Swesheloです。2022年7月末よりNeosVRを始め、現在はLogiX職人を目指しています。
本記事は[Neos VR Advent Calendar 2022](https://adventar.org/calendars/7623)、3日目の投稿です。4ヶ月Neosで遊んでみて、ゲームなどを作るにあたって、困ったことと、私が取った解決手段についてご紹介します。

## LogiXのノードがぐちゃぐちゃになる
> 解決: RedPrintを使う

RedPrintは、海外ユーザのUkilopさんによって開発されている、LogiXを平面的に組んでいくことができるツールです。[^1]
パッキングを1クリックで行える他、コメント機能なども用意されています。現在私は総ノード数が4000を超えるプログラムを作っており、恐らく完成する頃には10000ノードを超えるでしょう。素の状態では各LogiXの内容やパッキング位置を把握しておくのが困難な状態です。RedPrintはパック時に新しいスロットを自動で作ってくれるため、これだけで既に便利です。更に、範囲選択や選択内容の複製もできるため、最も重宝しているツールの一つです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/db17cee5-d501-b166-5f1d-7c68a5fa9216.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/9fabf4b5-e9b8-a441-c2e9-599c7ee6cae3.png)

## ノードがどこにあるのかわからない
> 解決: 検索機能のあるNodeBrowserを使う

NodeBrowser++という高機能なノードブラウザが内包された「LogiXTip++」を利用すれば、ノードがどこにあるのか探さずにすみます。同様に、BlueInspectorという高機能なインスペクターを内包したDevTooltipもあり、これらを利用すればノードやコンポーネントの名前や場所を完全に暗記する必要がなくなります。

## 両手にツールチップを装備したら身動きが取れなくなった
> 解決: MOD「[NoTankControls](https://github.com/furrz/NoTankControls)」を使う

[NoTankControls](https://github.com/furrz/NoTankControls)は、ツールチップを装備中の独特の動きを無効にし、通常の動き方に変えることができるMODです。これで右手でDevTool、左手でLogiXを操作できるようになります。

## 両手にツールチップを装備したらロコモーション変更できなくなった
> 解決: MOD「[Stop Disappearing Locomotion Menu](https://github.com/Banane9/NeosStopDisappearingLocomotionMenu)」を使う

[Stop Disappearing Locomotion Menu](https://github.com/Banane9/NeosStopDisappearingLocomotionMenu)は、ツールチップを装備した手のコンテキストメニューでもロコモーションが変更可能になるMODです。これで両手にツールチップを装備しても何の問題もなくなりました。

## Steam版と公式版、それぞれのディレクトリに.dllファイルを置くのが手間だ
> 解決: シンボリックリンクを張る

両方のビルドでどちらもMODを使う場合、実行時引数のLoadAssembly引数に絶対パスでdllを指定する方法もあるとは思いますが、私はリンクを張りました。管理者権限のあるcmd.exeで実行します。例えば、公式版のディレクトリにdllファイルを配置する運用にする場合は以下のとおりです。

```
CMD> mklink /D "C:\Program Files (x86)\Steam\steamapps\common\NeosVR\nml_mods" "C:\Neos\app\nml_mods"
```
これで、Steam版を起動する際には公式版のディレクトリにあるdllが参照されることになります。同様に`nml_libs`に関してもリンクを貼ると良いですが、こちらは頻繁にファイルを変えるものでもないでしょうから、直接ファイルを置いてしまっても良いでしょう。

## Slotを新規作成できない
> 解決: SlotをDuplicateする

LogiXではSlotを新規作成することが出来ないようです。そこで、作成したいSlotのテンプレートとなるようなSlotを1つ用意し、それを `Duplicate Slot` することで新たなSlotを作成することが出来ます。

## Dynamic Value (以下、DV)を上書きしたら、同じ名前の変数全てが上書きされてしまった
> 解決: Dynamic Value Spaceコンポーネントを付ける

同名の変数が同一の空間に複数存在し、書き込み先が一意に定まらない状況が発生しているために起こった事象です。意味のある集団ごとにDynamic Value Spaceを付けましょう。

## 配列がない
> 解決: SlotをObjectとして取り扱う

解決策の通り、SlotにDVを割り当て、それを複製したりすることでオブジェクトのように振る舞わせることが出来ます。難点として、ソートが非常に難しいです。Indexや、Offsetを操作することで順番を入れ替えることは可能ですから、ソートが不可能なわけではありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/f076e3ca-b0c0-f995-5a55-5b867779c960.png)

## Slotを配列として扱っていたら、どうもDVの挙動が怪しい
> 解決: `Delay` が必要?[^2]

Slotをオブジェクトとして取り扱うと、要素のテンプレートをDuplicateして要素を増やすことになりますが、ループ内で要素の増減(=>`Duplicate Slot`, `Set Parent`)を頻繁に行うと、どうも挙動が怪しいことがありました。私の勘違いかもしれませんが、現時点での対処として`Delay with value`で複製後のSlotを渡して、0.017秒待機するようにしています。また、要素を削除する際にDeleteしてしまうのではなく、ゴミ箱Slotに移動させ、要素追加時にゴミ箱内にSlotがあればそこから要素を拾い上げ待機時間をなくすという方法も採用しています。

## `Fire on Change`が動かない
>解決: そもそも`Fire on Change`を使わない または `Updating Relay` を使う

Fire on Changeは非常に便利なノードで、ある値が変動したときにその変動を検知してPulseを出してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/19461aea-455f-8370-c28c-6c76faa6be7d.png)
しかし、下の画像のような、「Reference先のDVを読む」などの場合は値の変更直後にPulseが出ず、対象のオブジェクトをグラブしてやるとPulseが出る、といった事象に悩まされることがあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/2195e56b-d123-f276-5188-f84d3b4f7326.png)


結果的に私が取った対処は、「Fire on Changeを使わない」というものです。特に、DVは値を変える際にPulseが出ているはずなので、Fire on Changeを使わず実装できるはずです。このように、値の変動にPulseを伴わせることができる場合は、そちらの手段を用いました。

なお、値が常に変動し続ける場合は、以下画像の`Updating Relay` ノードを使うことで、「グラブしたらPulseが出た」というような状況は回避できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/1cecf58e-a094-7eb6-f53a-787e98183c0b.png)
Lerp系の「変化のたびにPulseを必要としない、時間に応じて値が変わるもの」等の場合はこちらが有効です。このノードは、LogiXランタイムに毎フレームの評価を強制するらしいので、必要なときにのみ使うのが得策だと思います。

## あるSlot内のChildから条件を満たすもののSlotリストが欲しい
> 解決?: 個々のSlotにGUIDを付けました

本当はSlotのRefIDをテキストとして保管しておき、必要に応じてそのRefIDから対象のSlotを特定するということがやりたかったんですが、Zozokasuさんが謎技術で実現しており、更にその手法が自分には謎すぎたため断念しました。

条件を満たしたSlotの名前をCSVで保持する実装を行いました。

## Dynamic Impulse Trigger/Reciever with valueで複数の値を渡したい
> 解決: Dynamic Impulse Trigger/Reciever with Slot を使う

複数の引数を取る関数が欲しい時、DVを持つSlotを渡し、受け取ったSlotからDVを読み出すことで複数値の読み出しが実現できます。

## 特殊な型のDVを作りたい、特殊な値をD.Impulse Trig/Recv w/ Valueで渡したい
> 解決: `DV Node Generator`を使う

先述したノードブラウザを使います。Extra内にある「DV Node Generator」を取り出し、作りたいNodeの種別を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/963ed090-dcc4-2c52-16b0-442af6de2a73.png)

続いて、型名がわからない場合は同じくノードブラウザのExtra内「Ref Type Analyzer」に投入し、型名を控えます。
控えた型名を、「Custom Generic Type Name:」に投入します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/40c710f1-a6fd-94ad-5cc5-b1c312d47d8c.png)

通常は得られないNodeが出現しました。完成です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/a441baa0-a900-b08a-3ea5-de77effc291b.png)

## 最後に
VRの中で物を作り即座に動きを付けられるという、Neosだからこそできる素晴らしい体験に感謝したいと思います。もしこの記事の内容に誤りがあった場合は、是非コメント欄で教えてください。補足情報も歓迎します。

なお、本記事内では利便性など諸々の都合上アイテムのリンクは用意しておりません。来年のAdventCalender(があれば)ワールドを投稿する形にしてアイテム配布なども行ってみたいですね。

明日のAdventCalender記事は らいす🌾Rice_s さんによる「NeosVRに来てから4ヶ月経ったお話」という内容です。

[^1]: 平面にLogiXを記述することを避ける派閥もあるようです。
[^2]: これに関して、執筆中に`Updates relay with value`というノードについて知ったので以後挙動とか調べておきます、まだよくわかっていないです。