Unreal Engineでサクッとオンラインゲーム制作 Part2 ちょっと作ってみる

Post 2022年2月18日金曜日

C++ UE4 UE5 Unreal Engine オンラインゲーム

前回の続きです。

今回からオンラインゲームの制作をスタートしようと思います。

ちなみにネタバレなんですが、この記事は導入編となっていて、実際の制作はGameplay Ability System (GAS)を使って制作していくので、この記事の内容はほとんど全く使ってません!笑

あくまで概念の理解用です。

GASを使った本編は次回からやっていきます。

ただ、GASを使う場合も今回の概念は理解しておく必要があるので、ぜひ御覧ください。

お約束
この記事作成にあたって使用した主なUnreal Engine バージョン4.27.2

本日のゴール

ゲームの本体を作る


オンラインゲームの種類について

さていきなりですがオンラインゲームは二種類に大別されるわけです。

・プレイヤーの誰かがサーバーとなる"ListenServer"方式

→ サーバーを立てる必要がないが、サーバーとなっているプレイヤーが落ちるとゲームが終了する

・専用サーバーを有する"DedicatedServer"方式

→ サーバーを立てる必要があり、維持費がかかる

それぞれのメリットデメリットはあります。我々のような個人開発者であれば通常、維持コストがかからない前者の"ListenServer"方式を採用しますね。

ですが今回はあえて後者"DedicatedServer"方式に挑んでみたいと思いました。技術?採算性?維持費?そんなもん知らん。


オンラインゲーム(マルチプレイヤーゲーム)においてのGameModeについて

オンラインゲームを作る際に必要な、GameModeなどについて少し説明しておきます。

オフラインゲームではなんとなくで使い分けていたこれらのクラスですが、オンラインゲームでは明確に使い分ける必要があります。

GameModeなどの概要説明 (Listenサーバーを想定)

GameMode ... どのGameStateを使うか?どのPlayer Controllerを使うか?などを指定する、根本を決める機能。サーバーにしか存在しない。クライアント側からはアクセスできない。

GameState ... 大本はサーバーに存在し、クライアント側はReplicateされたGameStateを持つ。クライアント側から書き換えるためにはRPC Run On Serverを使った書き換えが必要。各PlayerStateの情報を持つ。基本的にはゲームスコア、残り時間などプレイヤー全体で共有したい情報を保存しておく。

Player Controller ... 前回の記事でも書いたとおり、サーバー側とクライアント側どちらも持つ。クライアント側では自分のControllerしかいじれない。RPC Run On Serverでは、このPlayer Controllerを橋渡しにしてサーバーに情報を送る。(なのでPlayer Controllerを有さない普通のアクターではRPC Run On Serverは使えない)

Player State ... クライアント側から直接他のクライアントの情報を得ることのできる唯一のクラス。基本的には各プレイヤー個人個人で異なる情報を残しておく。(各プレイヤーのHP, MP, 残弾, 武器などなど...)

他のクライアントのPlayer Stateにアクセスするには、GameState経由でアクセスする。

参考:


【UE4】GameMode、GameState、PlayerState、PlayerControllerの関連を確認してみる

UE4 ネットワークマルチプレイヤーゲームを作る時の役割について


プロジェクトの作成

今回は4.27.2バージョンを使います。(まあバージョンはなんでもいいですが)

私はmacもWindowsもどちらも持ってますが、今回はUE5ではないので、どちらでもいいです。

プロジェクト作成の時のポイントとしては、Project SettingsでC++にしておくことです。

(後述しますが、Listenサーバー型で作るオンラインゲームならBlueprintだけでもできそう)


今回、雪合戦なので"WarOfSnow"というプロジェクト名にしました。

早速マルチプレイで動作させてみましょう

実はUnreal Engineはこの状態でマルチプレイすることが可能です。マジすごい。


「Active Play Mode」から

"Number Of Players" を2に変更します。プレイヤーの数ですね。

"Net Mode"を Play As Listen Serverに変更します。

※Play As Clientに最終的にはするんですが、開発中はこっちの方がわかりやすい。

そして最後に"New Editor Window"を押してプレイします。


※実際にはウィンドウが重なっていると思うので、マウスでずらしてください

こんな感じで簡単にマルチプレイが実装できました。前回のあの小難しい説明は一体なんだったんや!という感じの簡単さですね。

ちなみにどちらの画面のキャラも操作が可能です。

【TIPS】実行中にもう一方のWindowに移るために、マウスを有効にしたい場合は Shift+F1キー (+fnキー) でマウスが有効になります。

ちゃんとキャラクターが前を通ると、一方のキャラクターの画面でも反映されているのがわかりますね。


さて、先ほどの実行中ウィンドウをよく見るとこんな表示がされています。

NetMode:Server と Client 1の表示があるのがわかりますか?

既に前回の記事をご覧になった方はわかると思いますが、これがサーバーとクライアントですね。

ここではListenサーバー形式を採用しているので、Serverとなっているアカウントがプレイヤーにもなっています。

ここではアカウント数は2にしましたが、最低3で実行するようにしましょう。理由は、クライアント間でちゃんとアクターなどが共有できているかを確認するためです。


雪玉を作ってみる

まずはコンテンツウィンドウで、右クリックしてブループリントを作ってみます。名前はBP_SnowBallとでもしておきましょう。

このブループリントを編集して、Add ComponentでSphereを追加して適当な大きさに調整します。あと一時的にPhysicsを有効にしておきましょう。



弾を打てるようにならないといけないので、ThirdPersonCharacterを開きます。


こんな感じで、H のキーを押すと、プレイヤーの場所に弾をスポーンするようにしました。

弾を前に打ち出すような動きは入れてませんが、まあいいでしょう。

早速この状態でプレイしてみます。


はい、予定調和ではありますがこの通り片方には弾が表示され、片方には表示されません。

前回の記事を見たみなさんは、この状態がなぜ起こっているかわかりますよね?

そう、Replicateがオンになっていないからです。BP_SnowBallに戻って、Replicateをオンにしましょう。

こんな感じで変更してみます。
ちなみにReplication Movementは物体の動きまでReplicateする機能です。

ではまずServer側でプレイします。

挙動はClientとServerで完全には一致していませんが、弾の表示はどちらでもされました。

次にClient側でプレイすると、

弾がServer側に表示されない!!!!

はい。前回の記事を見て貰えばわかりますが、これも当たり前。

ReplicateはServer側からClient側に一方的に世界をコピーする機能なので、Client側からはServer側にコピーされません。

ではどうすんの?って話なんですが

予習した通り、RPCを使います。進研ゼミでやったところだ!現象

ThirdPersonCharacterに戻って、カスタムイベントを作成します。

イベントを作成したら、このイベントのプロパティを変更して"Run On Server(サーバーで実行)"を選択します。Reliability(信頼性)はオフでいいです。

繋ぎかえて、

H keyのノードに、先ほど作ったカスタムイベントのCall Functionを呼び出します。

これだけ。あの小難しい説明はなんだったんや(2回目)


え?これだけ?

そう。これだけなんですよマジで。これだけ気をつければ、オンラインゲームはできちゃいます。

ただ、実際にやってもらうと分かりますがServerとClient側で動きにズレが生じているのがわかると思います。

このように、ローカルPC内でマルチプレイするだけでもズレが生じているので、オンラインでやったら難しいだろうことは誰もが体感できると思います。

これを解決していくのがマルチプレイゲーム制作の難しさになります。


ズレの解消

ズレの原因は、サーバー側で生成した雪玉の座標ポイントと、クライアント側で生成した雪玉の座標ポイントが異なっているからですね。

なので、座標はクライアント側、雪玉の生成はサーバー側ですれば問題は解決します。

クライアント側で雪玉生成の座標を取得し、サーバー側でその座標を受け取り、雪玉を生成します。

Client側で雪玉の座標を取得、RPC Run On Serverを使ってサーバー側で雪玉生成。

まあ本来はMoveFragとか使ってスマートに解決するべきなんでしょうけどね…。よくわかんないし。

※上の画像はSwitch Has Authorityを使ってましたが、今考えるとIs Locally Controlledノードで良かったような気がします。


アニメーションブループリントのReplicateについて

ちょっとだけアニメーションブループリントでは気をつけるポイントがあります。

アニメーションブループリントは、Boolean変数の状態で、アニメーションを分岐させることをよくやります。

例えばIsInAir?変数がTrueなら飛んでるアニメーションにして、Falseなら地上のアニメーションにする、などです。

この状態を表す変数はアニメーションブループリント内で宣言するのが普通です。

ところが、この状態変数をReplicatedにしてもうまくいかんのです。変数のReplicateはあくまでアクター単位で行われているようなのです。(間違っていたら誰か教えてください)

なので、

こんな感じにThirdPersonCharacterにRPC : Run On Serverを実装して、ThirdPersonCharacter内で宣言したThrowAvailable変数をオンオフします。
当然ThrowAvailable変数はReplicateをオンにしてくださいね。

んで、

アニメーションブループリント内で、Castして、この変数をアニメーションブループリント内の変数に入れます。このアニメーションブループリント内の変数を使って、アニメーションの状態を書き換えるわけですね。
※余談ですが実際のリリース時には、H keyみたいにキーを直接指定するのではなく、コントローラーから設定してくださいね。

次回、いよいよ本格的に制作スタート

次回はGASを使ったマルチプレイヤーゲーム制作について、解説していきいます。いよいよ骨太な記事になってきます。『覚悟』はできてるか?俺はできてない。

次回 GASでオンラインゲーム 第一回