[2020/12/06] MLAPIのSteamトランスポートが普通に存在した件を追記

前々回に書いた記事で、UnityでFall Guysみたいなゲームを作るなら、どのネットワークソリューションを使えばいいのか?という話をしました。

今、Unityでネットワークマルチプレイ作るのに何を使えばいいのか

この記事では、まず現在Unityの公式ネットワークソリューションが存在せず、宙に浮いていてユーザーが大困惑させられている事を問題視しました。

が、

その数日後に突如としてMLAPIが公式になったので、たちどころに問題は解決しました。

MLAPIがUnity公式ネットワークソリューションに採用された!!

それから、Unityで使えるネットワークソリューションを概観してみましたが、それは図らずも、ソリューションの進歩とそれに伴うネットワーク構成の変遷の歴史を追いかけるものになっていました。

つまり、最初は素のP2Pだったネットワークが、マスターサーバーが使われるようになり、リレーサーバが使われるようになり、最近では専用ゲームサーバ構成に移行する流れになっているというストーリーです。

記事は思ったより反響をいただきました。例えば「サーバーを自前で実装する選択肢は?」みたいな反応もありました。

というわけで、今回の記事では前回頂いた反応などに基づいて、補足するような内容を書いてみたいと思います。

自分でサーバー実装するって選択肢は?

たしかに、既存のネットワークソリューションを使わなくても、自分でサーバーのコード書くという選択肢はあると思います。リアルタイム性を考えるとnode.js+Websocketなどの構成が有力でしょうか。

私は前回の記事で、「Fall Guysみたいなゲームを作る」という要件で頭が一杯だったので、自作サーバ実装については考えてませんでした。

「Fall Guysみたいなゲーム」とは、具体的にはどういう要件か?と考えると、まず真っ先に①”60人の多人数マルチプレイ”が挙がりますが、もう一つ、②”相互作用する物理演算”の楽しさも重要な要素です。

①”60人マルチプレイ”だけなら自作サーバ実装で実現可能かもしれませんが、②”相互作用する物理演算”は自作サーバ実装では難しいです。

Fall Guysでは巨大なボールをサッカーみたいに相手側のゴールに入れ合う競技があります。自分もボールを押すし、反対側からは相手もボールを押すというようなシチュエーションはまさに物理演算の相互作用です。

サーバーあるいはホストが代表としてゲーム全体の物理演算を行ってくれれば、”一貫した物理演算”が実現できるので、問題は起きませんが、プレイヤーがそれぞれローカルで物理演算した結果を押し付け合えば、コンフリクトを起こしてボールの挙動がグチャグチャに破綻するハメになります。

ですので、ゲームの要件に”一貫した物理演算”が必要なら、自作サーバ実装だと難しそうですし、そうでないなら自作サーバ実装も選択肢に入ってくると思います。

Unityでの自作サーバ実装事例

例えば、凹さんが作ったUnityでWebsocketサーバを使ってUnityのオブジェクトを同期するシステムの事例があります。

Unity WebGL x WebSocket で複数クライアント間の大量のオブジェクトを簡単に同期できる仕組みを作ってみた

このシステムでは30~40人のマルチプレイで動作したそうです。

しかしやはり、”一貫した物理演算”は提供されていません。各プレイヤーがそれぞれローカルで動かしたプレイヤーキャラの座標を送信、同期する仕組みです。

各自のローカルで動かしているので、入力が即時反映されるというメリットがあります。”一貫した物理演算”では入力をサーバに送って演算結果をもらってようやく入力が反映するので、ラグが発生する問題があります。

他の事例で言えば、白猫プロジェクトのマルチプレイでもWebsocketサーバが使われています。

白猫プロジェクトの裏側! ~パフォーマンスチューニングとリアルタイム通信のすべて~

白猫のマルチにはホスト、ゲストの概念があり、ホストで一括して処理をしてゲストは同期だけする仕組みのようです。

このように、クライアントをホストとして扱う仕組みを用意すれば、自作のWebSocketサーバでも”一貫した物理演算”を実現する事は可能でしょう。

しかし、どうせホストを立ててそこでまとめて処理するならP2P+リレーサーバの構成でもいいんじゃないのって気がします。

TCPはUDPより遅い

ちなみにWebSocketではTCPプロトコルで通信が行われますが、TCPはUDPより遅いです。そもそもパケットが相手に到達するまでの遅延がUDPの2倍くらいかかるようです。

ネットワーク ゲームにおけるTCPとUDPの使い分け

遅延を小さくしたいゲームではTCPよりUDPを使いたいという事になりそうです。

Unity Physicsを自作サーバ実装で使う?

「Unityのヘッドレスビルドを自前でホストしてサーバとして使うなんて馬鹿げてる!」みたいな論調も見かけました。

まあその気持ちは分かります。試算した事は無いですが、Unityの専用サーバ構成でゲーム運営したら恐ろしいくらいサーバ代がかかるのは目に見えてます。

Fall GuysはSteamで2千円くらいで売ってるので、全てのプレイヤーが前払いしてる(PSPlusで無料で配られてたけど)わけですから、そこからサーバー費は捻出できるでしょう。FortNiteなどは基本無料で遊ばれまくられてしまって、採算がとれているというのが不思議なくらいですね。

「Unity Physicsを自作サーバ実装に乗っければ、ヘッドレスビルド使わなくても一貫した物理演算を提供できるやん」みたいな論もあった気がします。

たしかに、DOTSやUnity Physicsのパッケージはソースが公開されてます。しかし、Unity公式パッケージのソースは、Unity Companion Lisenceで提供されている場合がほとんどです。

つまり、Unityでビルドして組み込まないと利用できません
というのも、もしUnityを通さずに自作サーバにUnity Physicsを組み込めるなら、そもそもUnityを使う必要が無いし、Unityにお金払う必要もなくなります。それだとUnityにとっては困るからです。

ですので、Unityの規約が変わらない限りは、イヤでもヘッドレスビルドを使わざるを得ないでしょう。

ヘッドレスビルドは簡単

そもそも、Unityのヘッドレスビルドをサーバーに使うという事は、サーバサイドの実装が一切不要という事ですから、一番簡単です。Unityの一つのプロジェクトでクライアントとサーバが完結します。

専用ゲームサーバのサーバ代は高まりますが、サーバサイドを書くエンジニアを一人増やすコストと比べたらどっちがオトクか?という考え方もできます。

ヘッドレスビルドサーバならUnityの物理演算以外にも、AnimationやらNavMeshやら、Unityの全ての機能が使えます。当たり前ですが。

専用ゲームサーバ構成とチートの話

余談になりますが、MMOやソーシャル性のあるスマホゲームでは、チートをいかにして防ぐかが常に問題でした。

昔ながらの一人またはローカルマルチプレイで遊ぶ家庭用ゲームは、ソフトを買った上でチートをどれだけしたところで、ゲーム会社には特に不利益はありませんでした。

しかし、MMOなどでは、例えばチートで課金アイテムが取り放題なんて事になれば、真面目に課金して買ってるユーザはアホらしくなります。正しく遊んでいるユーザに不利益が発生しますし、チートが横行するゲームからはユーザーが去っていき、ゲーム会社にも打撃です。

MMOのチート対策は明快です。要するに、全てのゲームの処理をサーバ側で行います。クライアント側は単にサーバから送られてくる情報を表示してるだけです。これではチートしようがありません。クライアントからは操作の入力しか送れないからです。

それではスマホゲームではどうだったか?というと、例えば、スマホ以前のガラケーで遊べていたモバマスなどは、モバゲーのWebサイト上で動いているブラウザゲームだったので、そもそもゲーム処理はサーバ側で全て行うしかありませんでした。チートの余地も無いという事です。

それからスマホのネイティブアプリのゲームが出現し始めると、全てをサーバ側で処理するというわけにはいかなくなりました。家庭用のアクションゲームと遜色ないような、白猫プロジェクト崩壊3rdFGOなどなど、このようなゲームはサーバ側で処理できるわけありません。という事は、クエスト部分はクライアントで処理して、クエスト成功、失敗などの結果だけをサーバに送信する形になりました。

クライアントで処理する=チートされるという事です。
実際、白猫プロジェクトではキャラ無敵化などのチートが存在するようです。

じゃあユーザへの不利益になるじゃないか!と思われるかもしれませんが、これらのゲームはチートされても致命的な不利益にはならないように仕様を工夫してます。

つまり、クエストを不正にクリアされたところで、大した問題ではないです。重要なのは、ガチャで不正されない事です。クエスト部分はクライアント処理でも、その他のアイテムや課金石周りなどは当然サーバ処理されているので、チートの余地はありません。

チート可能な部分に大きな価値を置かない事でユーザーの不利益を防いでる感じです。(もちろんクエスト部分でもできる範囲で間接的にチート対策はしているはず…例えば通常プレイでは有り得ないほどの短時間でのクエストクリアを検出したらバンするとか)
白猫プロジェクトはネットワークマルチがありますが、協力プレイのみで、対戦ではないので、まあ最悪チートされても致命傷にはなりません。

一番避けたいのは、対人での対戦プレイでのチートです。
対戦プレイなんて、スマホゲームではあまり実装されません。危険だからです。対戦プレイで相手がチートしたせいで負けたら、当然腹が立ちます。

しかし、FortNiteやFall Guysが流行ってしまっている現状を見ると、いよいよ対戦プレイでのチート対策に本腰を入れざるを得なくなるでしょう。

専用ゲームサーバ構成に移行しようという流れはそういうチート対策からの観点でも有効です。モバゲー時代から一周してきて、専用ゲームサーバ構成ではまたサーバ側で全ての処理を行えるからです。

ちなみに、クライアントと同じロジックを自作サーバーに持たせて、対戦時だけサーバー処理すればいんじゃね?という論もあるかもしれません。
それはまあ…そうなんですが…

ヒーローズチャージというゲームでは、アリーナという疑似的なPvP対戦機能がありましたが、チート防止のためその時だけサーバ側でバトル判定を行う仕様でした。一応クライアント側でもバトルするのですが、勝敗は最初からサーバ側で判定されているという事です。

しかし、そのせいで、自分の画面では勝ったはずなのに、リザルト画面を見ると負けになってる事がたまにありました。サーバ側でのロジックとクライアント側でのロジックが微妙に食い違ってるせいでしょう。

ヘッドレスビルドサーバーを使えばクライアント側とサーバ側で同じバイナリを使ってるので簡単に処理を一致させることができます。

Steamのリレーサーバの話

これは単なるオトク情報の話です。

前回の記事では、PhotonCloudやモノビットクラウドを使えばリレーサーバをホストしてもらえるけど、利用料金がかさんでしまうという話をしました。

例えば、PhotonCloudの場合、100CCUまでなら100ドルくらいを一度だけ払えば済みますが、500CCUになると毎月1万7千円ほどを払うハメになります。

「趣味で作ったマルチプレイヤーゲームをリリースしたいけど、万が一流行ったらサーバ維持費で破産してしまうよ…」

そんなあなたに朗報です!

実はSteamは、無料かつ無制限で利用できるリレーサーバを提供しています!

Steam ネットワーキング

Steamにゲームを登録するには最初に1万円くらい登録料を払う必要がありますが、その後は完全に無料(というか、ゲームの売り上げからSteamに払われる手数料で賄ってる)なので、どれだけゲームが大ヒットしても心配ありません。

例えば、クラフトピアというゲームでも、マルチプレイにSteamのリレーサーバを使っています。

Steamのリレーサーバ、余りにもコスパが良すぎます。もうこれ一択じゃん!って感じですが、気になる所もあります。
多分、対応してるのはPCだけで、スマホとかは非対応でしょう。Steamはスマホゲームとか扱ってませんから。そして、コンシューマプラットフォームも非対応と思われます。コンシューマはSteamじゃないので。つまり、クロスプラットフォームのマルチプレイを実現したければSteamのリレーサーバは選択肢から外れるかもしれません。

実際にUnityのゲームにSteamのリレーサーバ対応したい!という話もちょっと書いてみます。自分もいつかやるかもしれないので。

まず、Steamworks SDKをC#でラップした、Steamworks.NETというものがあります。とりあえずこれだけでUnityから触れます。

「もっと簡単に触りたいんだが?」という場合は、Mirrorに統合されたFizzyFacepunchトランスポートというものがあるので、Mirrorから簡単に使えそうです。

あるいはこちらのUnityアセットでもSteamリレーサーバ機能がMirrorに統合されています。

実際にSteamリレーサーバを実装してみた話では、Chiepommeさんの記事が参考になりそうです。

https://chiepomme.gitbook.io/chiepomme/happy-oshare-time/assets-libraries

Steamworks.NET で P2P 通信してみた

ちなみにMLAPIへのSteamリレーサーバ統合は無いんですか?というと、

公式でSteamP2PTransportが用意されてます。

Unity NetCodeの補足

前の記事では、「Unity NetCodeはよく分からん」みたいな話をしましたが、実際にUnity NetCodeを使い込んでる人から情報をいただきました。

まず、大前提として、Unity NetCodeには現在P2P構成は影も形も存在してないそうで、専用ゲームサーバー&クライアント構成しか無いようです。

Unityのブログ記事にはあたかもP2Pで決定論的ロックステップが実装されてるかのように書かれてましたが、あれは妄想…あるいは将来的にはこうなったらいいな…みたいな気分が書かれていたようです。

もうUnityのブログ記事をソースにして書くのイヤになってきたな。

現在のUnity NetCodeの構成は、ヘッドレスビルドされた専用ゲームサーバとクライアントの組み合わせらしいです。
クライアントからサーバへは入力情報だけが送信されます。サーバはゲーム全体の物理演算を行って、結果をクライアントに送って同期します。
クライアントはサーバのシミュレート結果を待ってる間ラグが発生してしまうので、自キャラの動きだけローカルで物理演算して先読みして動きます。他のキャラからの干渉などで予測に誤差が発生したら決定論的ロールバックで巻き戻します。

どうせサーバで演算して結果を同期するだけなら決定論的かどうかはあんま関係ないだろと思いますが、一応予測の役には立つようです。

将来的にはP2Pモードも実装されるかもしれませんし、その時は決定論的実装が本領を発揮するかも…?

UE4の話

こういう話もあります。

UE4は詳しくないですが、元々エンジンに専用ゲームサーバ構成のネットワーク機能があるようです。

いくら強調しても足りないですが、Fortniteを作ったのはUE4を作ったEpicです。

というか、EpicはUE4を使ってFortniteを作りました
つまり、Fortniteを作るためのノウハウ最適化は全部UE4にフィードバックされてます。

https://www.unrealengine.com/ja/tech-blog/unreal-engine-improvements-for-fortnite-battle-royale?sessionInvalidated=true

つまり「UE4ならFortniteみたいなゲームが作れる」という事は確実に言えます。

それに対して、「UnityでもFortniteみたいなゲームが作れる」かどうかは相当に怪しい話です。

Epicが頑張ってゲームを作るたびにUE4にそのノウハウが蓄積して「実際に凄いゲーム作れるエンジン」として磨きがかかっていくのに対して、Mediatonicが必死こいてUnityでFall Guysを作るために行った最適化は、決してUnityエンジンには反映されません。

Unityフォーラムで見かけた意見ですが、「Unityで本格マルチプレイゲームを作ろうとした会社は、最終的に別のゲームエンジンへの移行を余儀なくされる場合がほとんど。逆にUE4で成功したゲームは大抵UE4組み込みのネットワーク機能をそのまま使っている」という話がありました。

諸々考えると、”本気で”バトルロイヤルライクなゲームを作って成功を狙うなら、UE4を使うべきなんだろうなと思ってしまいます。

UnityからUE4への移行は当然コストがかかりますが、Unityでネットワークソリューションをどうしようか?とか選定や最適化に手間取るコストに比べたら、箱から取り出してすぐに最適化されたネットワークが使えるUE4を使う方が手っ取り早そうです。

ちなみに、UE4にはSteamリレーサーバ統合も公式で用意されてます。

Steam Socket を使用する

所感

色々書きましたが、結論としてはUnity捨ててUE4に移行しよう!って事ですかね…

専用ゲームサーバ構成について長々と話してますが、専用ゲームサーバ構成は個人制作のゲームだとコストがかかりすぎて選択肢から外れます。

個人で低コストにマルチプレイゲームを作るなら、やはりSteamリレーサーバが良さそうです。しかし、P2P+リレーサーバはチートに対して脆弱です。将来的に、Unity NetCodeが決定論的P2Pに対応して、それをSteamネットワークに統合すれば、コストゼロかつチートも効かない鉄板構成になりそうですね。
PhotonQuantumがあるじゃない?って話ですが、あれはそもそも利用料金が高いですから。