1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【UEFN】guard_actions_component の検証 - 後編

Last updated at Posted at 2025-12-14

はじめに

前回の記事(前編)に引き続き、ガードAIの行動を制御する guard_actions_component について調べます

✅ Revive(蘇生)

❓ Attack(ターゲット攻撃:挙動確認中)

✅ Tether (Position)(地点への係留)

✅ Tether (Entity)(エンティティへの係留・追従)

✅ Untether(係留の解除)


環境:
UEFN v39.10(UE 5.8)


7. Revive

    # Revive the specified target.
    Revive<native><public>(Target:entity)<suspends>:result(void, ai_action_error_type)

# 指定されたターゲットを蘇生します。

  • 検証結果:
    • Revive() を実行すると、 NPCが対象へ歩み寄り、蘇生モーションを開始します
    • NPCを味方チームに変更してから Revive() を実行しました
検証用コード・設定

プレイヤーをダウン状態にするための準備:

  1. ダウンの仕掛けを配置します
  2. 2人プレイでゲームを開始し、NPCを1体スポーンします
  3. 全員を同じチームにします(コード内で実行しています)

ダウンの仕掛け
image.png

    # 復活のテスト
    Revive_Test(Agent:agent)<suspends>:void=
        Print("========== Revive_Test ==========")
        AllFortCharacters := GetAllFortCharacters()
        Print(" - AllFortCharacters.Length: {AllFortCharacters.Length}")
        TC := GetPlayspace().GetTeamCollection()
        for(I -> FC : AllFortCharacters, FCAgent := FC.GetAgent[]):
            if(FCEntity := FC.GetEntity[]):
                PrintAgentName(FCEntity)

            # NPCを含む全員が、Agentと同じチームになるように変更
            if(TC.ChangeTeam[FCAgent, Agent, team_attitude.Friendly]):
            else:
                Print(" - Error: ChangeTeam[] failed. {FCAgent}" + ToDiagnostic(team_attitude.Friendly))

        # Agent を ダウン状態にする
        DBNODevice.Down(Agent)
        
        # NPC が ダウン状態のプレイヤーを復活させる
        if(SimE := GetSimulationEntity[]):
            GuardActionsComps := for(Comp : SimE.FindDescendantComponents(guard_actions_component)){Comp}
            if(GuardActionsComp := GuardActionsComps[0]):
                GuardActionsComp.Revive(Agent)
        Print("===============================================")


8. Attack

    # Attack the target. The target must have been detected.
    Attack<native><public>(Target:entity)<suspends>:result(void, ai_action_error_type)

# ターゲットを攻撃します。ターゲットは検知されている必要があります。

  • 検証結果:
    • NPCを敵チームに変更後、Attack() を実行すると、プレイヤーに向かって攻撃アクションを開始しました
      しかし、Attack() を呼び出さなくても、敵チームであればNPCは自律的に攻撃を開始します。そのため、このメソッドによって攻撃がトリガーされたのか、標準AIの検知によって開始されたのかの区別が困難でした
    • 処理終了タイミング: NPCが攻撃しているプレイヤーが撃破した直後。また、NPCが撃破されてリスポーンした直後 に処理が終了しました
検証用コード

GetAgentFromEntity[]、ChangeTeam[] は自作関数です。 おまけ: 自作関数

    Attack_Test1(Agent:agent)<suspends>:void=
        if(SimE := GetSimulationEntity[]):
            GuardActionsComps := for(Comp : SimE.FindDescendantComponents(guard_actions_component)){Comp}
            TC := GetPlayspace().GetTeamCollection()
            if:
                GuardActionsComp := GuardActionsComps[0]
                AgentE := Agent.GetFortCharacter[].GetEntity[]
                NPCAgent := GetAgentFromEntity[GuardActionsComp.Entity]
                NPCAwarenessComp := GuardActionsComp.Entity.GetComponent[npc_awareness_component]

            then:
                NPCAwarenessComp.DetectTargetEvent.Subscribe(OnDetectTarget)
                NPCAwarenessComp.ForgetTargetEvent.Subscribe(OnForgetTarget)

                Print("========== Attack_Test1 ==========")
                # NPC を Agentと敵チームになるように変更
                if(TC.ChangeTeam[NPCAgent, Agent, team_attitude.Hostile]):
                    Print(" - ChangeTeam[] Success: Hostile")
                else:
                    Print(" - Error: ChangeTeam[] failed. Hostile")
                GuardActionsComp.Attack(AgentE)
                Print("===============================================") 

    OnDetectTarget(TaretInfo : npc_target_info):void=
        Print("OnDetectTarget: Target を検知されました")

    OnForgetTarget(TargetEntity : entity):void=
        Print("OnForgetTarget: Target を忘れられました")

9. Tether

    # Tether the NPC to a position.
    #  'Radius' is in centimeters.
    Tether<native><public>(Location:(/Verse.org/SpatialMath:)vector3, Radius:float):void

# NPCを位置にテザー(係留)します。
# 'Radius'はセンチメートル単位です。

  • 検証結果:
    • RoamAround() を実行すると、 この指定位置にNPCが位置に係留します
      特定の拠点を守る歩哨のような動きが作れます

10. Tether

    # Tether the NPC to an entity.
    #  'Radius' is in centimeters.
    Tether<native><public>(Target:entity, Radius:float):void

# NPCをエンティティにテザー(係留)します。
# 'Radius'はセンチメートル単位です。

  • 検証結果:
    • プレイヤーに Tether して RoamAround() を実行すると、 プレイヤーの移動に合わせてNPCも付いてくる(追従する) ようになります。護衛対象を守るガードの実装ができます
検証用コード
    Tether_Test(Agent:agent)<suspends>:void=
        Print("========== Tether_Test ==========")
        if(SimE := GetSimulationEntity[]):
            GuardActionsComps := for(Comp : SimE.FindDescendantComponents(guard_actions_component)){Comp}
            if(GuardActionsComp := GuardActionsComps[0]):

                # # 特定の位置にテザーする場合
                # TetherCenter := (/Verse.org/SpatialMath:)vector3{Left:=8456.0, Up:=0.0, Forward:=3340.0}
                # TetherRadius := 512.0
                # GuardActionsComp.Tether(TetherCenter, TetherRadius)

                # Agent にテザーする場合
                TetherRadius := 256.0
                GuardActionsComp.Tether(Agent, TetherRadius)
 
                Print(" - Tether to Agent. Radius: {TetherRadius}")
                GuardActionsComp.RoamAround()
        Print("===============================================")

11. Untether

    # Untether the NPC.
    Untether<native><public>():void

# NPCのテザー(係留)を解除します。

  • 検証結果:
    • Tether を設定して、 RoamAround() 実行中に Untether() することで、 係留を解除します
検証用コード
    Untether_Test(Agent:agent)<suspends>:void=
        Print("========== Untether_Test ==========")
        if(SimE := GetSimulationEntity[]):
            GuardActionsComps := for(Comp : SimE.FindDescendantComponents(guard_actions_component)){Comp}
            if(GuardActionsComp := GuardActionsComps[0]):
                # Tether(RoamAround) と Untether を繰り返す
                loop:
                    race:
                        block:
                            TetherRadius := 256.0
                            GuardActionsComp.Tether(Agent, TetherRadius)
                            Print(" - Tether to Agent. Radius: {TetherRadius}")
                            GuardActionsComp.RoamAround()
                            Print(" - RoamAround: End.")

                        block:
                            Sleep(3.0)
                            GuardActionsComp.Untether()
                            Print(" - Untether")
                    Sleep(2.0)
        Print("===============================================")

まとめ

前後編にわたって guard_actions_component を検証してきました。

このコンポーネントの登場により、「プレイヤーに付いてくるガード」や「ダウンした仲間を助けるNPC」が、Verseからシンプルに実装できるようになりそうですね

おまけ:自作関数

Agent と予想される Entity から Agent を取得する

# Agent と予想される Entity から agent オブジェクトを取得する。取得できない場合、失敗する
GetAgentFromEntity<public>(AgentEntity:entity)<transacts><decides>:agent=
    var ReturnValue:?agent = false
    for(Comp : AgentEntity.GetComponents()):
        if:
            FC := fort_character[Comp]
            Agent := FC.GetAgent[]
        then:
            set ReturnValue = option{ Agent }
    return ReturnValue?

すべての FortCharacter を取得する

    # すべての FortCharacter を取得する
    (Entity:entity).GetAllFortCharacters<public>()<transacts>:[]fort_character=
        var ReturnValue:[]fort_character = array{}
        if(SimE := Entity.GetSimulationEntity[]):
            for(ChildEntity : SimE.GetEntities()):
                for(Comp : ChildEntity.GetComponents()):
                    if(FC := fort_character[Comp]):
                        set ReturnValue += array{ FC }
        return ReturnValue

Agent1 を Agent2 の味方 or 敵 にする

# Agent1 を Agent2 の味方 or 敵 にする
# - Friendly(味方): Agent1 を Agent2 と同じチームにする
# - Hostile(敵): Agent1 を Agent2 と異なるチームにする
# - Neutral(中立): 実装なし。失敗扱い
# agent に team_attitude を直接設定することはできないため、 チームを変更することで関係性を設定する
(TeamCollection:fort_team_collection).ChangeTeam<public>(Agent1:agent, Agent2:agent, TeamAttitude:team_attitude)<transacts><decides>:void=
    case(TeamAttitude):
        team_attitude.Friendly =>
            Agent2Team := TeamCollection.GetTeam[Agent2]
            TeamCollection.AddToTeam[Agent1, Agent2Team]

        team_attitude.Hostile =>
            Teams := TeamCollection.GetTeams()
            # Agent2 が所属していない最初のチームを取得
            NewTeamQ := option:
                for(I -> Team : Teams, not TeamCollection.IsOnTeam[Agent2, Team]){ Team }[0]

            NewTeam := NewTeamQ?
            TeamCollection.AddToTeam[Agent1, NewTeam]

        team_attitude.Neutral =>
            Print(" - Neutral はチーム変更の実装なし. 失敗扱い")
            false?

関連記事

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?