70.9K Views
July 04, 17
スライド概要
2017年7月2日に行われたライセンシー様向けMO勉強会の資料です。実践編 @ (登壇者: Byking 鈴木孝司さま)
Unreal Engineを開発・提供しているエピック ゲームズ ジャパンによる公式アカウントです。 勉強会や配信などで行った講演資料を公開しています。 公式サイトはこちら https://www.unrealengine.com/ja/
バイキングとスピーカーについて • http://byking.jp/ • 対戦型のマルチプレイアクションタイプのゲームが得意 • 近年はUE4を採用することが多いです • 本日のスピーカー • 株式会社バイキング CTO 鈴木孝司 • レンダリング・ネットワーク・サーバー・ゲームプレイ・I/O・最適化・デバッ グ・・・
はじめに • サンプルプロジェクトの作成及び確認はUE4.16を用いています • DedicatedServerの運用はUE4.12.5で行っています • 大変申し訳無いのですが、スライド中の情報は不正確だったり 場合によっては誤っている事もあると思います • ツッコミがあったら共有お願いします! • 自分が見つけていない、より良い制御方法などもあるかと思い ますが、現状鈴木が把握している内容で紹介させて頂きます • 明確なドキュメントが不足している気がします・・・
お題目 • レプリケート • 遅延 • RepNotify • RPC • ReliableとUnreliable • RPCと帯域 • 帯域幅設定 • レプリケートとRPCの使い分け • DedicatedServer • • • • ListenServerとの違い Cosmetic 遷移フロー例 負荷軽減
Replicate
レプリケート • サーバー上にあるアクターをクライアント上で再現するための情報伝達 • サーバー上で生まれた、AActor::bReplicatesがtrueのアクターが対象 • そのアクターのうちReplicate対象と指定されているプロパティのみが対象 • UPROPERTY(Replicated) or UPROPERTY(replicatedUsing=xxx) • 送信されるのは変更された部分のみ • デフォルトで対象になっているアクター • GameState、PlayerController、PlayerState、Pawn • 逆にレプリケートされないアクター • GameMode、AIController、Hud関連、GameInstanceなど • 送信間隔は、帯域幅やAActor::NetPriority、Aactor::NetUpdateFrequencyなどの 影響を踏まえて制御される • うまく扱えば与えられたネットワーク帯域を効率よく利用したコードが書けます • 遅延することはあれど、サーバー上のプロパティはいつか必ず同期される。
レプリケート遅延 十分な帯域があれば、すべてのアク ターが送信できます ActorE ActorE ActorD ActorD ActorC ActorC ActorB ActorB ActorA ActorA
レプリケート遅延 ActorH 帯域が要求に対して不十分な場合プラ イオリティ付けされたリストの先頭か ら指定帯域まで送信されます ActorG 優 先 順 位 ActorF ActorF ActorE ActorE ActorD ActorD ActorC ActorC ActorB ActorB ActorA ActorA
プライオリティについて補足 • プライオリティ値 • おおざっぱにいって 「前回送信してからの時間」* 「ActorNetPriority」 • 前ページのようにそのTickで送信できなかったアクターは時間が伸びてすこしず つプライオリティが上がります。 • つまり、帯域が飽和している状態続いた場合、 NetPriorityの設定によって送信間隔がかなり変わる可能性があります • 参考ソースコード • AActor::GetNetPriority • オーバーライド可能なのでタイトルに合わせて自由に実装することも可能 • FActorPriority::FActorPriority
レプリケート遅延 ActorE ActorD FatActorB 変化していない プライオリティが高くても 変化してなければノーカン ※ただしプロパティ 検査コストはかかる ActorE ActorD FatActorA FatActorA
NetPriorityデモ • 帯域飽和状態 • NetUpdateFrequencyは100固定 • 上画面がサーバーで下画面がクライアント • 「すべてのキャラクターがNetPriority=100」と 「ステージ左側のキャラクターだけNetPriority=10000」を 切り替える
NetPriorityデモ YouTube Link
NetPriorityデモ • 同じアクターでもNetPriorityの違いによってレプリケートの挙 動に差が出る • ただし、NetPriorityの制御が行われるのは帯域が飽和している 時のみ • 帯域が十分であればすべてのアクターがレプリケートされる
レプリケートまとめ • レプリケート帯域が十分にあればNetUpdateFrequencyの頻度で、 クライアントにパラメータが送信されます • 帯域が飽和するとアクターの更新間隔が悪化していく • プロパティ量や頻度について適切にコントロールする • NetPriorityは帯域が飽和しているときだけ有効な事に注意する
RepNotify
ReplicatedUsing(RepNotify) • 「クライアント上で」レプリケーションによってパラメータが変 更された時に 指定した関数を呼び出す • 変化しなかった時は呼び出されない • クライアントと同じようにサーバーでも同じ関数が呼び出された ほうが便利 • ネイティブコードでプロパティを書き換えただけの場合サーバー上では 呼び出されない • ブループリントは「あるノード」がそれを処理している 0 1 0 1 Notify呼び出し
サーバー上のRepNotifyコールバック このノードが RepNotifyを呼び出す (※値が変化しなくても) 呼び出されない どちらもクライアント側では 動作します
NativeコードでサーバーRepNotify Setterを用意して ローカルでRepNotify関数を呼び出す ようにすると良いです
RepNotifyが 呼ばれないことがある・・・?
RepNotifyの仕組み • 値が変化したときに呼び出し • 値はレプリケートによって書き換えられる • レプリケートの間隔は設定や帯域依存
値を素早く切り替えると RepNotifyイベントが起きない事がある。 0 1 0 0 1 0 1 1 0 0
連続的に変化するプロパティの RepNotify消失 正常 0 1 0 2 1 2 消失 0 1 0 2 2
連続的に変化するプロパティの RepNotifyによるレアバグ • 消失する可能性のあるRepNotifyコールバックに、クリティカル なコード (インスタンスの生成やアセットのプリローディング) などが含まれているとバグを生む • 更新間隔が長いだけであれば、 AActor::ForceNetUpdate()を呼び 出して素早くレプリケート情報を送信することで回避・軽減で きるかも • 帯域的には無駄になるが、プロパティをレプリケートしつつ ReliableMulticastRPCでさらに値を送信することも可能
RPC
RPCについて 公式ドキュメントより
ServerRPC クライアントからサーバーになにかをお願いする時に呼び出す サーバーに何かを伝える場合はこれしかありません。 お小遣い頂戴
ServerRPCを呼び出せるアクター • クライアント側 • RoleがAutonomousProxy • AutonomousProxyに設定されるのは、 PlayerControllerとそこにPossessしているPawn • 加えて、上記アクターが保持しているコンポーネントでも呼び出し可能 • サーバー側 • すべてのアクターが呼び出し可能でローカルで呼び出しが完結する
ServerRPCが正しく動作しない例 • RoleがSimurateProxyに設定されているアクターが呼び出した場 合、すべてドロップ •例 • クライアント側のGameStateに実装されたServerRPC • サーバープロセス上だけで動作するので意味がないです。 • 自分が操作していないPawnのServerRPC • 特に警告などもないので慣れない内はハマりやすい
ClientRPC 特定のクライアントだけに メッセージを送る際に使う 今月は赤字
何に使うのか • 主な用途はServerRPCに対する返答 • クライアント「ABC弾をターゲット(x,y,z)に向かって発射!」 • サーバー「あなたは今動作出来ない状態なので弾が出せません!」 • クライアントからの要求を受けたが、ワールドに対して影響を与える必要が無く、 リクエスト送信者に対してだけなにかを返答を行いたいときに使います。
ClientRPCを呼び出せるアクター • RemoteRoleがAutonomousProxyのアクター • PlayerControllerおよび操作対象のPawn • ServerRPCに対して対になります
MulticastRPC サーバーが自分を含む 全員に同じメッセージを送信 ごはんよー ごはんよー ごはんよー ごはんよー
Reliable と Unreliable
Reliable • 特徴 • 呼び出した回数分「必ず」送信される • パケロス時の再送制御付き • パケットのコピーが送信者のプロセス上に保 存される
ReliableRPCを呼びすぎると どうなるのか • 受信確認が届かずに再送用のバッファが溢れた場合 ReliableBufferOverflowという警告と共に、即座に接続が切断され る
Unreliable • 特徴 • マルチキャストとそれ以外で挙動がちょっと違う • ClientとServer • 呼び出し回数分リクエストが送信される • 再送制御が無いので、パケットロストした場合は失われる • Multicast • これだけ挙動が違っていて、RPCリクエストは一旦バッファに蓄積されて、 アクターのレプリケート時に一緒に送信される • つまり送信が遅延する • バッファに蓄積された「同じRPC」が一定数を超えた場合、 送信すらされずに破棄される • 送信回数はパケロスが無かったとしても、保証されません
RPCと帯域
RPCは優先的に帯域を使う RPC先輩が使った帯域の余りを レプリケートに割り当てます ActorC ActorB ActorA ActorA RPC4 RPC4 RPC1 RPC3 RPC1 RPC3 RPC2 RPC2 RPC1 RPC1
帯域を超えるRPC呼び出し RPC3 RPC3 帯域設定を超えて送信される RPC2 RPC1 RPC2 RPC1 大きな構造体や配列を引数に使う とかんたんに飽和するので注意 ※10K / 60fpsだと1tickあたり 166byte程度しか無い
さらにもっと呼び出すと 帯域設定を超えて送信される!
エンジンは設定された帯域に 合わせようと頑張るので・・・
こうなります・・・ 大量のRPC レプリケートが しばらく沈黙 レプリケーションに多大な悪影響!
RPC レプリケートは なんか遅いしね RPCでしょ 全部Reliableで いいね! RPC 通信帯域が 飽和すると・・ レプリケートが 動作しない! RPCにしていくよ レプリケートは 信用できない! 何時くるの?!
といっても、 タイトルによっては デフォルトの帯域制限値ではどうしても不足することもある
枠を増やすしかない!
帯域幅設定 ***Engine.ini の ConfiguredInternetSpeed 各クライアントに対して 12345 byte/secの制限 12K * 4 client = 49KByte/sec 現在の帯域幅値表示 Displayall Player CurrentNetSpeed
ConfiguredInternetSpeed 以外の帯域幅設定方法 • void APlayerController::SetNetSpeed(int32 NewSpeed); • プレイヤーコントローラー毎に個別設定が可能 • ネイティブコードからアクセスする • Max(Internet)ClinetRateで制約される • お手軽で思い通りに操作できるのでオススメ • AGameNetworkManagerを使う • Ini設定などがあるため動きそうな気がする • 試しに設定してもデフォルトでは ゲームに影響を与えない模様。 • これもネイティブコードを触る必要がありそう • 未検証。すいません • 帯域設定は サーバーからクライアント向けの設 定のみ • クライアントからはRPC送信になるので帯域制限は ない
帯域制御デモ • SetNetSpeedを呼び出して、リアルタイムに割当帯域を変える • ネイティブコードからしかアクセスできないので、BP版を作成 した。
帯域制御デモ YouTube Link
技術要件やハードの制約などと相談して 適切な値を設定して下さい
RPCでアクタのステートを 変更する時の注意点 サーバー クライアントA レプリケート RPC(アクタAの顔色を青にして) クライアントB レプリケート あとから接続して来たクライ アントに情報が行き渡らない
RPCについてまとめ • 即座送信や再送制御は便利 とても頼りがいがある • 呼び出し回数については注意が必要 UnreliableMulticastRPCは間引かれたり遅延したりする Reliableは回数保証 • 引数のデータサイズに気を付ける FHitResultとかを引数にいれてはいけません • 大量に呼び出すと 通信帯域を食いつぶしてレプリケーションが断続的に動作しなくなる事がある せっかくNetPriorityやFrequencyを設定してもすべて無意味に • ターゲットに合わせて通信帯域を設定
MulticastRPCと レプリケートの使い分け
プロパティの変化を伝えるだけなら RPCでもレプリケートでも同じことが出来る
それぞれの特徴 • ReliableMulticastRPC • • • • • とても重要でサーバー側が呼び出した回数分処理される必要があるもの ロストしない 即時送信 大量に呼び出されるとレプリケート帯域が悪化するので注意する 信頼性が高い • UnreliableMulticastRPC • • • 最悪メッセージがロストする 遅延する 連続送信してしまったときに間引かれれる • • 大量送信に対する保険 帯域は消費する • UnreliableClientRPC • 即時送信&ロストを許容 • Replicate+ClientRPCで値の変化を約束しつつ、即時性を与えるという組み合わせも考えられる • Replicate • • プロパティを書き換える事が必要 いつか必ず同期する • • 遅延しても良い情報はReliableMulticastRPCではなくReplicateを使うべき 送信間隔による変化のロストや遅延が考えられる
わかりにくいのでフローチャート化
YES! ReliableMulticastRPC YES! ロスト不可 NO! UnreliableMulticastRPC プロパティを使えない イベント同報処理 NO!
YES! ReliableMulticastRPC YES! ロスト不可 NO! UnreliableClientRPC or ReliableMulticastRPC どんな時でも 遅延は許せない YES! Replicate NO! ロスト不可 NO! UnreliableMulticastRPC or Replicate
• やはりReliableMulticastRPCは便利で使いたくなる • 帯域が逼迫していなければレプリケートはほとんど遅延しない • 帯域に余裕をもたせることがとても重要!
SpawnActorを最速で レプリケートするトリック
SpawnActorの遅延 • SpawnActorで生成されたアクターはレプリケーションでクライ アントに伝達される • 空き帯域が十分でない状態でアクタを生成すると、 リモートクライアント上でのアクターの生成が遅延する。 利用可能帯域によっては生成されないことすらある。 • クライアント上の生成位置のズレなどが生まれる • 移動の軌跡を描きたいといった場合に問題が生ずる可能性がある • 初期座標などをレプリケートしてクライアント側で対処する • アクター生成をできるだけ早く処理するトリックを紹介
最速アクター生成デモ • ボタンに反応してプレイヤーキャラクターが2つの弾を同時に 発射する • 茶色の玉はアクターを生成しただけ • 白い玉はアクターを生成し、そのアクターに対してRPCを呼ぶ • 動画前半は帯域制限なし、動画後半は帯域制限有り • 左上がListenServerで下側の2つがClientです • 帯域制限があるときに生成される玉はクライアントごとに違う
Actor生成の後RPCを呼び出す RPC無し 茶玉 RPC有り 白玉
動画 YouTube Link
DedicatedServer
DedicatedServerについて • Listenとの違い • コスト • Cosmetic • Nativeコードでの注意 • DedicatedServerの遷移フロー • パフォーマンス制御
ListenServerとの違い • 低コスト • RPCやReplicateの仕組みを使っていればネットコード自体は基本同じ • DedicatedServerはviewportが無い • ローカルのカメラも無い • AController::IsLocalController() == true のPlayerControllerが無い • グローバルIPを割り当てることができる • NAT越え不要 • 大半のプレイヤーがトラブルなく接続できる • データセンターは基幹回線網に接続されているのでpingも良い • 自律的に動く • コンソールコマンドなどで直接操作はできない • クライアントからRPCを送信して操作する • 外部のゲームサーバー管理サーバーと通信して操作する
ハードウェア資源コスト Client DedicatedServer CPU資源 あるだけ使う (250%以上) 大量のスレッド 1コアを100%として ロビー 5% バトル中 60% MaxTickRate : 60 ※Xeon E5-2698 v3 @ 2.30GHz メモリ 4GB+ 仮想メモリ(VIRT) 1.5GB 物理メモリ(RES) 0.8GB GPU 必要 不要 安価なクラウドサーバーで動作可能 軽量なゲームであれば、1コアに2プロセス以上も可能
Cosmetic • DedicatedServer上で動くべきではないブループリントノードは Cosmetic指定が適用されている • UIやサウンドなど • これらのノードはDedicatedServerでは処理がスキップされます
NativeコードでCosmetic関数 • NativeコードにはCosmeticの様な自動的にスキップ処理をおこな うような機構は無い • それぞれ IsNetMode(NM_DedicatedServer) などを用いて適切に処理する • BPコードをNativeコードに移した時にnullアクセスバグが起こりやすい • BPはnullアクセスがあっても警告のみで継続して動作する
デディケートサーバーの 遷移フロー例
LEVEL:ロビー マッチング LEVEL:ロビー DedicatedServerサーバーに接続 プレイヤーを待つ すべてのプレイヤーが揃って、 全員の必要なアセットの読み込みが終わった ServerTravel(バトルレベル) LEVEL:バトルレベル LEVEL:バトルレベル バトル サーバーから退出 Open(マッチング) バトルが終わったら全員いなくなるのを待つ ServerTravel(ロビー)
ServerTravel時の注意 • 移動前に必要なアセットを予め読み込んでおく • トラベル時にブロック読み込みが長時間発生するとタイムアウトが発 生する可能性があるため • ちなみに最初にクライアントがサーバーに接続する際も 最低でもタイムアウトしない程度にアセットをプリロードする必要があります。 • 接続エラーや切断 • 問題が発生した場合はGameDefaultMapへ移動する • GameDefaultMapは UGameMapsSettings::SetGameDefaultMap(FString&); でオーバーライドできる
デプロイとテスト バージョン違いによって、接続できるが正しく実行できない現象 が起こる。 ソースコントロールのリビジョン番号を埋め込むなどで早い段階 でバージョンの相違が検出できるようにしておいたほうが良い。
サーバーのパフォーマンス制御
アクタやコンポーネントのTick省略 FTickFunction BlueprintはActor単位で
パケット数制御 • 通信負荷軽減の為にPacketの数を制御したい • Packetの送信数はサーバーのTick回数×クライアント数 • IniのNetServerMaxTickRateで最大Tick回数を制限する • NetClientTicksPerSecond • 1秒間に処理するクライアント数の制限値 • ListenServerならデフォルトで有効 • Dedicatedserverで適用したい場合は、コマンドラインオプション limitclientticks を つける • MagiciansDeadでは使っていません • あまりに低い値を設定しても効果が無い • ACKパケットだけが入ったパケットが送信されるためパケット数が余り減らない。謎
レプリケーション負荷制御 • サーバーはTick毎に、レプリケート対象のアクターのプロパティを検査して変更を検出 する • アクターやレプリケート対象のプロパティが多いと、この検査コストが処理負荷として大きな比 重を占めることがある • アクター毎に適切な NetUpdateFrequency を設定する • 重要性が低いものは更新間隔を低めにしておく • 必要あればランタイム中にNetUpdateFrequencyを手動で変更してもよい • 更新したいタイミングが明示的であれば、更新間隔自体を長めに設定しておきつつ、 値のセットと同時にAActor::ForceNetUpdate()をつかってすぐさま送信しても良い • Dormancyという機能もあるらしい • 更新間隔の自動制御 • net.UseAdaptiveNetUpdateFrequency(UE4.16ではデフォルトで有効) • 更新間隔の実績値を基準として次の更新予定を設定する • 値の範囲 • AActor::MinNetUpdateFrequency(下限) ~ AActor::NetUpdateFrequency(上限)
まとめ
まとめ • RPCの使いすぎは危ない • メリットとデメリットを把握してレプリケートで十分な所はレプリ ケートを用いる • 帯域が飽和しなければレプリケートは迅速に行われることを胸に刻む • 帯域を適切に設定して効率よく使う • NetUpdateFrequencyとNetPriorityを適切にコントロールする
謝辞&参考資料 • イラスト屋様 • バイキングスタッフ • CedricさんのUE Network解説ドキュメント http://cedricneukirchen.net/Downloads/Compendium/UE4_Network_Compendiu m_by_Cedric_eXi_Neukirchen.pdf • 公式ドキュメント • UE4 Auswerhub
開発者募集中! 世界にどデカイ爪痕を残さないか!? 勤務地 東京(東新宿) 勤務時間 10:00~18:30 (昼休み13:00~14:00) 給与 固定給制月給 ※経験・能力を考慮の上、決定。 例 プログラマー 業界歴11年 月給45万円 プログラマー 業界未経験 月給22万円 休日・休暇 完全週休2日制(土・日) 祝日 年末年始 有給休暇 結婚記念日 家族の誕生日 ※選考希望の方は弊社HPまでご連絡ください。 http://byking.jp/recruit/
ご清聴ありがとうございました!
おまけ P2Pキー同期の通信モデル
P2Pキー同期モデル キャラクターはレプリケートOFF ListenServer PlayerController1 (Authority) キーバッファ1P 1Pキャラ キーバッファ2P 2Pキャラ 物理コントローラー Unreliable ClientRPC Client PlayerController2 (Automonious) 物理コントローラー Unreliable ServerRPC キーバッファ1P 1Pキャラ キーバッファ2P 2Pキャラ
• P2Pであれば1HOPで通信できてお手軽 • キーデータは複数フレーム分を束ねて送る • Reliableだと再送が起きた時にブロックするのでUnreliableが良い
3人以上のルームを作りたい場合は • サーバー経由(2HOP)のメッセージのやり取りが必要になる • メッシュ型ネットワークが構成できると良いがむずかしい • やっぱりUE4を頼らずに独自実装にならざるを得ない