前回に引き続き、UnrealQuest1の課題の続きです。
1日目初級の残り、当たると死ぬ棘を作りました。
今回の成果
・MultiplayにおけるUI操作
・PlayerStateの使用
・Respawn処理
👇公式の解説動画。当たると死ぬ棘のところ。
当たると死ぬ棘
BP_Needle

BP_ThirdPersonGameMode
EventGraph

PlayerStatusという名前で構造体を作成。
GameModeでは初期値を配列で作成し、PlayerStateに代入している。

MyPlayerController
CreateWidgetの前のBranchにより、Controllerを所有しているプレイヤーのみ処理を実行できる。
これを使わないと、Serverに同じWidgetが複数作成されてしまう。
ここではOnOwingClientのカスタムイベントと置き換え可能。ただし意味は少し違う。
RefleshWidgetのカスタムイベントを先ほどのBranchと置き換えることはできない。
RespawnPawnのイベントがRunOnServerであるので、そもそもServer以外の処理は実行されていないからである。

BP_ThirdPersonCharacter
EventGraph
Healthの変数
Healthの変数をPlayerStateとは別にキャラクターに作成した。
ダメージを受けてからUIに反映させるまでに時間がなく、
PlayerStateのHealthを直接使うとClientのUI反映に失敗したため。
PlayerState
BeginPlayされるタイミングが、PlayerStateが設定されるタイミングより早いようだ。
そのため、BeginPlayでキャストすると失敗する。
PlayerStateの使い方に困っていたが、これが原因だったようだ。
Delayを使って、無理やりタイミングをずらした。
ログイン
Clientの数が増えると、その増えたClientにおいて他のキャラクターが見えない現象がおきた。(たぶんBeginPlayが実行されていない。)
Clientのログインの遅れが問題だった。Delayで無理やりBeginPlayのタイミングをずらすことで一応解決。
死亡時の処理
OnOwingClientで死亡判定→死ぬイベント(Multicast)だと、Clientの死亡判定が他のプレイヤーに伝わらずラグドールにならない。
RunOnServerで死亡判定→死ぬイベント(Multicast)にすれば全プレイヤー視点で死んだPawnはラグドールになる。
HealthBarの更新は、頭上のものはMulticastで全プレイヤーに伝える。
WB_HealthBarの更新はDispatcherにより、そのキャラクターが紐づいているWidgetのみ更新される。
Respawn
今回はRestartPlayerを使わずに、CharacterとControllerに処理を書いている。あらかじめ用意されているRestartPlayerのようなノードは便利だが、中身を理解していないとカスタマイズするのが難しい。今回RestartPlayerを使うと少し不都合があったので自分で処理を作成した。
👇Blueprint

👇関数Change Color Random

👇RestartPlayerを使った場合の例

HealthBarWidget
頭上にWidgetComponentを追加した。
SpaceをScreen、Draw at Desired SizeをONにしてある。

下記のBPで、自分のプレイヤーが操作しているPawnの時のみ頭上のHealthBarを消した。
Pawnを所有しているプレイヤーと、所有していないプレイヤーで処理を分けることができる。

Get Controllerは、そのポーンを操作しているControllerをとってくる。
Get Player Controllerは、そのプレイヤーが操作しているControllerをとってくる。
自分のプレイヤーがそのPawnを操作しているときのみTrueになる。
WB_Player
Designer

Graph

👇RespawnしてもWidgetは消えないので、Bindされたカスタムイベントは実行される。ただし、Characterは作り直されるので、Characterの変数は代入しなおす必要がある。

知識・考察
PlayerStateの設定や、Clientのログインを待つのにDelayを使った。
出来れば関数によって制御したい。
アクターを生成させたプレイヤー(server,clientなど)が持つらしい。
基本的にServerが所有権を持つらしい。
PlayerState,PlayerController,PlayerPawnは例外として各プレイヤーが所有権を持つらしい。
正直、まだよくわかってないみたい。

👇この記事で、クライアントの所有権について少し説明してくれている。
所有権というのは、PawnやPlayerControllerなどに対して、プレイヤーが持つものだと今のところ理解しておく。それでAuthorityとは別物。
いろいろ調べても、今の僕では理解できないのでこのくらいにしておく。
RPC=Remote Procedure Call
ローカルで呼び出される関数。
Replicateされた関数。
Sever,Client,Multicastの3つを持つ。
Procedure=手順、手続き。
プロシージャ
戻り値のない関数

Dispatcherの使いどころがいまいちつかめないので少しまとめる。
他のBPでイベントを実行する場合を考える。
ここではCallする方を呼び出し元、イベントを実行する方を呼び出し先と呼ぶこととする。
カスタムイベント
呼び出し元が呼び出し先の参照をとる。1度のCallで1つの参照先に対してイベントを実行。
Dispatcher
呼び出し先が呼び出し元の参照をとりバインドして使用する。Dispatcher自体は呼び出し元に作成する。呼び出し元を複数のクラスが参照してバインドしていれば、1度のCallで複数のクラスでイベントを実行することが可能。多分。

考察
今回、WB_PlayerにおいてRespawn時にDispatcherを使用した。
BP_ThirdPersonCharacterが呼び出し元、WB_Playerが呼び出し先である。
Respawn時に古いPawnは消えるので、そのタイミングでWB_Playerで新しく生まれたPawnの参照をとってきて設定した。
このときの参照をとってくる方向の違いより、処理の実装のしやすさは変わると思う。
また、あらかじめBindしているということで実行の処理速度などにも影響はあるのかと推測。準備してるし、Dispatcherの方がちょっと速そう。
マクロ。
ServerはAuthority、ClientはRemote。
Serverが動かしているPawnでも、Clientが動かしているPawnでもAuthorityはServer。


UEはServer主導型だから、常にServerはGameStateに対する権限を持つとのこと。Authorityと所有権は別物。
参考
👇神。
👇すごく分かりやすい。

👇かなり参考にした動画
👇解説動画内で紹介されてたやつ。

Twitterしてます
ブログの更新をお知らせ
コメント