UdonSharpの同期変数の仕組みが分かってきたので、
同期を活かした新しいワールドを作ることにしました。
ジャンルとしては仲間同士で遊べるようなゲームワールドを作りたいと思い、
繰り返し遊べる要素があって、
みんなで遊べるゲームとはどんなゲームだろうと考え、
思い浮かんだのが少し古典的なゲームですがマインスイーパーでした!
そんなこんなでマインスイーパーのVR化を進めていきました。
マインスイーパー自体のプログラムは、
だいたい想像することができましたが、
ググってみるとたくさんコードが転がっていたので、
ゲーム部分は想像していた処理とネットの情報を組み合わせながら、
こちらはおおむね詰まることなく完成させることができました。
基本的にはそのブロックが爆弾かどうか、
爆弾でない場合周囲に爆弾があるか、
あれば数字を表示して、なければ周囲のブロックに対して再帰処理を行う。
こんな感じのロジックですね。
そしてしっかり考えなければいけないのがUdonSharpのクセの強い同期処理です。
同期の方式としては基本的には以前世代別交流用ワールドに設置した
入室者カウンターと同じ方式を取り入れました。
同期は同期変数を用いて、
更新は全てオブジェクトオーナーに移譲する同期方法です。
参考)世代別交流用ワールドに入室者数カウンターを設置する
複数人でゲーム参加しようとした場合、
あちこちでブロックを消したりすることを想定すると、
排他的な処理は必須となります。
オブジェクトオーナーに同期変数の更新処理を集中させると、
通信のラグがどうしても生まれてしまうのですが、
ゲームの性質上、多少のラグがあっても確実に排他できる方が良いと考え、
この方式に決めました。
同期変数を利用して、
複数のユーザが同じようにゲームを進行できるようにするためには、
状態管理がとても大事で、
大きく「ゲームの状態」「設定の状態」「ブロックの状態」
この3つの状態をコントロールしてゲームの進行を維持することにしました。


そして設計を実装に落としていくことで、
ゲームが形になっていきました。
CyanEmuでデバッグもしつつ無事に完成と思い
いよいよVRChatへアップロードして動作確認をと、
アップロードしたワールドへ移動しようとしてみたのですが、
ぜんぜん読み込みが終わらず、、、
真っ暗な画面が延々と続く状態に、
VRChatはパフォーマンス系の制約も結構厳しいようで、
これまでもマテリアルの画像を圧縮して容量自体は削減したり工夫はしていたのですが、
実装面でも気をつけるべき点がいろいろとあるようです。
まず取り組んだのが
「脱string型」
string (文字列)は定義しているだけでもGC Allocを発生させるということで、
stringを使わないで実現できる部分は可能な限り他の型に変えたり、
処理の見直しを行いました。
(参考)こちらの記事参考にさせていただきました。
https://qiita.com/toRisouP/items/16bd06aa303a1bb1a747
でも結果は変わらず、、
次に取り組んだのが
「同期変数のサイズ削減」
同期変数は更新をかける度に通信が発生し、
ネットワークもそれほど潤沢では無いようで、
同期変数の通信量削減はとても大事なポイントのようです。
ということで、整数型の同期変数は出来るだけ
int型ではなくbyte型に変換していきました。
最近は通信量意識して実装したりとかあまりしていなかったので、
久々に型の重要性を再認識させられました(笑)
でも結果は変わらず、、
その次に取り組んだのが
「手あたり次第怪しい実装を外してみる」
もう原因が読めなかったので、
とりあえず怪しい実装やゲームオブジェクトそのものを削除してみたりと、
手あたり次第ですが、
症状改善するポイントを切り分ける作業を試みました。
特に怪しいと考えていたのが、
ゲームの設定上、マス目を16×16にした場合にブロックの数が256個になるのですが、
当初それぞれのブロックに対して、
どこからでも「破壊」と「フラグ」が実行できるようにと、
ボタンを5面に設置していました。
これだけでボタンの数が2,560個、
全部のボタンにインタラクトできるように
Udon Behaviourコンポーネントを割り当てていました。
(リンクしているスクリプトは同じものでしたが)
この辺りが読み込みを遅延させているのではと考え、
コンポーネントを外してみたり、
SerializeFieldを使っていたので外してみたり、
ブロックのオブジェクトの数を減らしてみたり、
ボタンを5面設置(5組)じゃなく1組にしてみたり、
と試行錯誤をしていく中、
結果ボタンの数を減らすことで事象の改善が現れることが分かりました。
「対策」
対策としてはシンプルにブロック上面に「破壊」と「フラグ」ボタン1組を設置する形に変更して、
何とかゲームが読み込まれる状態に改修することができました。

それでも他のワールドに比べると読み込み時間は遅く、
根本的な原因は特定できていないので、
これは今後の課題かな、
最後にパフォーマンスで苦労しましたが、
何とか対策することができ公開に繋がげることができました!
まだまだ知れたことは断片的かもしれないですが、
毎回いろいろ障害にぶつかっていて、
なかなか勉強させられますね。

1人で黙々と挑戦しても良し、仲間とわいわいと楽しんでも良しのVRマインスイーパーです。
ぜひぜひ遊びにきてください♪