前提条件のトピック:トリプル侵入ゲーム5Verseでプレイヤーを不可視にする
Infiltrator にちらつくビジュアル エフェクトを適用するには、各プレイヤーのキャラクターの表示と非表示を繰り返し切り替えます。 この処理は Infiltrator がダメージを受けた際に関数を通じて行いますが、この関数が呼び出された際に、残りのコードが引き続き実行されることも確認する必要があります。 この処理は、複数の Infiltrators が存在する場合にはより複雑になります。 ゲーム中に複数の Infiltrator が同時にダメージを受ける可能性もあるため、それぞれを個別に処理することのできるコードが必要になります。
こうしたコードを構築するには、spawn 式を多用する必要があります。関数をスポーンすることで、残りのコードを停止させることなく非同期でその関数を実行できます。それぞれの Infiltrator で関数をスポーンすることで、各 Infiltrator のちらつきを他のものから独立した形で実行することができます。
以下の手順では、ダメージを受けた際にそれぞれの Infiltrator のキャラクターをちらつかせる方法を示します。
ちらつき (Flicker) のループを作成する
- 新しい
FlickerCharacter()関数をinvisibility_managerクラス定義に追加します。この関数はfort_characterを受け取り、対象となるキャラクターの表示と非表示を切り替えてちらつかせます。非同期で実行させるために、この関数に<suspends>指定子を加えます。# fort_character の非表示と表示を繰り返し、エージェントの表示状態をちらつかせます FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("FlickerCharacter() invoked") FlickerCharacter()内でHide()をInCharacterでループし、Sleep()を使って特定の期間中 (以前にFlickerRateSecondsで定義した期間) スリープにして、その後Show()で表示して、再びスリープの状態にします。こうすることで、Infiltrator キャラクターにちらつきのエフェクトが適用され、敵のプレイヤーが Infiltrators をトラックできるようになりますが、それでも不可視の状態が多少残るため、狙いを定めることは容易ではありません。# fort_character の非表示と表示を繰り返し、エージェントの表示状態をちらつかせます FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("FlickerCharacter() invoked") # キャラクターの非表示と表示をループして、ちらつきエフェクトを作成します。 loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds)ループを解除する
キャラクターのちらつきを停止するために、このループ関数を解除する方法が必要になります。以前に設定した
PlayerVisibilitySecondsマップはプレイヤーのちらつきの残り時間をトラックするもので、それぞれのループ中にこの残り時間を減らしていく必要があります。残り時間が 0 になったらプレイヤーのちらつきを停止して、ループを解除できます。InCharacter.GetAgent[]をキーとして使用してPlayerVisibilitySecondsマップにアクセスし、それをTimeRemaining変数に格納することで、プレイヤーのちらつきの残り時間を取得します。FlickerRateSeconds * 2によって残り時間の値を減少させることで、マップ内の同じ式で残り時間を設定することも可能です。こうすることで、set式の解決後にTimeRemainingがPlayerVisibilitySecondsの値になります。ループごとにSleep()を 2 回呼び出すため、FlickerRateSecondsは 2 の乗算値である必要があります。# fort_character の非表示と表示を繰り返し、エージェントの表示状態をちらつかせます FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("FlickerCharacter() invoked") # キャラクターの非表示と表示をループして、ちらつきエフェクトを作成します。 loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds) # 各ループで、キャラクターがちらつく時間を FlickerRateSeconds の値だけ減少させます。 #残り時間が 0 になった場合、ループから抜け出します。 if: TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2TimeRemainingが 0 以下の値であるかどうかをチェックして、そうであればキャラクターのちらつきを停止します。ちらつきを停止するには、キャラクターでHide()を呼び出して再び不可視の状態にして、breakでループを解除します。FlickerCharacter()関数は次のようになるはずです。# fort_character の非表示と表示を繰り返し、エージェントの表示状態をちらつかせます FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("FlickerCharacter() invoked") # キャラクターの非表示と表示をループして、ちらつきエフェクトを作成します。 loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds) # 各ループで、キャラクターがちらつく時間を FlickerRateSeconds の値だけ減少させます。 #残り時間が 0 になった場合、ループから抜け出します。 if: TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2 TimeRemaining <= 0.0 then: InCharacter.Hide() break
ちらつきを開始してリセットする
すでにちらついて表示されている Infiltrator がさらにダメージを受けたときの動作を検討します。この場合は別の FlickerCharacter() 関数がスポーンされますが、ダメージを受け続けるたびに別の関数がスポーンされて、同じキャラクター上でいくつもの関数が処理されることになるため、そのうちに制御不能に陥る可能性があります。これを避けるために、ダメージを受けた際に PlayerVisibilitySeconds のプレイヤーの値をリセットする必要があります。リセットするには、まずプレイヤーが現在ちらついているかどうかをチェックする関数を定義します。すでにちらついている場合は、ちらつきの継続期間をリセットします。ちらついていない場合は、そのキャラクター用に新しいちらつきイベントをスポーンします。
- 新しい
IsFlickering()ヘルパー関数をinvisibility_managerクラス定義に追加します。この関数を使って、プレイヤーがちらついて表示されているかどうかを判断します。この関数はエージェントを引数として受け取り、PlayerVisibilitySecondsのその値が0.0よりも大きい場合はtrueを返します。この関数にdecides指定子とtransacts指定子を追加します。この両方によってこれが「失敗する可能性がある」関数となり、失敗した際にロールバックできるようになります。# プレイヤーにちらつきの時間が残っているかどうかを返します IsFlickering(InAgent:agent)<decides><transacts>:void= PlayerVisibilitySeconds[InAgent] > 0.0 - 新しい
StartOrResetFlickering()関数をinvisibility_managerクラス定義に追加します。この関数はエージェントを引数として受け取り、プレイヤーのちらつきを開始すべきか、リセットすべきかを判断します。# エージェントが非表示の場合は新しいちらつきイベントを開始し、それ以外の # 場合は、エージェントの進行中のちらつきをリセットします。 StartOrResetFlickering(InAgent:agent):void= StartOrResetFlickering()で、この特定のエージェントがちらついて いない かどうかをチェックします。ちらついていない場合は、このエージェント用に新たなちらつきイベントを開始する必要があります。そのエージェントのfort_characterを取得して、それをFortCharacter変数に保存します。# エージェントが非表示の場合は新しいちらつきイベントを開始し、それ以外の # 場合は、エージェントの進行中のちらつきをリセットします。 StartOrResetFlickering(InAgent:agent):void= if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Attempting to start NEW FlickerEvent for this character")PlayerVisibilitySecondsのエージェントの値をVulnerableSecondsに設定し、spawnを使ってそのエージェント用に新しいFlickerCharacter()関数をスポーンして、そのFortCharacterを渡します。if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Attempting to start NEW FlickerEvent for this character") # 新しいちらつきが開始しました if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): spawn{FlickerCharacter(FortCharacter)} Logger.Print("Spawned a FlickerEvent for this character")- エージェントがすでにちらついている場合は、
PlayerVisibilitySecondsでその値をVulnerableSecondsにリセットするだけです。 前述のFlickerCharacter()関数ではこの値を非同期で読み取るため、この値がFlickerCharacter()のループ中にリセットされた場合は、breakで解除されないままループが続くことに注意してください。StartOrResetFlickering()関数は次のようになるはずです。# エージェントが非表示の場合は新しいちらつきイベントを開始し、それ以外の # 場合は、エージェントの進行中のちらつきをリセットします。 StartOrResetFlickering(InAgent:agent):void= if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Attempting to start NEW FlickerEvent for this character") # 新しいちらつきが開始しました if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): spawn{FlickerCharacter(FortCharacter)} Logger.Print("Spawned a FlickerEvent for this character") else: # 進行中のちらつきをリセットします if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): Logger.Print("Reset character's FlickerTimer to VulnerableSeconds")
ダメージを受けた際に Infiltrators をちらつかせて表示する
これらの関数をすべてまとめるには、Infiltrator がダメージを受けた際の動作を処理する関数を定義します。FlickerCharacter() を使用する場合と同じように、それぞれの Infiltrator を個別にトラックして、それらがダメージを受けたかどうかを判断する必要があります。このために使用する関数は非同期である必要があるため、それぞれの Infiltrator で一つずつスポーンします。
- 新しい
OnInfiltratorDamaged()関数をinvisibility_managerクラス定義に追加します。この関数はエージェントを受け取り、そのエージェントがダメージを受けた際にStartOrResetFlickering()の呼び出しを処理します。非同期で実行させるために、この関数に<suspends>指定子を加えます。# ダメージを受けたときにエージェントをちらつかせます OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Attempting to start flickering this character") - 現在のプレイ空間の
fort_team_collectionを取得して、それをTeamCollection変数に保存します。次に、この関数に渡されたエージェントのfort_characterを取得します。# ダメージを受けたときにエージェントをちらつかせます OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Attempting to start flickering this character") TeamCollection := GetPlayspace().GetTeamCollection() if(FortCharacter := InAgent.GetFortCharacter[]): - この関数はエージェントを継続して監視する必要があるため、ループが必要になります。このループは、当該のキャラクターがダメージを受けるたびに実行されて、関数で監視されているエージェントで
StartOrResetFlickeringを呼び出します。OnInfiltratorDamagedにループを加えます。# ダメージを受けたときにエージェントをちらつかせます OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Attempting to start flickering this character") TeamCollection := GetPlayspace().GetTeamCollection() if(FortCharacter := InAgent.GetFortCharacter[]): loop: - このループ内で
IsVisibilitySharedが True であるかどうかをチェックします。True の場合は、ある Infiltrator がダメージを受けると、そのチーム内のすべての Infiltrators がちらついて表示されます。この設定が有効な場合は、このエージェントのチームと、そのチームのプレイヤーの両方を、GetTeam[]とGetAgents[]へのそれぞれの呼び出しを通じて取得します。if(FortCharacter := InAgent.GetFortCharacter[]): loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): - 次に、
forループでそれぞれのチームメイト (Teammate) でStartOrResetFlickeringを呼び出します。if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # チームメイトごとに、PlayerVisibility 秒単位で設定し、FlickerEvent をスポーンします for(Teammate : TeamAgents): Logger.Print("Calling StartOrResetFlickering on a Teammate") StartOrResetFlickering(Teammate) - 可視性 (Visibility) が共有されていない場合は、この関数で監視しているエージェントで
StartOrResetFlickeringを呼び出します。loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # チームメイトごとに、PlayerVisibility 秒単位で設定し、FlickerEvent をスポーンします for(Teammate : TeamAgents): Logger.Print("Calling StartOrResetFlickering on a Teammate") StartOrResetFlickering(Teammate) else: # ダメージを受けたキャラクターのみをちらつかせます Logger.Print("Calling StartOrResetFlickering on InAgent") StartOrResetFlickering(InAgent) - 最後に、ループの終了時に
Await()を使って特定のキャラクターのDamagedEvent()を待ちます。こうすることで、キャラクターがダメージを受けた場合のみにループがイテレートします。このループは、関数の開始時に少なくとも一度実行されます。つまりStartOrResetFlickering()への呼び出しが少なくとも一度行われることに注意してください。このため、Infiltrator はゲームの開始時にはちらついて表示されて、その後に不可視になります。これにより、Infiltrator に自分が不可視であること、さらに常に不可視であるわけではないことを示唆することができます。OnInfiltratorDamaged()関数は次のようになるはずです。# ダメージを受けたときにエージェントをちらつかせます OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Attempting to start flickering this character") TeamCollection := GetPlayspace().GetTeamCollection() if(FortCharacter := InAgent.GetFortCharacter[]): loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # チームメイトごとに、PlayerVisibility 秒単位で設定し、FlickerEvent をスポーンします for(Teammate : TeamAgents): Logger.Print("Calling StartOrResetFlickering on a Teammate") StartOrResetFlickering(Teammate) else: # ダメージを受けたキャラクターのみをちらつかせます Logger.Print("Calling StartOrResetFlickering on InAgent") StartOrResetFlickering(InAgent) FortCharacter.DamagedEvent().Await()
ゲームの開始時にキャラクターの関数をスポーンする
StartInvisibilityManager() に戻り、プレイヤーのキャラクターで Hide() を呼び出す前に、そのキャラクターに向けて OnInfiltratorDamaged() 関数をスポーンします。こうすることで、Infiltrators を非同期で監視し、それらのちらつきに関連するすべてのロジックを処理する関数を各 Infiltrator に持たせることができます。StartInvisibilityManager() は次のようになるはずです。
StartInvisibilityManager<public>(AllTeams:[]team, AllPlayers:[]player, Infiltrators:team):void=
Logger.Print("Invisibility script started!")
set Teams = GetPlayspace().GetTeamCollection().GetTeams()
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
# 各プレイヤーについて、Infiltrator チームでスポーンした場合は、そのプレイヤーの OnInfiltratorDamaged 関数をスポーンします。
# player.次に、キャラクターを不可視にします。
for (TeamPlayer : AllPlayers):
if:
FortCharacter:fort_character = TeamPlayer.GetFortCharacter[]
CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]
Logger.Print("Got this player's current team")
Teams[0] = CurrentTeam
set PlayerVisibilitySeconds[TeamPlayer] = 0.0
Logger.Print("Added player to PlayerVisibilitySeconds")
then:
spawn{OnInfiltratorDamaged(TeamPlayer)}
Logger.Print("Player spawned as an infiltrator, making them invisible")
FortCharacter.Hide()
else:
Logger.Print("This player isn't an infiltrator")
スクリプトを保存してビルドし、UEFN ツールバーの [Launch Session (セッションを起動)] をクリックしてレベルをプレイテストします。レベルをプレイテストすると、それぞれの Infiltrator がスクリプトの開始時にちらついて表示され、その後に不可視になるはずです。ダメージを受けると、IsVisibilityShared の設定に応じて個別の Infiltrator またはチーム全体がちらついて表示されます。

次のステップ
このチュートリアルの 次のステップ では、進行中のゲームに途中参加するプレイヤーの処理について説明します。