セッションの概要と自己紹介

﨑健悟氏(以下、﨑):それでは「Unityによるマルチプレイヤーアクションのための別解C#統一理論」ということで、セッションを始めます。

はじめにセッションの概要です。本セッションでは、最初にネットワークゲームシステムの構成について、さまざまな選択肢やその選び方の話をします。続いて、協力型MO(Multiplayer Online)アクションを実現するために採用した、よりタイトル固有の技術についてお話ししたいと思います。

得られる知見としては、Unityでネットワークゲームを作成するにあたって考慮すべき、技術要素とその選び方。また、アクション性の高いMOゲームを実現するための、より実践的なテクニックなどについてお伝えできればと思っております。

それでは、あらためまして自己紹介です。ゲーム事業本部に所属している﨑健悟と申します。PC向けMMORPGのサーバーエンジニアとして業界に入り、モバイルゲームの勃興に合わせて軸足をスマートフォンに移して、途中アーケードに寄り道したりもしつつ、2016年にDeNAに入社しました。

入社後は内製汎用リアルタイムリレーサーバーのIRIS/IRIS2の開発・運用に携わり、その後、新規ゲームタイトルの立ち上げに伴い異動して、マルチプレイパートの開発を担当しています。2022年3月現在も同タイトルに所属して、引き続き開発・運用を担当しています。

あらためて目次です。最初にゲームデザインに合わせた構成の検討ということで、まず前提となるゲームの内容を共有して、その後、ネットワーク構成・同期方式・言語などの各技術要素についての検討をお話しします。

その後、協力型MOアクション実現のための設計や実装ということで、共有ゲームロジック・通信方式・ユーザーの体感を損ねない同期の実現といった、よりゲームの内容に属した技術についてお話しします。

前提となるゲームの内容

まず、ゲームデザインに合わせた構成の検討について。前提となるゲームは、iOS/Android対応のモバイルゲームで、アクション要素の強いRPGになります。ユーザーはさまざまなクエストを繰り返し攻略することで経験値などの報酬を得て、キャラクターを成長させていきます。

各クエストはそれぞれ数分のアクションゲームのようになっており、プレイヤーは自身のキャラクターを操作して、フィールド上の敵を倒して条件を満たすことでクエストをクリアします。敵との戦闘はリアルタイムで進行して、ユーザーはその中でコンボや回避、スキルの発動などアクション性の高い操作を求められることになります。

このクエストのうち一部のクエストがオンラインマルチプレイ、いわゆるMOの専用クエストとなっていて、インターネット越しにつながった他のプレイヤーと協力して敵を倒していくことになります。このネットワークプレイに関しては日本国内だけではなく、世界各地の人が快適なMO体験をできることが求められます。

ネットワーク構成の検討

前提の共有が終わったところで、具体的な検討に入っていきます。まずはネットワーク構成の検討について。マルチプレイの実現のためのネットワーク構成の選択肢は、大きく2つに分かれます。1つ目はクライアント同士が直接通信をするPeer-to-Peer型、P2P型と呼ばれるもの。(スライドを示して)これが図でいう一番端のものになります。

もう1つは、各クライアントが運営側の用意したサーバーに接続する、クライアント・サーバー型。図の真ん中よりのほうになります。この2つについてはそれぞれ得手不得手があるので、ゲームの特性に合わせてどちらを採用するかを判断していく必要があります。

本タイトルにおいては、以下3つの要素が考慮として上がっていました。1つ目が多人数安定性。こちらは耳馴染みのない言葉だと思いますが、実際汎用的な言葉ではなく、今回の説明のために作った単語になります。これはクライアントの一部に問題が生じた場合に、それが他のプレイヤーに対して影響するかどうかというところに関しての安定性、ゲームを続けられるかという部分の評価になります。こちらについては表のとおり、クライアント・サーバー型が有利です。

これはどういうことかというと、P2P型は図に表したとおり、4人であれば、その4人がメッシュを組んでそれぞれに接続をしています。この接続において、例えば上の2人の間の1本の線に問題が生じた場合、P2P型では、上の2人同士はそれぞれが切断したことがわかります。

しかし、下の2人にとっては、上の2人のどちらに問題が生じたのか、ゲームの進行においてどちらを優先するのかの判断がより複雑になってしまいます。そのため、この部分の安定性でP2P型は若干不利になります。

一方で、クライアント・サーバー型は図にあるとおり、中央にいるサーバーに対してすべてのクライアントがつながっています。サーバーが、(クライアントの)誰がいないかを判定できるので、より安定してゲームを進行できる特徴があります。

続いて、チート耐性。これは不正なクライアントからの攻撃に対する耐性(のこと)です。こちらに関しても、クライアント・サーバー型がやはり若干有利です。

というのは、P2P型はクライアント同士が直接通信する関係上、ユーザーが所持している端末の中にあるアプリケーションがすべての判断を司るかたちになるので、その端末の(中の)自分たちが管理できない場所にあるプログラムの中で処理が行われるので、チート対策として取れる対応の範囲に限界があるかたちになります。

一方で、クライアント・サーバー型は運営側、つまり自分たちが用意した管理された環境にいるサーバーの中にあるプログラムで判定が行われるので、より安定したかたちで、より高度なチート対策が行えるところで若干有利になります。

3つ目にコストの話。これはもうそのまま文字どおりで、主に運用時にかかる費用です。こちらはP2P型が有利です。これは図で見れば一目瞭然ではありますが、P2P型がクライアント同士のみで完結しているのに対して、クライアント・サーバー型は上に1つ管理しているサーバーが入ってきます。このサーバー分の費用がどうしてもプラスになってしまうというところで、P2P型のほうが有利です。

今回のタイトルでは、まず3人以上の協力プレイが企画されていたことや、近年のチートの傾向、弊社の既存タイトルでの傾向を踏まえ、多人数安定性とチート耐性を重視して、コストについても許容範囲に収まるという判断をして、クライアント・サーバー型を採用しました。

同期方式の検討

続いて、同期方式の検討です。MOの同期方式には大別すると、完全同期型と非同期型の2種類があります。完全同期型はフレーム単位で全クライアントの入力状態などを集約して、都度全体の状態を更新する。要は、1フレームずつ状態をまとめて同じように更新をしていくかたちになります。各クライアント上で表示される内容はフレーム単位で同期されているので、原則すべて同一のものとなります。

一方の非同期型ですが、こちらは各クライアントがそれぞれ独立してフレームを更新していきます。自キャラの行動など、共有したい情報については都度他者に送信し、受け取った側はそれを自身の環境に反映しながらゲームを進行させていくことになります。

こちらは非同期型という呼び名が完全同期型に対応するかたちでつけられていますが、実際のところ、受信した他者の状態をその時点の自身の環境に随時反映していくかたちになるので、「時間軸に対してバッファを持った同期が行われている」という理解のほうが適切かもしれません。

完全同期型は、1つの端末にネットワーク経由で全コントローラーをつなげているのと同じようなかたちになるので、同期設計的にはとてもシンプルです。ただし、フレームごとに情報を集約する構造上、最もネットワーク環境の悪い参加者の状態にフレームの更新処理が引きずられてしまう。要は待たなけれないけないという問題があります。

これがモバイルでさらに3台以上となると、結果としてラグの発生頻度がどうしても高くなってしまい問題になってくるんですが、一方で、これを軽減するテクニックも世の中には存在しています。存在はしているんですが、これは本来待たなければいけない部分をどうにかするということになってくるので、相応に複雑なものになります。今回に関しては、順当に非同期型を選択しました。

言語・開発環境の前提と検討

続いて、言語・開発環境の検討です。こちらについてはまず前提からお話しします。クライアント・サーバー型のゲームにおいては、プログラムを構成する要素は大きく以下の3つに分けられます。まず1つ目はゲームロジック。これはオブジェクトの管理や攻撃判定など、MOの対象となる戦闘の論理的な処理を担うかたちになります。要は、ゲームとして何を行って、どういうルールで物事を解決していくかという部分に関しての実装です。

2つ目がクライアント。これは先ほど言ったゲームロジックを内包していて、それに加えてアプリとしてユーザー端末にインストールされるための機能を実装する部分になります。例えば描画とか、プレイヤーからの入力を処理してゲームロジック側に渡すような機能が含まれます。

3つ目がサーバーです。こちらはクライアントと同じようにゲームロジックを内包しつつ、Linux上でサーバープロセスとして実行されるかたちになります。ゲームロジックを内包した上で、クライアントの管理や送受信といった機能を実装することで、ゲームロジックを駆動させてゲームを実行します。

前提を共有したところで、実際の検討です。先ほど言った3つの要素のうち、まずクライアントに関しては、社内の(既存の)開発体制があるので、C#(Unity)になります。ゲームロジックとサーバーをどうするかという部分の採用パターンに関しては、大別すると以下の3種類に分かれてきます。

まず1つ目が、golangやC++などクライアントとは別の言語でサーバーを実装して、クライアントとサーバーのそれぞれでゲームロジックを実装するパターンです。これはクライアントとサーバーそれぞれ環境ごとの最適解を選べるので、理想的には最も実行効率の良い実装を行えます。ただし問題として、ゲームロジックをそれぞれの言語で二重に実装をした上で、継続的にそれぞれの環境用に保守・拡張をしていく必要があります。

今回はアクション性の高いタイトルになるので、ダメージ計算やコンボのタイミング制御などで、ゲームロジックが相応に複雑になることが見込まれていました。そうなってくると、別々に開発して、その2つの実装をそれぞれ整合性を保ちながら作っていくことは困難であるという問題がどうしても発生してしまいます。

2つ目の選択肢は、golangやC++などクライアントとは別の言語でサーバーを実装し、一方でゲームロジックはクライアントとサーバーのどちらかの言語で実装したものをライブラリ化などをして共有するというかたちになります。例えばサーバーと共有ゲームロジックにはC++で実装をして、クライアントのUnityではC++で実装したゲームロジックをライブラリとして利用するというような形式です。

この形式の場合、ゲームロジックの二重開発は避けられます。しかし、ゲームロジックがライブラリになっていてUnity側とつなぎこむということで、Unityのほうの描画とか、入力からのゲームロジックへのキャラクターのアクションの入力とかに関してそれぞれAPIを作るという話になってきます。そうすると、どうしてもAPIが複雑になってしまうという問題があります。

最後に3つ目です。これはC#、つまり.NETでサーバーを実装して、同じくC#で実装したゲームロジックを(クライアント・サーバーの)双方で共有するというかたちになります。こちらは(クライアント、サーバー、ゲームロジックがすべて)C#、C#、C#という組み合わせになるので、構成としては最もシンプルで、開発部分も二重開発がなく最小となります。

ただネックとしては、C++などの歴史のある言語やgolangといったサーバー環境を設計から念頭に置いていた言語に比べると、.NETでのサーバーは社内での採用実績も少なく、実際のところ運用に耐えるのかということが最大のネックとして考えられていました。

ただ、こちらについては、社内で行った基礎的な評価の結果も悪くなく、また、ゲームでの商用運用実績もその当時は世に出てきていたので、十分使えると判断いたしました。

ということで、結論としてはクライアント、サーバー、ゲームロジックのすべてをC#で実装することにしました。

ここでこういったジャンルの情報を日々追っている方は、当然(このように)思われると思います。「それってつまりC#大統一理論なのでは???」と。「C#大統一理論」をご存知ない方もいると思うので説明すると、CySharpのneueccさんが提唱された、「ネットワークゲームのすべてのプログラムをC#で実装する」という思想です。

今回でいうと、クライアント、サーバー、ゲームロジックをC#というところで(C#大統一理論と)被ってくる訳ですが、弊社の場合は、MOバトル以外のサーバーで一般的にAPIサーバーと言われたりする部分については社内の既存の資産が十分使えるので、MOバトル部分のみをC#にする形式をとりました。ということで、今回のタイトルの開発体制としては、C#小統一理論ぐらいの気持ちでやっています。

次回に続く