はじめに
UEFN (Verse) の npc_actions_component について、公式ドキュメントがまだ充実していないため、実際にコードを動かして挙動を検証しました。 機能が多岐にわたるため、今回は 「移動(Navigation)」 に関連する以下の機能に絞って紹介します。
-
GetCurrentDestination: 現在の目的地の取得
-
NavigateTo: 特定の場所への移動
-
MovementSpeedMultiplier: 移動速度の変更
※本記事の内容は筆者の実験結果に基づく推測を含みます。今後のアップデートで挙動が変わる可能性がありますのでご注意ください。
環境:
Fortnite v39.00 (UE 5.8)
npc_actions_component とは
NPC の行動を制御するためのコンポーネントです。 NPC を特定の位置へ移動させたり、視線を制御したりすることができます。
API定義: npc_actions_component
@available {MinUploadedAtFNVersion := 3900}
# Fortnite NPC AI actions management
npc_actions_component<native><public> := class<final_super><epic_internal>(component):
# Return the current destination of the character.
GetCurrentDestination<native><public>()<transacts><decides>:(/Verse.org/SpatialMath:)vector3
# Navigate toward the specified navigation target.
NavigateTo<native><public>(Target:navigation_target, ?MovementType:movement_type = external {}, ?ReachRadius:float = external {}, ?AllowPartialPath:logic = external {})<suspends>:result(navigation_action_success_type, navigation_action_error_type)
# Multiplier on the movement speed (value is clamped between 0.5 and 2).
var MovementSpeedMultiplier<public>:float = external {}
# Stop navigation.
StopNavigation<native><public>():void
# Stay idle for a specific duration.
Idle<native><public>(?Duration:float = external {})<suspends>:void
# Look at the specified location. If LockFocus is false, the action stops once the NPC is facing the position.
Focus<native><public>(Location:(/Verse.org/SpatialMath:)vector3, ?LockFocus:logic = external {})<suspends>:void
# Look at the specified Entity. If LockFocus is false, the action stops once the NPC is facing the entity.
Focus<native><public>(Target:entity, ?LockFocus:logic = external {})<suspends>:void
1. GetCurrentDestination
# Return the current destination of the character.
GetCurrentDestination<native><public>()<transacts><decides>:(/Verse.org/SpatialMath:)vector3
# キャラクターの現在の目的地を返します。
検証結果
-
戻り値は「直近の経由地点」
NavigateTo() で指定したターゲット(最終目的地)ではなく、ナビゲーションメッシュ上の「現在向かっている中継地点(パス上の次のポイント)」の座標が返ってくるようです。 -
実行タイミング
移動(NavigateTo())が開始される前に呼び出すと失敗します。
ターゲット(最終目的地)に向かうための、現在目指している中継地点が取得できます。
NPCが3つの黄色い人形を順番に移動します。GetCurrentDestination[] で取得した座標に、ピンクの人形を毎秒テレポートさせています。
黄色い人形(最終目的地)へ向かうルート上の、少し手前の位置(現在向かっている場所)にピンクの人形がテレポートしていることがわかります。
検証用コード
# 現在の目的地を取得するテスト
GetCurrentDestination_Test(Agent:agent)<suspends>:void=
Print("========== GetCurrentDestination_Test ==========")
if(SimE := GetSimulationEntity[]):
# SimulationEntity 以下にある npc_actions_component を1つ取得
NPCActionsComps := for(Comp : SimE.FindDescendantComponents(npc_actions_component)){Comp}
if(NPCActionsComp := NPCActionsComps[0]):
sync:
block:
# 3秒待機後に NavigateTo を実行。3箇所に移動する
Sleep(3.0)
for(X := 0..2, NavTargetProp := NavTargetProps[X]):
TargetPosUE := NavTargetProp.GetTransform().Translation
TargetPosVerse := FromVector3(TargetPosUE)
Print("-----------------------------------------------")
Print(" - NavigateTo[{X}] TargetPosition: {TargetPosVerse}")
# UEモジュール, Verseモジュール どちらのvector3でもOK
NPCActionsComp.NavigateTo(MakeNavigationTarget((TargetPosVerse)))
Print(" - NavigateTo tests done.")
Print("-----------------------------------------------")
block:
# 20秒間 GetCurrentDestination[] を計測
for(X := 0..20):
Sleep(1.0)
if(Destination := NPCActionsComp.GetCurrentDestination[]):
Print(" - GetCurrentDestination: {Destination}")
# Destination の位置をマップ内で確認するため、 Prop をテレポート
DestinationUE := FromVector3(Destination)
Rot := (/UnrealEngine.com/Temporary/SpatialMath:)IdentityRotation()
if(CurrentDestinationProp.TeleportTo[DestinationUE, Rot]){}
# 📝: NavigatoToが開始前は失敗。
else:
Print(" - GetCurrentDestination: 失敗. ")
Print(" - GetCurrentDestination tests done.")
Print("===============================================")
ログ
``` ========== GetCurrentDestination_Test ========== - GetCurrentDestination: 失敗. - GetCurrentDestination: 失敗. ----------------------------------------------- - NavigateTo[0] TargetPosition: {Forward = -768.000000, Left = 0.000000, Up = 0.000000} - GetCurrentDestination: 失敗. - GetCurrentDestination: {Forward = -672.000000, Left = 0.000000, Up = 8.000000} - GetCurrentDestination: {Forward = -672.000000, Left = 0.000000, Up = 8.000000} ----------------------------------------------- - NavigateTo[1] TargetPosition: {Forward = 768.000000, Left = 0.000000, Up = 0.000000} - GetCurrentDestination: {Forward = 504.000000, Left = 0.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 504.000000, Left = 0.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 504.000000, Left = 0.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 504.000000, Left = 0.000000, Up = 8.000000} ----------------------------------------------- - NavigateTo[2] TargetPosition: {Forward = 0.000000, Left = -1280.000000, Up = 0.000000} - GetCurrentDestination: {Forward = 720.000000, Left = -72.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 504.000000, Left = -504.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 36.371134, Left = -1224.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 36.371134, Left = -1224.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 0.000000, Left = -1280.000000, Up = 8.000000} - NavigateTo tests done. ----------------------------------------------- - GetCurrentDestination: {Forward = 0.000000, Left = -1280.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 0.000000, Left = -1280.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 0.000000, Left = -1280.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 0.000000, Left = -1280.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 0.000000, Left = -1280.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 0.000000, Left = -1280.000000, Up = 8.000000} - GetCurrentDestination: {Forward = 0.000000, Left = -1280.000000, Up = 8.000000} - GetCurrentDestination tests done. =============================================== ```2. NavigateTo
# Navigate toward the specified navigation target.
NavigateTo<native><public>(Target:navigation_target, ?MovementType:movement_type = external {}, ?ReachRadius:float = external {}, ?AllowPartialPath:logic = external {})<suspends>:result(navigation_action_success_type, navigation_action_error_type)
# 指定されたナビゲーションターゲットに向かって移動します。
NPCを指定したターゲット(座標 または agent)に向かって移動させます。
NavigateTo() でナビゲーションが終了すると、成功であれば成功のタイプが、エラーであればエラーのタイプが戻ってきます。result(navigation_action_success_type, navigation_action_error_type)
結果の確認方法は、ページ下部のコードをご参照ください
付録:検証に使用したログ出力関数
ReachRadius (到達判定半径)
ReachRadius を設定すると、ターゲットの中心まで行かずとも、指定した半径内に入れば「到達した」とみなされます。
-
検証: ReachRadius:=256.0 に設定
-
結果: 黄色の人形(ターゲット)の手前で立ち止まり、次のアクションへ移行しました
検証用コード
# ReachRadius オプションのテスト
NavigateTo_Test2()<suspends>:void=
Print("========== NavigateTo_Test2 ==========")
if(SimE := GetSimulationEntity[]):
NPCActionsComps :=
for(Comp : SimE.FindDescendantComponents(npc_actions_component)){Comp}
if(NPCActionsComp := NPCActionsComps[0]):
Target1 := MakeNavigationTarget(
(/Verse.org/SpatialMath:)vector3{Left:=0.0, Up:=0.0, Forward:=-768.0})
Result1 := NPCActionsComp.NavigateTo(Target1, ?ReachRadius:=256.0)
PrintNavActionResult(Result1)
Target2 := MakeNavigationTarget(
(/Verse.org/SpatialMath:)vector3{Left:=0.0, Up:=0.0, Forward:=768.0})
Result2 := NPCActionsComp.NavigateTo(Target2, ?ReachRadius:=256.0)
PrintNavActionResult(Result2)
Print("===============================================")
ログ
========== NavigateTo_Test2 ==========
- NavigateTo: GetSuccess[] (/Fortnite.com/AI:)navigation_action_success_type.Reached
- NavigateTo: GetError[] Failed.
- NavigateTo: GetSuccess[] (/Fortnite.com/AI:)navigation_action_success_type.Reached
- NavigateTo: GetError[] Failed.
===============================================
AllowPartialPath (不完全なパスの許可)
目的地が「到達不可能な場所(例:屋根の上など)」にある場合の挙動を制御します。
動画では、ターゲットを同じにして、 AllowPartialPath を変更して検証しました。ターゲットは、ピンクの人形がある位置です。
AllowPartialPath = true の場合
「行けるところまで行く」設定と考えられます。
-
挙動: 屋根の下で壁にぶつかるまで移動してから止まりました
-
結果: エラー navigation_action_error_type.Blocked が返されました
AllowPartialPath = false の場合
「完全に到達できないなら動かない」設定と考えられます。
-
挙動: その場から一歩も動きませんでした
-
結果: エラー navigation_action_error_type.Invalid が返されました
検証用コード
# AllowPartialPath オプションの違いをテスト(true)
NavigateTo_Test3a()<suspends>:void=
Print("========== NavigateTo_Test3a ==========")
if(SimE := GetSimulationEntity[]):
NPCActionsComps :=
for(Comp : SimE.FindDescendantComponents(npc_actions_component)){Comp}
if(NPCActionsComp := NPCActionsComps[0]):
FarPos := (/Verse.org/SpatialMath:)vector3{
Left := -192.0,
Up := 1180.0,
Forward := 5784.0
}
Print("--- Test: 遠い地点 (AllowPartialPath = true) ---")
Result := NPCActionsComp.NavigateTo(
MakeNavigationTarget(FarPos),
?AllowPartialPath := true
)
PrintNavActionResult(Result)
Print("===============================================")
# AllowPartialPath のオプション違いをテスト(false)
NavigateTo_Test3b()<suspends>:void=
Print("========== NavigateTo_Test3b ==========")
if(SimE := GetSimulationEntity[]):
NPCActionsComps :=
for(Comp : SimE.FindDescendantComponents(npc_actions_component)){Comp}
if(NPCActionsComp := NPCActionsComps[0]):
FarPos := (/Verse.org/SpatialMath:)vector3{
Left := -192.0,
Up := 1180.0,
Forward := 5784.0
}
Print("--- Test : 遠い地点 (AllowPartialPath = false) ---")
Result := NPCActionsComp.NavigateTo(
MakeNavigationTarget(FarPos),
?AllowPartialPath := false
)
PrintNavActionResult(Result)
Print("===============================================")
ログ
========== NavigateTo_Test3a ==========
--- Test: 遠い地点 (AllowPartialPath = true) ---
- NavigateTo: GetSuccess[] Failed.
- NavigateTo: GetError[] (/Fortnite.com/AI:)navigation_action_error_type.Blocked
===============================================
========== NavigateTo_Test3b ==========
--- Test : 遠い地点 (AllowPartialPath = false) ---
- NavigateTo: GetSuccess[] Failed.
- NavigateTo: GetError[] (/Fortnite.com/AI:)navigation_action_error_type.Invalid
===============================================
3. MovementSpeedMultiplier
# Multiplier on the movement speed (value is clamped between 0.5 and 2).
var MovementSpeedMultiplier<public>:float = external {}
# 移動速度の倍率(値は0.5から2の間にクランプされます)。
検証結果
-
NavigateTo() で移動中でも、動的に速度を変更することが可能です
-
動画では 0.5倍(歩き)と 2.0倍(走り)が交互に切り替わっている様子が確認できます
コード
# MovementSpeedMultiplier の変更テスト
NavigateTo_Test4()<suspends>:void=
Print("========== MovementSpeedMultiplier ==========")
if(SimE := GetSimulationEntity[]):
NPCActionsComps := for(Comp : SimE.FindDescendantComponents(npc_actions_component)){Comp}
if(NPCActionsComp := NPCActionsComps[0]):
race:
block:
# NavigateTo で3箇所に順番に移動する
loop:
for(X := 0..2, NavTargetProp := NavTargetProps[X]):
TargetPos := NavTargetProp.GetTransform().Translation
Print("-----------------------------------------------")
Print(" - NavigateTo[{X}] TargetPosition: {TargetPos}")
NPCActionsComp.NavigateTo(MakeNavigationTarget((TargetPos)))
Sleep(0.5)
Print(" - NavigateTo tests done.")
Print("-----------------------------------------------")
block:
# 3秒おきに移動速度を切り替える
for(X := 0..4):
if(NPCActionsComp.MovementSpeedMultiplier = 0.5):
set NPCActionsComp.MovementSpeedMultiplier = 2.0
else:
set NPCActionsComp.MovementSpeedMultiplier = 0.5
Sleep(2.0)
Print(" - MovementSpeedMultiplier: {NPCActionsComp.MovementSpeedMultiplier}")
Print(" - MovementSpeedMultiplier tests done.")
Print("===============================================")
付録:検証に使用したログ出力関数
NavigateTo() の結果(result) を確認するための、ヘルパー関数です
# NavigateTo の結果をログに出力する関数
# - 成功(GetSuccess[]に成功)の場合、 navigation_action_success_type を出力
# - エラー(GetError[]に成功)の場合、 navigation_action_error_type を出力
PrintNavActionResult(NavActionResult:result(navigation_action_success_type, navigation_action_error_type))<suspends>:void=
if(SuccessType := NavActionResult.GetSuccess[]):
Print(" - NavigateTo: GetSuccess[] " + ToDiagnostic(SuccessType))
else:
Print(" - NavigateTo: GetSuccess[] Failed.")
if(ErrorType := NavActionResult.GetError[]):
Print(" - NavigateTo: GetError[] " + ToDiagnostic(ErrorType))
else:
Print(" - NavigateTo: GetError[] Failed.")
まとめと次回
今回は npc_actions_component の移動機能を中心に検証しました。 特に NavigateTo() の戻り値や AllowPartialPath による挙動の違いは、ゲームロジックを組む上でのポイントになりそうです。
次回(後編)では、残りの関数について検証します。
-
StopNavigation
-
Idle
-
Focus