はじめに
前回、 npc_actions_component の移動関連の機能を検証しました。
今回は後編です。 npc_actions_component の以下の機能について、挙動を検証します
npc_actions_component のこちらを検証します
- StopNavigation: ナビゲーションの中断(終了)
- Idle: アイドル状態への変更
- Focus: 特定の場所・Entity の注視
4. StopNavigation
# Stop navigation.
StopNavigation<native><public>():void
# ナビゲーションを停止します。
検証結果
- NavigateTo() で移動中に、 StopNavigation() を実行すると、NavigateTo() は中断され終了します
- 戻り値としては、navigation_action_error_type.Invalid が返ってきます
NPCは3つの黄色い人形を順番に移動します。
移動中に StopNavigation() を実行すると、次の黄色の人形に向かって移動を開始することが確認できます。
検証用コード・ログ
# StopNavigate のテスト
StopNavigate_Test()<suspends>:void=
Print("========== StopNavigate_Test ==========")
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}")
Result := NPCActionsComp.NavigateTo(MakeNavigationTarget((TargetPos)))
PrintNavActionResult(Result)
Sleep(0.0)
Print(" - NavigateTo test done.")
Print("-----------------------------------------------")
block:
# 2秒おきに StopNavigation を呼び出す
for(X := 0..4):
NPCActionsComp.StopNavigation()
Print(" - StopNavigation: called.")
Sleep(2.0)
Print(" - StopNavigate test done.")
Print("===============================================")
========== StopNavigate_Test ==========
-----------------------------------------------
- NavigateTo[0] TargetPosition: {x=-768.000000,y=0.000000,z=0.000000}
- StopNavigation: called.
- NavigateTo: GetSuccess[] Failed.
- NavigateTo: GetError[] (/Fortnite.com/AI:)navigation_action_error_type.Invalid
-----------------------------------------------
- NavigateTo[1] TargetPosition: {x=768.000000,y=0.000000,z=0.000000}
- StopNavigation: called.
- NavigateTo: GetSuccess[] Failed.
- NavigateTo: GetError[] (/Fortnite.com/AI:)navigation_action_error_type.Invalid
-----------------------------------------------
- NavigateTo[2] TargetPosition: {x=0.000000,y=1280.000000,z=0.000000}
- StopNavigation: called.
- NavigateTo: GetSuccess[] Failed.
- NavigateTo: GetError[] (/Fortnite.com/AI:)navigation_action_error_type.Invalid
-----------------------------------------------
- NavigateTo[0] TargetPosition: {x=-768.000000,y=0.000000,z=0.000000}
- StopNavigation: called.
- NavigateTo: GetSuccess[] Failed.
- NavigateTo: GetError[] (/Fortnite.com/AI:)navigation_action_error_type.Invalid
-----------------------------------------------
- NavigateTo[1] TargetPosition: {x=768.000000,y=0.000000,z=0.000000}
- StopNavigation: called.
- NavigateTo: GetSuccess[] Failed.
- NavigateTo: GetError[] (/Fortnite.com/AI:)navigation_action_error_type.Invalid
-----------------------------------------------
- NavigateTo[2] TargetPosition: {x=0.000000,y=1280.000000,z=0.000000}
- StopNavigate test done.
===============================================
5. Idle
# Stay idle for a specific duration.
Idle<native><public>(?Duration:float = external {})<suspends>:void
# 指定した時間だけアイドル状態を保ちます。
検証結果
- Duration に指定した秒数、アイドル状態になります。
- Duration を指定しない場合、外部から中断されるまでアイドル状態が継続します(永久に待ち続けるのかもしれません)。検証では60秒経過しても終了しませんでした
アクションの競合
NavigateTo() で移動中に、 Idle() を実行すると、NavigateTo() は中断(終了)されます。また、 Idle() 実行中に NavigateTo() を実行すると、 Idle() は中断(終了)されます
検証用コード・ログ
# Idle のテスト1。NavigateTo() 実行中に Idle() を呼び出す⇒ NavigateTo() は終了する
Idle_Test1(Agent:agent)<suspends>:void=
Print("========== Idle_Test1 ==========")
if(SimE := GetSimulationEntity[]):
NPCActionsComps := for(Comp : SimE.FindDescendantComponents(npc_actions_component)){Comp}
if(NPCActionsComp := NPCActionsComps[0]):
sync:
block:
NavStartTime := GetSimulationElapsedTime()
Print(" - NavigateTo: start. NavStartTime: {NavStartTime}")
Result := NPCActionsComp.NavigateTo(MakeNavigationTarget(Agent))
NavEndTime := GetSimulationElapsedTime()
Print(" - NavigateTo: end. NavElapsedTime: {NavEndTime - NavStartTime} 秒. NavEndTime: {NavEndTime}")
PrintNavActionResult(Result)
block:
Sleep(2.0) #NavigateTo開始後に Idle を呼び出すため、少し待機
IdleStartTime := GetSimulationElapsedTime()
Print(" - Idle: start. IdleStartTime: {IdleStartTime}")
NPCActionsComp.Idle(?Duration:=2.0)
IdleEndTime := GetSimulationElapsedTime()
Print(" - Idle: end. IdleElapsedTime: {IdleEndTime - IdleStartTime} 秒. IdleEndTime: {IdleEndTime}")
Print("===============================================")
# Idle のテスト2。 Idle() 実行中に NavigateTo() を呼び出す ⇒ Idle() は終了する
Idle_Test2(Agent:agent)<suspends>:void=
Print("========== Idle_Test2 ==========")
if(SimE := GetSimulationEntity[]):
NPCActionsComps := for(Comp : SimE.FindDescendantComponents(npc_actions_component)){Comp}
if(NPCActionsComp := NPCActionsComps[0]):
sync:
block:
Sleep(2.0) #Idle開始後に NavigateTo を呼び出すため、少し待機
NavStartTime := GetSimulationElapsedTime()
Print(" - NavigateTo: start. NavStartTime: {NavStartTime}")
Result := NPCActionsComp.NavigateTo(MakeNavigationTarget(Agent))
NavEndTime := GetSimulationElapsedTime()
Print(" - NavigateTo: end. NavElapsedTime: {NavEndTime - NavStartTime} 秒. NavEndTime: {NavEndTime}")
PrintNavActionResult(Result)
block:
IdleStartTime := GetSimulationElapsedTime()
Print(" - Idle: start. IdleStartTime: {IdleStartTime}")
NPCActionsComp.Idle(?Duration:=10.0)
IdleEndTime := GetSimulationElapsedTime()
Print(" - Idle: end. IdleElapsedTime: {IdleEndTime - IdleStartTime} 秒. IdleEndTime: {IdleEndTime}")
Print("===============================================")
========== Idle_Test1 ==========
- NavigateTo: start. NavStartTime: 8914.769514
- Idle: start. IdleStartTime: 8916.806538
- NavigateTo: end. NavElapsedTime: 2.037024 秒. NavEndTime: 8916.806538
- NavigateTo: GetSuccess[] Failed.
- NavigateTo: GetError[] (/Fortnite.com/AI:)navigation_action_error_type.Invalid
- Idle: end. IdleElapsedTime: 2.003690 秒. IdleEndTime: 8918.810228
===============================================
========== Idle_Test2 ==========
- Idle: start. IdleStartTime: 9248.211967
- NavigateTo: start. NavStartTime: 9250.249026
- Idle: end. IdleElapsedTime: 2.037058 秒. IdleEndTime: 9250.249026
- NavigateTo: end. NavElapsedTime: 6.578599 秒. NavEndTime: 9256.827625
- NavigateTo: GetSuccess[] (/Fortnite.com/AI:)navigation_action_success_type.Reached
- NavigateTo: GetError[] Failed.
===============================================
6. Focus
# 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
# 指定した位置を見ます。LockFocusがfalseの場合、NPCがその位置を向いた時点でアクションが完了します。
検証結果
- 指定した位置を、NPCが体ごと向けて注視します。
- LookFocus のデフォルトは true です。true の場合、注視したあとも処理は終了せず、Focus() は中断されるまで実行され続けます
- 後続処理を実行するには、 LookFocus を false で設定します
NPCは3つの黄色い人形を順番に注視します。
LockFocus = false にすることで、 NPCが対象を向いた時点で Focus() が終了し、次の人形を注視する処理へ移行しています。
検証用コード・ログ
# Focus のテスト1。 座標に注視させる
Focus_Test1()<suspends>:void=
Print("========== Focus_Test1 ==========")
if(SimE := GetSimulationEntity[]):
NPCActionsComps := for(Comp : SimE.FindDescendantComponents(npc_actions_component)){Comp}
if(NPCActionsComp := NPCActionsComps[0]):
loop:
for(X := 0..2, NavTargetProp := NavTargetProps[X]):
TargetPosUE := NavTargetProp.GetTransform().Translation
TargetPos := FromVector3(TargetPosUE)
Print("-----------------------------------------------")
Print(" - Focus[{X}]: start. TargetPosition: {TargetPos}")
# NPCActionsComp.Focus(TargetPos) # LockFocus のデフォルトは true。注視後も Focus() は終了しない
NPCActionsComp.Focus(TargetPos, ?LockFocus:=false) # 注視後に後続処理へ進む
# NPCActionsComp.Focus(TargetPos, ?LockFocus:=true) # 注視後も Focus() は終了しない
Print(" - Focus[{X}]: end. ")
Sleep(1.0)
Print("===============================================")
========== Focus_Test1 ==========
-----------------------------------------------
- Focus[0]: start. TargetPosition: {Forward = -768.000000, Left = 0.000000, Up = 0.000000}
- Focus[0]: end.
-----------------------------------------------
- Focus[1]: start. TargetPosition: {Forward = 768.000000, Left = 0.000000, Up = 0.000000}
- Focus[1]: end.
-----------------------------------------------
- Focus[2]: start. TargetPosition: {Forward = 0.000000, Left = -1280.000000, Up = 0.000000}
- Focus[2]: end.
-----------------------------------------------
- Focus[0]: start. TargetPosition: {Forward = -768.000000, Left = 0.000000, Up = 0.000000}
- Focus[0]: end.
-----------------------------------------------
- Focus[1]: start. TargetPosition: {Forward = 768.000000, Left = 0.000000, Up = 0.000000}
- Focus[1]: end.
-----------------------------------------------
- Focus[2]: start. TargetPosition: {Forward = 0.000000, Left = -1280.000000, Up = 0.000000}
- Focus[2]: end.
-----------------------------------------------
- Focus[0]: start. TargetPosition: {Forward = -768.000000, Left = 0.000000, Up = 0.000000}
- Focus[0]: end.
-----------------------------------------------
- Focus[1]: start. TargetPosition: {Forward = 768.000000, Left = 0.000000, Up = 0.000000}
- Focus[1]: end.
-----------------------------------------------
- Focus[2]: start. TargetPosition: {Forward = 0.000000, Left = -1280.000000, Up = 0.000000}
- Focus[2]: end.
-----------------------------------------------
- Focus[0]: start. TargetPosition: {Forward = -768.000000, Left = 0.000000, Up = 0.000000}
- Focus[0]: end.
7. Focus
# 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
# 指定したエンティティを見ます。LockFocusがfalseの場合、NPCがそのエンティティを向いた時点でアクションが完了します。
検証結果
- 6.Focus(Location) と同じですが、注視する対象に entity を指定します
プレイヤー(player) は entity なので、Focus() で注視する対象に設定することが可能です。
検証用コード
# Focus のテスト2。 agent に注視させる
Focus_Test2(Agent:agent)<suspends>:void=
Print("========== Focus_Test2 ==========")
if(SimE := GetSimulationEntity[]):
NPCActionsComps := for(Comp : SimE.FindDescendantComponents(npc_actions_component)){Comp}
if:
NPCActionsComp := NPCActionsComps[0]
TargetEntity := Agent.GetFortCharacter[].GetEntity[]
then:
loop:
NPCActionsComp.Focus(TargetEntity, ?LockFocus:=false) # 注視後に後続処理へ進む
# NPCActionsComp.Focus(TargetEntity, ?LockFocus:=true) # 注視後も Focus() は終了しない
Sleep(1.0)
Print("===============================================")
おわりに
全2回の連載を通して、新しいVerseAPI、 npc_actions_component の主要な機能について、コードを動かしながらその挙動を検証することができました。
個人的な感想になりますが、NavigateTo() で移動している最中に Idle() を実行すると、ナビゲーションが一時停止ではなく完全に終了してしまうという挙動は、大変興味深い発見でした。てっきりアイドル状態の後に移動が再開されるのかと予想していたので、アクションが競合するのを知るよい機会になりました。