はじめに
NPCのコンポーネント検証シリーズ、今回は npc_actions_component のサブクラスである guard_actions_component について調べました。
このコンポーネントを使うと、ガードNPC特有の「徘徊」「しゃがみ」「ジャンプ」といった、より人間らしい高度なアクションをVerseから制御できるようになります。
今回は前編として、以下の6つのアクションを検証しました。
✅ RoamAround(周辺の徘徊)
❓ MoveInRangeToAttack(射程内への移動:挙動確認中)
✅ Crouch(しゃがみ)
⚠️ StandUp(立ち上がり:代替案あり)
✅ Jump(ジャンプ)
✅ PlayRandomEmote(ランダムエモート)
今回の検証では、一部のメソッドにおいて公式の意図通りに動作しない、あるいは発動条件が特定できないケースがありました。本記事では、現時点で判明した「動作する条件」と「挙動が不明だった点」をありのままにまとめています。
もし「こうすれば動いたよ!」という解決策をご存知の方がいれば、ぜひコメントで教えていただけると幸いです。
※本記事の内容は筆者の実験結果に基づく推測を含みます。今後のアップデートで挙動が変わる可能性がありますのでご注意ください。
環境:
- UEFN: v39.10(UE 5.8)
guard_actions_component とは
v39.00 のリリース ノート で追加された、高度なガードAI用のコンポーネントです。
- 特徴: 攻撃、障害物の飛び越え、蘇生など、従来のNPCより一歩踏み込んだ動作が可能になります。
-
移行点: 従来の
leashableインターフェースに代わるものとして導入されました。
API定義(guard_actions_component)
@available {MinUploadedAtFNVersion := 3900}
# Fortnite Guards AI actions management
guard_actions_component<native><public> := class<epic_internal>(npc_actions_component):
# Roam around the current position. Use 'Tether' to specify the radius; otherwise, the Fortnite guard will roam anywhere.
RoamAround<native><public>(?MovementType:movement_type = external {})<suspends>:result(void, ai_action_error_type)
# Move in range to attack the current target.
MoveInRangeToAttack<native><public>()<suspends>:result(void, ai_action_error_type)
# Change stance to crouch. Will never complete unless interrupted.
Crouch<native><public>()<suspends>:result(void, ai_action_error_type)
# Go back to standing.
StandUp<native><public>()<suspends>:result(void, ai_action_error_type)
# Trigger a jump.
Jump<native><public>()<suspends>:result(void, ai_action_error_type)
# Play a random emote from the character emotes bank.
PlayRandomEmote<native><public>()<suspends>:result(void, ai_action_error_type)
# Revive the specified target.
Revive<native><public>(Target:entity)<suspends>:result(void, ai_action_error_type)
# Attack the target. The target must have been detected.
Attack<native><public>(Target:entity)<suspends>:result(void, ai_action_error_type)
# Tether the NPC to a position.
# 'Radius' is in centimeters.
Tether<native><public>(Location:(/Verse.org/SpatialMath:)vector3, Radius:float):void
# Tether the NPC to an entity.
# 'Radius' is in centimeters.
Tether<native><public>(Target:entity, Radius:float):void
# Untether the NPC.
Untether<native><public>():void
事前設定(検証環境)
-
Island Settings: チームを
FreeForAllに設定。 -
NPCキャラクター定義:
- NPC Behavior Script: テンプレート「NPC Behavior Basic」を使用(変更なし)。
- Team Modifier: Index 1 を指定。ソロプレイ時はNPCがプレイヤーの味方になります。
- Guard Perception Modifier: デフォルト(オーバーライドなし)。
1. RoamAround
# Roam around the current position. Use 'Tether' to specify the radius; otherwise, the Fortnite guard will roam anywhere.
RoamAround<native><public>(?MovementType:movement_type = external {})<suspends>:result(void, ai_action_error_type)
# 現在の位置の周辺を徘徊します。'Tether'を使用して半径を指定します。指定しない場合、ガードはどこでも徘徊します。
MovementType の指定なし (= Running)
MovementType = Walking
-
検証結果:
- 徘徊させるには
Tether()(係留設定) の実行が必須です。Tetherを設定しない場合、NPCはその場から動きませんでした - 引数の
MovementTypeで歩き(Walking)か走り(Running)かを選択できます(デフォルトは Running)
- 徘徊させるには
検証用コード
RoamAround_Test()<suspends>:void=
Print("========== RoamAround_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)
# ※ デフォルトは Running
GuardActionsComp.RoamAround()
# GuardActionsComp.RoamAround(?MovementType:=movement_type.Walking)
# GuardActionsComp.RoamAround(?MovementType:=movement_type.Running)
Print("===============================================")
2. MoveInRangeToAttack
# Move in range to attack the current target.
MoveInRangeToAttack<native><public>()<suspends>:result(void, ai_action_error_type)
# 現在のターゲットを攻撃するための射程内に移動します。
-
検証結果:
- 目に見えるアクションは確認できませんでした
- 攻撃アクション中に呼び出すと、攻撃を中断してその場に立ち止まるような動きが見られました
- 推測: 単体で呼び出すのではなく、Attackと併用するなど、ほかに条件があるのかもしれません
3. Crouch
# Change stance to crouch. Will never complete unless interrupted.
Crouch<native><public>()<suspends>:result(void, ai_action_error_type)
# しゃがみ姿勢に変更します。中断されない限り完了しません。
-
検証結果:
- NPCがしゃがみ体勢になります
- Crouch() 単体の実行では、NPCはしゃがみ姿勢になりません
RouamAround() または、npc_actions_component.Idle() のあとに Crouch() を実行することで、NPCはしゃがみ状態へ移行しました - Crouch() 自体は中断されるまで完了しない( が終わらない)ため、race 式などで制御する必要があります
検証用コード
# RoamAround() か Idle() を実行後、Crouch() を実行すると NPCはしゃがむ
Crouch_Test()<suspends>:void=
if(SimE := GetSimulationEntity[]):
GuardActionsComps := for(Comp : SimE.FindDescendantComponents(guard_actions_component)){Comp}
if(GuardActionsComp := GuardActionsComps[0]):
Print("========== Crouch_Test ==========")
GuardActionsComp.RoamAround()
Print(" - RoamAround: End.")
# GuardActionsComp.Idle(?Duration:=1.0)
# Print(" - Idle: End1.")
GuardActionsComp.Crouch()
Print("===============================================")
4. StandUp
# Go back to standing.
StandUp<native><public>()<suspends>:result(void, ai_action_error_type)
# 立ち上がります。
※ この動画は StandUp() ではない方法で立ち上がらせています
-
検証結果:
- Crouch() 実行中に、StandUp() を呼び出してもNPCは立ち上がりませんでした
戻り値としてはエラーにならないものの、見た目はしゃがんだままです - 動画は、Idle() で立たせています。(検用証コード「StandUp_Test2()」)
- Crouch() 実行中に、StandUp() を呼び出してもNPCは立ち上がりませんでした
【Tips】NPCを確実に立たせる方法
Crouch() と、 RoamAround() または npc_actions_component.Idle() を race することで、NPCは立ち上がります
検証用コード
# Crouch() 実行中に、 StandUp() を実行する
# ⇒ 結果は エラーではないが、立ち上がらない
StandUp_Test1()<suspends>:void=
if(SimE := GetSimulationEntity[]):
GuardActionsComps := for(Comp : SimE.FindDescendantComponents(guard_actions_component)){Comp}
if(GuardActionsComp := GuardActionsComps[0]):
Print("========== StandUp_Test1 ==========")
race:
block:
GuardActionsComp.Idle(?Duration:=1.0)
Print(" - Idle: End1.")
GuardActionsComp.Crouch()
Print(" - Crouch: End.")
block:
# Crouch() 実行中に、StandUp() を実行するための待機
Sleep(3.0)
Result := GuardActionsComp.StandUp()
if(ErrorType := Result.GetError[]):
Print(" - StandUp: ErrorType: " + ToDiagnostic(ErrorType))
else:
Print(" - StandUp: Success")
Print("===============================================")
# Crouch() 実行中に、 RoamAround() または Idle() を実行する
StandUp_Test2()<suspends>:void=
if(SimE := GetSimulationEntity[]):
GuardActionsComps := for(Comp : SimE.FindDescendantComponents(guard_actions_component)){Comp}
if(GuardActionsComp := GuardActionsComps[0]):
Print("========== StandUp_Test2 ==========")
race:
block:
GuardActionsComp.Idle(?Duration:=1.0)
Print(" - Idle: End1.")
GuardActionsComp.Crouch()
Print(" - Crouch: End.")
block:
# Crouch() 実行中に、処理を実行するための待機
Sleep(3.0)
# # RoamAround() ⇒ 立ち上がる
# GuardActionsComp.RoamAround()
# Print(" - RoamAround: End.")
# Idle() ⇒ 立ち上がる
GuardActionsComp.Idle(?Duration:=1.0)
Print(" - Idle: End2.")
Print("===============================================")
5. Jump
# Trigger a jump.
Jump<native><public>()<suspends>:result(void, ai_action_error_type)
# ジャンプをトリガーします。
-
検証結果:
- NPCがその場で1回ジャンプします
- 着地したタイミングで
Jump()の処理が終了します
検証用コード
Jump_Test()<suspends>:void=
if(SimE := GetSimulationEntity[]):
GuardActionsComps := for(Comp : SimE.FindDescendantComponents(guard_actions_component)){Comp}
if(GuardActionsComp := GuardActionsComps[0]):
Print("========== Jump_Test ==========")
GuardActionsComp.Jump()
Print("===============================================")
6. PlayRandomEmote
# Play a random emote from the character emotes bank.
PlayRandomEmote<native><public>()<suspends>:result(void, ai_action_error_type)
# キャラクターのエモートバンクからランダムなエモートを再生します。
-
検証結果:
- 一度開始すると、そのエモートを繰り返します。自動で終了することはありませんでした
-
race式を使い、一定時間後に他のアクションを割り込ませることで終了可能です
検証用コード
PlayRandomEmote_Test1()<suspends>:void=
if(SimE := GetSimulationEntity[]):
GuardActionsComps := for(Comp : SimE.FindDescendantComponents(guard_actions_component)){Comp}
if(GuardActionsComp := GuardActionsComps[0]):
Print("========== PlayRandomEmote_Test ==========")
GuardActionsComp.PlayRandomEmote()
Print("===============================================")
後編に続きます。
関連記事
