オワコンみたいな海底に住んでいる潜水艦(わたし)はどうすりゃいいですか?

こちらのアドベントカレンダー用の記事です。
表と裏の同日をこんな記事で占領してしまって申し訳ないです。二正面作戦ということで……。

前の日の記事--->

パチスロは……ちょっと知らない領域でコメントができない……。

ここではUnityでオンライン機能を簡単にというか難しく付けることができるPhoton Fusionに対する愚痴が書かれています。


PUN ---> Photon Fusion

気に入っていた3DSのゲームのオンライン機能が永久メンテに入って、そのままサービスが終了してしまうだろうことになった。
寂しいのでUnityで再現することにした。ちなみに、既にCFWによって復活もできたという話を見た。意味がないと言えばないわけである……。

オンライン機能の実装に使ったのは Photon Fusion。

3年くらい見なかったうちにPUNから進化を遂げていたし、難しくなっていた気がする。

制作を開始したが結局詰まってしまった。実装したいものは沢山あって、軽量な通信、再利用可能なコーディング、独立したスクリプト。色々考えすぎているうちにやる気が迷子になってしまった。
こんなことになったのはオンライン実装にあたって拡張性を重視したコーディングを試みたからだと思う。クラスを色々に分けた結果、収拾がつかなくなった。
ある程度まで作ると沢山時間かけても全然見た目が変わらないことなんてザラにある。そこでやる気がどこかへ行ってしまったのだと思う。絵を描くときにも出がちなよくない癖が出た感じ。

Photon Fusion詰まったところ紹介

ホストマイグレーション(撤退)

一番時間がかかったことはホスト/クライアントモードで作っていたときに実装しようとしていたホストマイグレーション機能。

この機能はゲームのホストが切り替わる機能なのだがデフォルトでは実装されておらず、サンプル等が公開されているとはいえ複雑なもの。一回目は想定通り機能したが、二回目が上手くいくことなく結局3週間ぐらい時間を吸われてしまった。しかも実装できなかったというオチ。結論から言えばホスト/クライアントモードは自分には難しかった。
のでシェアモードで作業をすることにした。そうしたら思いの外スムーズに作業は進んだ。

ネットワークプロパティへのアクセス(ごり押し解決)

気を付けなくてはいけないことが、ネットワークプロパティにアクセスするとき初期化の前にアクセスしてはいけないということ。ローカルのときは順番を気にしなくてもすぐに処理が終わることが多かったからけども、オンラインになると同期の問題があるのか次のフレームにアクセスすると頻繁にエラーが飛ぶ。Debug.Logで測ってみると自分の実行環境ならば3フレームぐらい経ったあとならばセーフらしい。

このページで書かれている対処法のようにnullチェックをするというものが一番楽だと思う。エラーを防ぐために大量のnullチェックコルーチンを書く、というのはなんともダメなスクリプトの典型のような気がするけど他の方法が思い浮かばない。

ロビー実装(ごり押し解決)

4 v.s 4 のゲームであるためチーム分けが必要そのためロビー機能が必要。
接続した順番によって偶数、奇数とチームに振り分けるようとしたが、この順番を保存する変数をネットワーク化するかが迷いどころ。別に頻繁に変化するわけでもなくずれた場合はユーザー側で入り直してもらえばいいし。
そのため、「ユーザー生成時に新規ユーザーに現在の番号を割り当てて次のプレイヤー用に番号を加算する」という形をとったが一つ問題が生じた。
「新規ユーザー接続→ロビーシーン読み込み→新規ユーザー生成」
という順番で行わなければいけなくなった。またコルーチンかよ。これらの処理はFusionのネットワーク管理用のオブジェクトが行えるがまたコルーチンのお世話になっております。

またもう一つ面倒なことがある。現在ロビーにいるユーザー名を表示するという機能である。こちらは新規ユーザーがロビーに入ったときに表示が増えるわけだが、常にユーザーの情報をチェックするのは悪そう。当然入退室時のみ切り替えるようにするのだが、「ネットワークプロパティへのアクセス」のところで書いたようにあまりにもアクセスが速すぎるとエラーが出る。そのため、既存ユーザーが入室を感知(OnPlayerJoinedとかIPlayerJoinedとか)だと多分ダメ(だったと思う)。そのため、表示切替のトリガーは新規ユーザーで実装を試みた。
具体的には「新規ユーザー初期化(この間にロビーと同期)→シングルトン化したロビーのスクリプトを取得→新規ユーザーがロビーのpublic関数実行」という形。うーん、スパゲッティ。

弾は同期する?しない?(多分解決)

FPSなら相手プレイヤーへの攻撃手段である弾の同期はとても重要。大抵の場合は発射した側で処理を進めると快適なゲームになりやすい。
今回の場合は動きが直進のみの魚雷なので通信負荷を下げるためにも、毎回同期するのではなく発射時に座標と方向のベクトルだけを全プレイヤーで同期すればいい気がする。通信の速い遅いはあれど魚雷の軌道は一応同じになるので発射の情報さえ受け取れれば全員がローカルでのシミュレーションで魚雷を避けることができる。

多い?少ない?通信量(撤退)

一応オンラインで集まれるようにはしたが、やたらと通信量が多い。
一人での接続だけで、送信:56.0Kbps、受信32.0Kbps 。
これって多いんですかね?自分は3DSで動いていたような手軽なゲームを目指しているので少ない通信量が望ましいのですが……。ふと脳裏に浮かぶ通信量の少ないゲーム。クラッシュロワイヤル。通信制限下でもスイスイ遊べる学生の味方ともいえるゲーム。あいつ携帯の表示で0.1KB/sって書いてあった気がするのだがな……。まああれはプロがやっているだろうしアマチュア、それも完成すらしていないのにこんなところで時間は使うべきではないと判断。なにしろ通信量を減らそうと同期頻度を落としたらそれに合わせて動いているオブジェクトたち、つまりプレイヤーたちがカクカク動き始めた。これは自分の手に負えない。撤退。

撃沈

現時点では、プレイヤーの基本ステータスとゲーム本編内でのステータスを別のクラスに分けて、移動や攻撃なども別スクリプトに分けた結果、どうやってプレイヤーオブジェクトと紐づければいいのかわからなくなっている。

うーん、スクリプトにはインスペクター上でアタッチしたい値もあるし……。ステータス情報を持ったScriptableObjectを基本ステータスに渡しておいて、それを初期化用インターフェースの引数で渡して処理するか……。

なんか書いていてこれがいい気がする。

よし忘れないうちに実装しよう。久しぶりにUnity開いてコード編集を……
なんだこのスクリプト!?(驚愕)
なんと大量の食べ残したスパゲッティがあった。
やる気がまたどこかへ行ってしまった……。

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