Closed5

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

aiiroIndyaiiroIndy

きっかけ

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

aiiroIndyaiiroIndy

リスポーン、自滅、撃破、途中退出の処理順は?

対象プレイヤーを取得できる関数

  • 対象:リスポーンしたプレイヤー
    撃破マネージャー(自滅有効設定)の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 時点)

aiiroIndyaiiroIndy

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[]が成功するわずかなタイミングだけと言えそう。

aiiroIndyaiiroIndy

途中退出後、ボタンにインタラクトして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に失敗するプレイヤーでは表示されないんだな、

このスクラップは2024/09/13にクローズされました