プレイヤーの無効になるタイミングを探る

きっかけ
プレイヤーがゲームを去った時は、GetPlayspace().PlayerRemovedEvent()で取ることができる。その取得したプレイヤーに対して、player.IsActive[], fort_character.IsActive[]をするとどうなるだろう、いつまでIsActive[]は成功するか

リスポーン、自滅、撃破、途中退出の処理順は?
対象プレイヤーを取得できる関数
-
対象:リスポーンしたプレイヤー
撃破マネージャー(自滅有効設定)のEliminatedEvent
fort_character.EliminatedEvent(※) -
対象:自滅したプレイヤー
撃破マネージャー(自滅有効設定)のEliminatedEvent
fort_character.EliminatedEvent(※) -
対象:撃破されたプレイヤー
撃破マネージャーのEliminatedEvent
fort_character.EliminatedEvent。(elimination_result.EliminatedCharacter) -
対象:途中退出したプレイヤー
撃破マネージャー(自滅有効設定)のEliminatedEvent
fort_character.EliminatedEvent(※)
GetPlayspace().PlayerRemovedEvent
プログラムと出力結果
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/Characters }
using { /Fortnite.com/Game }
monitoring_when_players_become_disabled := class(creative_device):
@editable
ElimManager:elimination_manager_device = elimination_manager_device{}
@editable
PlayerIdHud:hud_message_device = hud_message_device{}
var PlayerIdMap:[player]int = map{}
OnBegin<override>()<suspends>:void=
GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)
ElimManager.EliminatedEvent.Subscribe(OnAgentEliminated)
for:
I -> Player : GetPlayspace().GetPlayers()
FortChara := Player.GetFortCharacter[]
set PlayerIdMap[Player] = I
do:
FortChara.EliminatedEvent().Subscribe(OnFortCharaEliminated)
PlayerIdHud.SetText(ToMessage("PlayerId={I}"))
PlayerIdHud.Show(Player)
Print("{Player}" + "'s PlayerId={I}")
OnPlayerRemoved(Player:player):void=
for(Key -> Value : PlayerIdMap, Key = Player):
Print("OnPlayerRemoved() PlayerId={Value}" )
OnAgentEliminated(Agent:agent):void=
for(Key -> Value : PlayerIdMap, Key = Agent):
Print("OnAgentEliminated() PlayerId={Value}" )
OnFortCharaEliminated(ElimResult:elimination_result):void=
for(Key -> Value : PlayerIdMap):
if(FortChara := Key.GetFortCharacter[]):
if(FortChara = ElimResult.EliminatedCharacter):
Print("OnFortCharaEliminated() PlayerId={Value}" )
if(EliminatingChara := ElimResult.EliminatingCharacter?):
if(FortChara = EliminatingChara):
Print("OnFortCharaEliminated() PlayerId={Value} is EliminatingCharacter." )
else:
Print("OnFortCharaEliminated() PlayerId={Value} GetFortCharacter[] 失敗" )
ToMessage<public><localizes>(Text:string)<transacts>:message = "{Text}"
ToMessage<public><localizes>(Agent:agent)<transacts>:message = "{Agent}"
ToString<public>(Agent:agent):string = "{Localize(ToMessage(Agent))}"
[Output Log]
2人プレイヤーで検証
(1)リスポーンした時
(撃破マネージャーは自滅有効の設定)
LogVerse: : OnAgentEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=1 is EliminatingCharacter.
(撃破マネージャーは自滅無効の設定)
LogVerse: : OnFortCharaEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=1 is EliminatingCharacter.
(2)ダメージボリュームで自滅した時
(撃破マネージャーは自滅有効の設定)
LogVerse: : OnAgentEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=1 is EliminatingCharacter.
(撃破マネージャーは自滅無効の設定)
LogVerse: : OnFortCharaEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=1 is EliminatingCharacter.
(3)武器で撃破された時
(撃破マネージャーは自滅有効の設定)
LogVerse: : OnAgentEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=0 is EliminatingCharacter.
LogVerse: : OnFortCharaEliminated() PlayerId=1
(撃破マネージャーは自滅無効の設定)
LogVerse: : OnAgentEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=0 is EliminatingCharacter.
LogVerse: : OnFortCharaEliminated() PlayerId=1
(4)途中退出した時
(撃破マネージャーは自滅有効の設定)
LogVerse: : OnAgentEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=1 is EliminatingCharacter.
LogVerse: : OnPlayerRemoved() PlayerId=1
(撃破マネージャーは自滅無効の設定)
LogVerse: : OnFortCharaEliminated() PlayerId=1
LogVerse: : OnFortCharaEliminated() PlayerId=1 is EliminatingCharacter.
LogVerse: : OnPlayerRemoved() PlayerId=1
まとめ
- ログに出力されるタイミングはほぼ同時。
- ただし、出力されている順番はいつも同じだったので、以下を処理順として備忘。
(1) プレイヤーがリスポーンした時
撃破マネージャー(自滅有効)のEliminatedEvent → fort_character.EliminatedEvent
(2) プレイヤーが自滅した時
撃破マネージャー(自滅有効)のEliminatedEvent → fort_character.EliminatedEvent
(3) プレイヤーが撃破された時
撃破マネージャー → fort_character.EliminatedEvent
(4) プレイヤーが途中退出した時
撃破マネージャー(自滅有効)のEliminatedEvent → fort_character.EliminatedEvent → PlayerRemovedEvent
(24.09.07 時点)

fort_playspace.PlayerRemovedEvent()で取得したプレイヤーが無効になるまで
monitoring_when_players_become_disabled := class(creative_device):
@editable
ElimManager:elimination_manager_device = elimination_manager_device{}
@editable
PlayerIdHud:hud_message_device = hud_message_device{}
@editable
PrintStopBtn:button_device = button_device{}
var PlayerIdMap:[player]int = map{}
OnBegin<override>()<suspends>:void=
GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)
for:
I -> Player : GetPlayspace().GetPlayers()
FortChara := Player.GetFortCharacter[]
set PlayerIdMap[Player] = I
do:
PlayerIdHud.SetText(ToMessage("PlayerId={I}"))
PlayerIdHud.Show(Player)
Print("{Player}" + "'s PlayerId={I}")
OnPlayerRemoved(Player:player):void=
for(Key -> Value : PlayerIdMap, Key = Player):
Print("OnPlayerRemoved() PlayerId={Value}" )
spawn:
LoopPrint(Player)
# PrintStopBtnが押されるまで、ログ出力処理を毎秒実行。
LoopPrint(Agent:agent)<suspends>:void=
branch:
var LoopCount:int = 0
loop:
Print_IsActive(Agent, LoopCount)
Sleep(1.0)
set LoopCount += 1
PrintStopBtn.InteractedWithEvent.Await()
# ログ出力。Agentが有効かチェックする
Print_IsActive(Agent:agent, LoopCount:int):void=
# playerに型変換
if(Player := player[Agent]):
# playerが有効か
if(Player.IsActive[]):
Print("Print_IsActive() {LoopCount} Player.IsActive[] ")
else:
Print("Print_IsActive() {LoopCount} Player.IsActive[] 失敗")
# map型のValue値を取得
if(PlayerId := PlayerIdMap[Player]):
Print("Print_IsActive() {LoopCount} PlayerIdMap PlayerId={PlayerId}")
else:
Print("Print_IsActive() {LoopCount} PlayerIdMap 取得失敗")
else:
Print("Print_IsActive() {LoopCount} player[Agent] 型変換失敗")
# fort_character取得
if(FortChara := Agent.GetFortCharacter[]):
# fort_characterが有効か
if(FortChara.IsActive[]):
Print("Print_IsActive() {LoopCount} FortChara.IsActive[]")
else:
Print("Print_IsActive() {LoopCount} FortChara.IsActive[] 失敗")
else:
Print("Print_IsActive() {LoopCount} Agent.GetFortCharacter[] 失敗")
ToMessage<public><localizes>(Text:string)<transacts>:message = "{Text}"
ToMessage<public><localizes>(Agent:agent)<transacts>:message = "{Agent}"
ToString<public>(Agent:agent):string = "{Localize(ToMessage(Agent))}"
LogVerse: : OnPlayerRemoved() PlayerId=1
LogVerse: : Print_IsActive() 0 Player.IsActive[]
LogVerse: : Print_IsActive() 0 PlayerIdMap PlayerId=1
LogVerse: : Print_IsActive() 0 FortChara.IsActive[] 失敗
LogVerse: : Print_IsActive() 1 Player.IsActive[] 失敗
LogVerse: : Print_IsActive() 1 PlayerIdMap PlayerId=1
LogVerse: : Print_IsActive() 1 Agent.GetFortCharacter[] 失敗
LogVerse: : Print_IsActive() 2 Player.IsActive[] 失敗
LogVerse: : Print_IsActive() 2 PlayerIdMap PlayerId=1
LogVerse: : Print_IsActive() 2 Agent.GetFortCharacter[] 失敗
LogVerse: : Print_IsActive() 3 Player.IsActive[] 失敗
LogVerse: : Print_IsActive() 3 PlayerIdMap PlayerId=1
...
ログを見るとPlayer.IsActive[]は一度成功している。Agent.GetFortCharacter[] も一度成功している。
そこで、ログ出力を0.01秒おきに変更してみたが 結果は同じだった。
まとめ
途中退出したプレイヤーについて
a.player型への型変換(player[Agent])は成功する
b.player.IsActive[] は最初成功するが、以降失敗する
c.agent.GetFortCharacter[] は最初成功するが、以降失敗する
d.fort_character.IsActive[] は失敗する
e.map型のKey値に指定したValue値の取得は成功する
途中退出したプレイヤーに対して、player.IsActive[]はPlayerRemovedEventで受け取った直後の1回のみ成功。fort_character.IsActive[]は一度も成功しなかった。
playerへの型変換は成功するし、map型のKey値に指定しても成功するので、受け取ったagentが全く無効になるとは言えないかもしれない。ただ、プレイヤーとして有効なのは、player.IsActive[]が成功するわずかなタイミングだけと言えそう。

途中退出後、ボタンにインタラクトしてIsActiveをチェック
PlayerRemovedEventについては確認した( https://zenn.dev/link/comments/0a80a285b79437 )ので、途中退出後、残ったプレイヤーがボタンにインタラクトしてIsActiveの結果を見る。退出直後ではないので、常に失敗するはず。
前回、agentをキーとするmapに事前に保存しておくと、途中退出後もmapから取得できることがわかった。そこで、IsActiveをチェックする対象プレイヤーの情報には、mapから取得したデータを使用する。
program
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/Characters }
using { /Fortnite.com/Game }
user_info := struct:
PlayerId:int
Player:player
YearsOld:int
Birthday:string #yyyymmdd
monitoring_when_players_become_disabled := class(creative_device):
@editable
ElimManager:elimination_manager_device = elimination_manager_device{}
@editable
PlayerIdHud:hud_message_device = hud_message_device{}
@editable
LoopPrintStopBtn:button_device = button_device{}
@editable
PrintBtn:button_device = button_device{}
var UserInfodMap:[player]user_info = map{}
OnBegin<override>()<suspends>:void=
GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)
PrintBtn.InteractedWithEvent.Subscribe(OnPrintBtnInteracted)
for:
I -> Player : GetPlayspace().GetPlayers()
FortChara := Player.GetFortCharacter[]
UserInfo := user_info:
PlayerId := I
Player := Player
YearsOld := 20 + I
Birthday := "{20040101 + I*10000}"
set UserInfodMap[Player] = UserInfo
do:
PlayerIdHud.SetText(ToMessage("PlayerId={I}"))
PlayerIdHud.Show(Player)
Print("{Player}" + "'s PlayerId={I}")
OnPlayerRemoved(Player:player):void=
Print("OnPlayerRemoved() Player={Player}")
spawn:
LoopPrint(Player)
OnPrintBtnInteracted(Agent:agent):void=
Print("OnPrintBtnInteracted() Agent={Agent}")
for(Key -> Value : UserInfodMap):
Print_IsActive(Key)
# LoopPrintStopBtnが押されるまで、ログ出力処理を毎秒実行。
LoopPrint(Agent:agent)<suspends>:void=
branch:
var LoopCount:int = 0
loop:
Print_IsActive(Agent, ?LoopCount:=LoopCount)
Sleep(1.0)
set LoopCount += 1
LoopPrintStopBtn.InteractedWithEvent.Await()
# ログ出力。Agentが有効かチェックする
Print_IsActive(Agent:agent, ?LoopCount:int=-1):void=
var CountStr:string = "{LoopCount}"
if(LoopCount < 0):
set CountStr = ""
# playerに型変換
if(Player := player[Agent]):
# playerが有効か
if(Player.IsActive[]):
Print("Print_IsActive() {CountStr} {Agent} Player.IsActive[] ")
else:
Print("Print_IsActive() {CountStr} {Agent} Player.IsActive[] 失敗")
# map型のValue値を取得
if(UserInfo := UserInfodMap[Player]):
Print("Print_IsActive() {CountStr} {Agent} UserInfodMap PlayerId={UserInfo.PlayerId}, YearsOld={UserInfo.YearsOld}, Birthday={UserInfo.Birthday}")
Print("Print_IsActive() {CountStr} {Agent} UserInfodMap " +
"{if(UserInfo.Player.IsActive[]) then "UserInfo.Player.IsActive[]" else "UserInfo.Player.IsActive[] 失敗"}")
else:
Print("Print_IsActive() {CountStr} {Agent} UserInfodMap 取得失敗")
else:
Print("Print_IsActive() {CountStr} {Agent} player[Agent] 型変換失敗")
# fort_character取得
if(FortChara := Agent.GetFortCharacter[]):
# fort_characterが有効か
if(FortChara.IsActive[]):
Print("Print_IsActive() {CountStr} {Agent} FortChara.IsActive[]")
else:
Print("Print_IsActive() {CountStr} {Agent} FortChara.IsActive[] 失敗")
else:
Print("Print_IsActive() {CountStr} {Agent} Agent.GetFortCharacter[] 失敗")
ToMessage<public><localizes>(Text:string)<transacts>:message = "{Text}"
ToMessage<public><localizes>(Agent:agent)<transacts>:message = "{Agent}"
ToString<public>(Agent:agent):string = "{Localize(ToMessage(Agent))}"
LogVerse: : OnPrintBtnInteracted() Agent=Anonymous[256]
LogVerse: : Print_IsActive() Anonymous[256] Player.IsActive[]
LogVerse: : Print_IsActive() Anonymous[256] UserInfodMap PlayerId=0, YearsOld=20, Birthday=20040101
LogVerse: : Print_IsActive() Anonymous[256] UserInfodMap UserInfo.Player.IsActive[]
LogVerse: : Print_IsActive() Anonymous[256] FortChara.IsActive[]
LogVerse: : Print_IsActive() Anonymous[257] Player.IsActive[]
LogVerse: : Print_IsActive() Anonymous[257] UserInfodMap PlayerId=1, YearsOld=21, Birthday=20050101
LogVerse: : Print_IsActive() Anonymous[257] UserInfodMap UserInfo.Player.IsActive[]
LogVerse: : Print_IsActive() Anonymous[257] FortChara.IsActive[]
LogVerse: : OnPlayerRemoved() Player=Anonymous[257]
LogVerse: : Print_IsActive() 0 Anonymous[257] Player.IsActive[]
LogVerse: : Print_IsActive() 0 Anonymous[257] UserInfodMap PlayerId=1, YearsOld=21, Birthday=20050101
LogVerse: : Print_IsActive() 0 Anonymous[257] UserInfodMap UserInfo.Player.IsActive[]
LogVerse: : Print_IsActive() 0 Anonymous[257] FortChara.IsActive[] 失敗
LogVerse: : Print_IsActive() 1 Player.IsActive[] 失敗
LogVerse: : Print_IsActive() 1 UserInfodMap PlayerId=1, YearsOld=21, Birthday=20050101
LogVerse: : Print_IsActive() 1 UserInfodMap UserInfo.Player.IsActive[] 失敗
LogVerse: : Print_IsActive() 1 Agent.GetFortCharacter[] 失敗
LogVerse: : Print_IsActive() 2 Player.IsActive[] 失敗
LogVerse: : Print_IsActive() 2 UserInfodMap PlayerId=1, YearsOld=21, Birthday=20050101
LogVerse: : Print_IsActive() 2 UserInfodMap UserInfo.Player.IsActive[] 失敗
LogVerse: : Print_IsActive() 2 Agent.GetFortCharacter[] 失敗
LogVerse: : OnPrintBtnInteracted() Agent=Anonymous[256]
LogVerse: : Print_IsActive() Anonymous[256] Player.IsActive[]
LogVerse: : Print_IsActive() Anonymous[256] UserInfodMap PlayerId=0, YearsOld=20, Birthday=20040101
LogVerse: : Print_IsActive() Anonymous[256] UserInfodMap UserInfo.Player.IsActive[]
LogVerse: : Print_IsActive() Anonymous[256] FortChara.IsActive[]
LogVerse: : Print_IsActive() Player.IsActive[] 失敗
LogVerse: : Print_IsActive() UserInfodMap PlayerId=1, YearsOld=21, Birthday=20050101
LogVerse: : Print_IsActive() UserInfodMap UserInfo.Player.IsActive[] 失敗
LogVerse: : Print_IsActive() Agent.GetFortCharacter[] 失敗
LogVerse: : Print_IsActive() 3 Player.IsActive[] 失敗
LogVerse: : Print_IsActive() 3 UserInfodMap PlayerId=1, YearsOld=21, Birthday=20050101
LogVerse: : Print_IsActive() 3 UserInfodMap UserInfo.Player.IsActive[] 失敗
LogVerse: : Print_IsActive() 3 Agent.GetFortCharacter[] 失敗
...
LogVerse: : OnPlayerRemoved() Player=Anonymous[257]
一方のプレイヤーが途中退出し、
LogVerse: : OnPrintBtnInteracted() Agent=Anonymous[256]
もう一方のプレイヤーがボタンにインタラクトした結果、
LogVerse: : Print_IsActive() Anonymous[256] Player.IsActive[]
ボタンにインタラクトしたプレイヤーはIsActiveに成功し、
LogVerse: : Print_IsActive() Player.IsActive[] 失敗
退出したプレイヤーはIsActiveに失敗した。
ところで 「Anonymous〇〇」はIsAcitveに失敗するプレイヤーでは表示されないんだな、