メディックは、多くのゲームで一般的なキャラクターのアーキタイプです。 メディックの任務は周囲にいるキャラクターを回復させることで、ダメージを受けたチームメイトの回復を支援します。 メディックはゲームによってさまざまな役割を果たします。たとえば、病院で患者に治療を提供する医師、チーム戦を支援し、回復も担当するコンバット メディック、あるいは誰でも回復させる中立的な立場などです。
この例で作成するメディック キャラクターは、一連のロジック ルールに従います。
- アイドル:
- エージェントの回復を開始する
- 回復ループ
- エージェントまで移動する
メディックはアイドル状態から開始して、エージェントが回復ゾーンに入るまで巡回し、 回復ゾーンに入ったエージェントはメディックの回復キューに追加されます。 メディックは次に回復が必要なエージェントを追跡する必要があります。キューは先入れ先出しのデータ構造になっているため、この目的に役立つデータ構造を提供します。 これは、最初に回復ゾーンに入ったキャラクターが最初に回復されることを意味します。
メディックが次に回復の必要なエージェントを見つけると、まず最初にエージェントのヘルスが回復のしきい値を下回っているかどうかを確認します。 下回っている場合は、エージェントのヘルスがしきい値に達するまで、またはエージェントが回復ゾーンから出ていくまで、一定の速度で回復を開始します。 回復中、メディックはエージェントに向かって移動を続け、彼らから離れないようにします。 エージェントのヘルスがしきい値まで回復すると、メディックは次に回復させるエージェントを見つけて、このプロセスを再び開始します。 回復するエージェントがいない場合、メディックはアイドル状態に戻ります。
メディック NPC のロジックは、次の有限ステート マシンを使用して視覚化できます。有限ステート マシンの詳細については、「NPC の動作を理解する」を参照してください。
このガイドを読み終えると、NPC 動作スクリプトを使用して、周囲にいる他のキャラクターのヘルスが特定のしきい値を下回ると回復させるカスタム メディック キャラクターを作成する方法を習得できます。 完全なスクリプトは、参照用としてこのガイドの末尾に記載されています。
新規 NPC 動作スクリプトを作成する
独自の NPC メディック キャラクターの作成を開始するには、「medic_example」という名前の新規 NPC 動作スクリプトを作成します。独自の NPC 動作スクリプトを作成する方法については、「独自の NPC 動作を作成する」を参照してください。Visual Studio Code で Verse ファイルを開きます。
次の手順に従って、UEFN で周囲のプレイヤーを回復させるメディック キャラクターをスポーンする NPC 動作スクリプトを作成します。
回復キューを実装する
NPC 動作スクリプトは、キャラクター移動とデバッグ ビジュアライゼーションに使用されるいくつかの値から始まります。 このスクリプトでは、これらすべてが必要となるわけではないため、不要なコードをこの時点で削除します。
-
medic_exampleクラス定義の先頭で、OnBegin()より前の値を削除します。メディック キャラクターはキャラクターまでの移動を待機せず、代わりに回復中はキャラクターを追いかけます。この例ではデバッグ値は必要ありません。メディックがキャラクターを回復するときは、他のオブジェクトを使用して表示します。 -
OnBegin()では、最初のif文でキャラクターのインターフェースを取得した後、then文内のコードを削除します。メディック キャラクターはスポーン後にポイント間をループする必要はありません。代わりにスポーン地点を巡回して、回復するキャラクターを待機します。 -
DrawDebugLocation()関数とDrawDebugLookAt()関数を削除します。この例ではデバッグ値を使用しないため、それらを使用する関連の関数も必要ありません。
不要なコードを削除したら、メディック キャラクターの作成を開始できます。
-
medic_exampleクラス定義の先頭に、次の値を追加します。-
編集可能な float の
HealingThreshold。キャラクターが回復を受けるには、ヘルスがこのしきい値を下回っている必要があります。# キャラクターを回復する条件となる HP のしきい値。 @editable HealingThreshold:float = 50.0 -
編集可能な float の
HealingDelayを追加します。これは、キャラクターを回復するときの各回復インスタンス間の待機時間です。希望するメディックの回復速度に応じて、この値を変更します。# キャラクターを回復する条件となる HP のしきい値。 @editable HealingThreshold:float = 50.0 # キャラクターを回復するまでの待機時間 @editable HealingDelay:float = 1.5 -
編集可能な float の
HealingAmount。これは、回復インスタンスごとにキャラクターを回復するヘルスの量です。メディック NPC がキャラクターを回復すると、HealingDelayの秒数おきにHealingAmountの量のヘルスが回復します。# キャラクターを回復するまでの待機時間 @editable HealingDelay:float = 1.5 # 回復インスタンスごとにキャラクターを回復する量 @editable HealingAmount:float = 5.0 -
編集可能なミューテーター ゾーンの
HealVolume。これは、回復を受けるキャラクターが入るボリュームです。ミューテーター ゾーンにはメディックがサブスクライブして回復を必要とする可能性があるキャラクターをチェックできるAgentEntersEventがあるため、この例ではミューテーター ゾーンを使用します。# 回復インスタンスごとにキャラクターを回復する量 @editable HealingAmount:float = 5.0 # 回復を受けるためにキャラクターが入るボリューム。 @editable HealVolume:mutator_zone_device = mutator_zone_device{} -
編集可能な VFX スポナーの
VFXSpawner。コードが機能していることを確認するにはビジュアル フィードバックが重要となるため、VFX スポナーを使用してキャラクターが回復しているときのエフェクトをスポーンします。# 回復を受けるためにキャラクターが入るボリューム。 @editable HealVolume:mutator_zone_device = mutator_zone_device{} # キャラクターが回復しているときに VFX を再生するための VFX スポナー。 @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {} -
「
AgentToFollow」という名前のオプションのagent変数。これは、メディックが回復中に追いかけるキャラクターへの参照を格納します。# キャラクターが回復しているときに VFX を再生するための VFX スポナー。 @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {} # 回復中に追いかけるエージェント var AgentToFollow:?agent = false -
「
AgentsToHeal」という名前のエージェントの可変キュー。複数のキャラクターが回復を必要としている場合、メディックはHealVolumeに入った順番に基づいてキャラクターを回復させます。次の手順でキュー コードを設定します。キューのデータ構造の詳細については、「Verse でのスタックとキュー」を参照してください。# 回復中に追いかけるエージェント var AgentToFollow:?agent = false # 複数のエージェントが回復ボリュームにいる場合の回復するエージェントのキュー。 var AgentsToHeal<public>:queue(agent) = queue(agent){} -
float 変数の
UpdateRateSeconds。これは、HealVolumeとVFXSpawnerの位置を更新するまで待機する時間です。# 複数のエージェントが回復ボリュームにいる場合の回復するエージェントのキュー。 var AgentsToHeal<public>:queue(agent) = queue(agent){} # HealVolume と VFXSpawner の位置を更新する速度を指定するために使用されます UpdateRateSeconds<private>:float = 0.1
-
-
AgentsToHealキューを実装するには、この手順の最後に提供されるコードを使用します。- Verse Explorer に戻って目的のプロジェクト名を右クリックし、[Add new Verse file to project (新規 Verse ファイルをプロジェクトに追加)] を選択して [Create Verse Script (Verse スクリプトを作成)] ウィンドウを開きます。
-
[Create Verse Script] ウィンドウで、スクリプトとして選択するために [Verse Class (Verse クラス)] をクリックします。
-
[Class Name (クラス名)] フィールドのテキストを「
queue」に変更することで Verse クラスに名前を付けます。 -
[Create (作成)] をクリックし、Verse ファイルを作成します。
-
Verse Explorer 内で Verse ファイルの名前をダブルクリックして Visual Studio Code で開きます。
-
「
queue」ファイルのコードを次のコードに置き換えます。このコードでは、リスト データ構造を使用してtype型の汎用キューを実装します。これは パラメトリック型 の例です。キューの実装は作成元の型に関係なく機能します。この例では、キャラクターのキューを使用するため、medic_exampleにおけるキューの定義はqueue(agent)になります。list(t:type) := class: Data:t Next:?list(t) queue<public>(t:type) := class<internal>: 要素<internal>:?list(t) = false サイズ<public>:int = 0 Enqueue<public>(NewElement:t):queue(t) = queue(t): Elements := option: list(t): Data := NewElement Next := Elements Size := Size + 1 Dequeue<public>()<decides><transacts>:tuple(queue(t), t) = List := Elements? (queue(t){Elements := List.Next, Size := Size - 1}, List.Data) 前<public>()<decides><transacts>:t = Elements?.Data CreateQueue<public><constructor>(InData:t where t:type) := queue(t): Elements := option: list(t): Data := InData Next := false Size := 1 -
Verse スクリプトを個別のフォルダに格納することにより、整理しやすくなるだけでなく、一般的に使用されるファイルを簡単に参照できるようにもなります。その例として、このプロジェクトに NPC の動作を格納するフォルダを作成します。Visual Studio Code で [New Folder (新規フォルダ)] ボタンをクリックして UEFN プロジェクトに新規フォルダを作成します。このフォルダに
npc_behaviorsという名前を付けます。「medic_example」 Verse ファイルをこの新規フォルダにドラッグします。
これで、medic_example のコードが正しくコンパイルするようになります。
ボリューム内でキャラクターを回復する
負傷したキャラクターが HealVolume に入ると、そのキャラクターのヘルスが HealingThreshold に満たない場合はメディック キャラクターが回復を始める必要があります。キャラクターのヘルスが HealingThreshold を超えると、メディックはそのキャラクターの回復を終了し、回復を必要とする次のキャラクターのところへ移動します。複数のキャラクターがいる場合、メディックは HealVolume に入った順番にキャラクターを回復する必要があります。次の手順に従い、キャラクターが HealVolume に入ると回復を行います。
-
「
medic_example」ファイルに戻り、then文の後のOnBegin()でloopを開始します。loop内で、AgentsToHealキューからDequeue()関数の結果を取得し、それをDequeueResult変数に保存します。then: loop: # キューで次に回復するエージェントを取得します。回復するエージェントがいる場合、AgentToHeal を呼び出して回復します。 # 回復するエージェントがいない場合、エージェントが HealVolume に入るまで待機します if: DequeueResult := AgentsToHeal.Dequeue[] -
DequeueResult変数は、最初の要素が削除されたAgentsToHealキューのコピーと、キューの先頭にいるエージェントの両方を返すtupleです。AgentsToHealをタプルの最初の値に設定して更新し、2 番目の値をAgentToHealとして保存します。if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) -
回復するエージェントが現れたら、
HealVolumeにいる間に回復を開始する必要があります。これを処理するために「HealCharacter()」という名前の新しい関数を定義します。「HealCharacter()」という名前の新しい関数をmedic_exampleクラス定義に追加します。この関数は、関数の引数としてメディック キャラクターのNavigatableインターフェースとFocusableインターフェースの両方のAgentToHealを取得します。キャラクターの回復時にいくつかの非同期タスクを実行する必要があるため、この関数に<suspends>モディファイアを追加します。# キャラクターを回復したら、HealingDelayAmount の時間だけ待機します。 # キャラクターのヘルスが HealingThreshold に達するか # キャラクターが HealVolume を離れると終了します。 HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= -
HealCharacterで、IsInVolume[]を呼び出してAgentToHealを引数として渡すことで、AgentToHealがボリューム内にいるかどうかを確認します。エージェントがボリューム内にいる場合は、彼らの回復を開始できます。すべての回復可能なエージェントは、エージェントのfort_characterの一部であるhealthfulインターフェースを実装します。エージェントのfort_characterを取得し、それをCharacterToHeal値に保存します。HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # 回復は、そのキャラクターが HealVolume 内にいる場合にのみ行います if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] -
キャラクターを回復する準備ができると、メディックは回復対象のキャラクターのそばから離れないことを確認する必要があります。
MakeNavigationTargetを使用してAgentToHealからnavigation_targetを作成し、それをNavigationTarget変数に保存します。次にbranch文で、NPC のnavigatableインターフェースを使用してNavigateTo()関数を呼び出し、メディックをAgentToHealまで移動させます。branch関数でもMaintainFocus()関数を呼び出し、メディックが確実にAgentToHealに集中できるようにします。このコンテキストでbranch文を使用すると、同時にNavigateTo()とMaintainFocus()の両方を非同期に実行することができ、branchの後のコードをすぐに実行することができます。branch 式の詳細については、「Verse の branch」ページ を参照してください。# 回復は、そのキャラクターが HealVolume 内にいる場合にのみ行います if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Character is in volume, starting healing") NavigationTarget := MakeNavigationTarget(AgentToHeal) branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) -
VFXSpawnerを有効にして、メディックがキャラクターを回復するときに VFX を再生します。次にdefer式で、VFXSpawnerを無効にします。VFXSpawnerを無効にするコードはdefer式にあるため、現在のスコープが終了するまで実行されません。この場合、コードは関数が終了したときにのみ実行されることを意味するため、関数内で一番最後に実行されることが保証されます。defer 式の詳細については、defer のページ を参照してください。branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() -
CharacterToHealを回復するとき、2 つの条件のうちのいずれかが発生した場合に回復を終了する必要があります。キャラクターのヘルスが回復してHealingThresholdを超えた場合か、キャラクターがHealVolumeから出た場合です。これを達成するには、race式を使用します。race式をHealVolume.AgentExitsEvent.のloopとAwait()の間に設定します。branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() race: loop: HealVolume.AgentExitsEvent.Await() -
loop内で、GetHealth()を使用してキャラクターの現在のヘルスを取得し、それをCurrentHealth値に保存します。次にif文で、CurrentHealthにHealingAmountを足した値がHealingThresholdより大きいかどうかをチェックします。大きい場合、メディックは回復を終了し、ループをbreakで終了する必要があります。ただし、キャラクターの現在のヘルスが回復しきい値よりわずかに少ない場合は、回復しきい値まで回復する必要があります。CurrentHealthがHealingThresholdより小さい値かどうかをチェックする 2 つ目のif文を最初の if 文の中に追加します。この場合、キャラクターのヘルスをHealingThresholdに設定します。race: loop: CurrentHealth := CharacterToHeal.GetHealth() if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Character has reached HealingThreshold, stopping healing") break HealVolume.AgentExitsEvent.Await() -
そうしない場合は、
CurrentHealthにHealingAmountを足した値がHealingThresholdより小さい場合、キャラクターのヘルスをCurrent HealthにHealingAmountを足した値にします。if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Character has reached HealingThreshold, stopping healing") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount) -
loopの最後で、HealingDelayの時間だけスリープします。このスリープを行わないと、キャラクターはシミュレーションの更新ごとに回復されるため、HealingDelayですぐに回復されることを防ぎます。完成したHealCharacter()コードは次のようになります。# キャラクターを回復したら、HealingDelayAmount の時間だけ待機します。 # キャラクターのヘルスが HealingThreshold に達するか # キャラクターが HealVolume を離れると終了します。 HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # 回復は、そのキャラクターが HealVolume 内にいる場合にのみ行います if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Character is in volume, starting healing") NavigationTarget := MakeNavigationTarget(AgentToHeal) branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() race: loop: CurrentHealth := CharacterToHeal.GetHealth() if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Character has reached HealingThreshold, stopping healing") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount) Sleep(HealingDelay) HealVolume.AgentExitsEvent.Await() -
OnBegin()に戻り、loop内のthen式で、AgentToHeal、Navigableインターフェース、およびFocusableインターフェースを渡してHealCharacter()を呼び出します。if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Dequeued the next agent to heal") HealCharacter(AgentToHeal, Navigatable, Focusable) -
常にメディックの近くに回復するキャラクターがいるとは限りません。また、
AgentsToHealキューにエージェントがいない場合、Dequeue[]関数は失敗します。これを処理するには、else文をloopの末尾に追加します。このif文内で、HealingDelayの時間だけSleep()を呼び出し、次にHealVolume.AgentEntersEventに対してAwait()を実行します。これにより、メディック キャラクターがAgentsToHealキューでDequeue[]を呼び出し続けることがなくなり、代わりに新しいキャラクターがHealVolumeに入ってくるまでループの再開を待機するようになります。完成したループは次のようになります。loop: # キューで次に回復するエージェントを取得します。回復するエージェントがいる場合、AgentToHeal を呼び出して回復します。 # 回復するエージェントがいない場合、エージェントが HealVolume に入るまで待機します if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Dequeued the next agent to heal") HealCharacter(AgentToHeal, Navigatable, Focusable) else: Print("AgentsToHeal is empty!") Sleep(HealingDelay) HealVolume.AgentEntersEvent.Await()
キャラクターの回復ボリュームへの出入りを追跡する
キャラクターが HealVolume に出入りするタイミングを取得するには、HealVolume の AgentEntersEvent と AgentExitsEvent の両方を新しい関数にサブスクライブします。
-
「
medic_example」という名前の新しい関数をOnAgentEnters()クラス定義に追加します。この関数は、HealVolumeに入ったばかりのエージェントを取得してAgentsToHealキューに追加します。OnAgentEnters(EnteredAgent:agent):void= Print("Agent entered the heal volume") -
OnAgentEnters()で、ボリューム内のエージェントがメディック キャラクターではないことをチェックします。メディック キャラクターではない場合、EnteredAgentを使用してEnqueue[]を呼び出した結果にAgentsToHealキューを設定します。完成したOnAgentEnters()関数は次のようになります。OnAgentEnters(EnteredAgent:agent):void= Print("Agent entered the heal volume") if (EnteredAgent <> GetAgent[]): set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent) -
エージェントが
HealVolumeを離れても、AgentsToHealキューから削除する必要はありません。これは、OnBegin()のループがすでにループ内でDequeue[]を呼び出しているためです。ただし、この例ではエージェントがボリュームを離れたときにコードを実行したいため、ここでそのための関数を設定します。「OnAgentExits()」という名前の新しい関数をmedic_exampleクラス定義に追加します。OnAgentExits(ExitAgent:agent):void= Print("Agent exited the heal volume") -
OnBegin()内で、HealVolumeのAgentEntersEventとAgentExitsEventをOnAgentEntersとOnAgentExitsにそれぞれサブスクライブします。これは無効の状態から開始する必要があるため、ここはキャラクター スポナーでDisable()を呼び出すのに適しています。OnBegin<override>()<suspends>:void= Print("Hello, AI!") VFXSpawner.Disable() HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters) HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
メディックと回復ボリュームを移動する
メディック キャラクターが移動すると、HealVolume も現在のメディックの位置と一致するように移動させる必要があります。VFXSpawner についても同様です。そのためには、新しい関数である DeviceFollowCharacter() を使用します。
-
「
DeviceFollowCharacter()」という名前の新しい関数をmedic_exampleクラス定義に追加します。この関数は継続的に仕掛けの位置を更新するために非同期的に実行する必要があります。そのため、<suspends>モディファイアを追加します。DeviceFollowCharacter()<suspends>:void= -
DeviceFollowCharacter()関数内で、最初にGetAgent[]を使用してエージェントを取得し、次にGetFortCharater[]を呼び出してメディックのfort_characterを取得します。DeviceFollowCharacter()<suspends>:void= if: # この動作が関連付けられているエージェント (AI キャラクター) を取得します。 Agent := GetAgent[] # エージェントの fort_character インターフェースを取得してフォートナイトのキャラクター固有の動作、イベント、関数、インターフェースにアクセスします。 Character := Agent.GetFortCharacter[] -
次に、
HealVolumeとVFXSpawnerをCharacterの位置まで継続的に移動させる必要があります。これを行うには、両方の仕掛けでMoveTo()をループさせます。loopを開始してCharacterのトランスフォームを取得し、それをCharacterTransform変数に保存します。if: # この動作が関連付けられているエージェント (AI キャラクター) を取得します。 Agent := GetAgent[] # エージェントの fort_character インターフェースを取得してフォートナイトのキャラクター固有の動作、イベント、関数、インターフェースにアクセスします。 Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform() -
MoveTo()をVFXSpawnerとHealVolumeの両方で呼び出し、それらをCharacterTransform.TranslationとCharacterTransform.Rotationに移動させます。 期間をUpdateRateSecondsの秒数に設定します。最後に、シミュレーションの更新のたびに仕掛けの位置が更新されないように、UpdateRateSecondsの時間だけSleep()を呼び出します。シミュレーションの更新のたびに仕掛けの位置が更新されると、仕掛けの動きがぎくしゃくする可能性があります。 完成したDeviceFollowCharacter()コードは次のようになります。DeviceFollowCharacter()<suspends>:void= if: # この動作が関連付けられているエージェント (AI キャラクター) を取得します。 Agent := GetAgent[] # エージェントの fort_character インターフェースを取得してフォートナイトのキャラクター固有の動作、イベント、関数、インターフェースにアクセスします。 Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform() VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds) HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds) Sleep(UpdateRateSeconds) -
OnBegin()内で、キャラクター インターフェースを保存するif文とループの間で、DeviceFollowCharacter()のインスタンスをスポーンします。
キャラクターをレベルに追加する
-
「Medic」という名前の新しい NPC キャラクター定義を作成します。新しい NPC キャラクター定義をクリックし、[NPC Character Definition (NPC キャラクター定義)] 画面を開きます。
-
[NPC Character Definition] 画面で、次のプロパティを変更します。
-
[NPC Character Type (NPC キャラクターのタイプ)] で、[Type (タイプ)] を [Guard (ガード)] に設定します。[Guard] インターフェースを使用すると、ガードが警戒したり疑っている場合のイベントや、ガードを雇用して味方として活用するなど、ガードに固有のキャラクターの機能にアクセスできます。また、ガードは武器を装備できます。一方、カスタムおよび野生動物タイプのキャラクターは、現時点では装備できません。さらに、キャラクターの名前は [Name (名前)] タブで変更できます。
-
[NPC Character Behavior (NPC キャラクター動作)] で、[Behavior (動作)] を [Verse Behavior (Verse 動作)] に設定します。続いて、[NPC Behavior Script (NPC 動作スクリプト)] を
medic_exampleに設定します。キャラクターは、引き続き [Guard] インターフェースから機能にアクセスできますが、OnBeginとOnEndの間で Verse スクリプトを使用して実行すべきアクションを決定します。 -
[Modifiers (モディファイア)] タブの [Guard Spawn Modifier (ガード スポーン モディファイア)] で、[Cosmetic (ビジュアル)] タブをクリックし、キャラクターのビジュアル面のアピアランスを変更します。既存のビジュアルから選択することも、[Character Cosmetic Retargeting (キャラクターのビジュアル リターゲティング)] を有効にしてカスタム モデルを使用することもできます。なお、ガードおよびカスタム タイプのキャラクターのみがキャラクターのビジュアル リターゲティングを利用できますが、野生動物は利用できません。キャラクターのモディファイアや、さまざまなキャラクター タイプに該当するモディファイアの詳細については、「キャラクター定義」のページを参照してください。
-
-
NPC キャラクター定義を保存します。コンテンツ ブラウザ で、NPC キャラクター定義をレベル内にドラッグします。これにより、新しいキャラクター スポナーが自動的に作成され、そのスポナーに NPC キャラクター定義が割り当てられます。
-
ミューテーター ゾーンを 1 つと VFX スポナーの仕掛けを 1 つ、レベルにドラッグします。
-
キャラクター スポナーを選択します。アウトライナー で、次の手順に従って [User Options (ユーザー オプション)] を選択します。
-
[AIBehavior Script override (AI 挙動スクリプト上書き)] を
medic_exampleスクリプトに設定します。アウトライナーで AI 挙動スクリプトをオーバーライドすると、レベル内の仕掛けを参照することができます。また、HealVolume と VFXSpawner を割り当てるには、この機能が必要になります。 -
HealVolume をミューテーター ゾーンに、VFXSpawner をレベル内に配置した VFX スポナーに設定します。
-
-
ミューテーター ゾーンを選択します。アウトライナー の [User Options] で、[Zone Visible During Game (ゲーム中にゾーンを表示)] を True に設定します。これは、
HealVolumeの位置と、メディック キャラクターとともに移動する様子を視覚化するのに役立ちます。 -
VFX スポナーを選択します。アウトライナー の [User Options] で、[Visual Effect (ビジュアル エフェクト)] を任意のエフェクトに設定します。この例では、治癒を表すために [Bubbles (バブル)] エフェクトを使用していますが、花火や火花などの別のエフェクトを使用することもできます。キャラクターのニーズに合わせてビジュアル エフェクトを変更してください。
-
UEFN ツールバーの [Launch Session (セッションを開始)] をクリックしてレベルをプレイテストします。プレイテストでは、メディック キャラクターはミューテーター ゾーンに入ってくる負傷したキャラクターを回復させる必要があります。キャラクターを回復させるときは VFX が再生され、メディックは回復させているキャラクターを追いかけてフォーカスする必要があります。
完全なスクリプト
HP が特定のしきい値を下回るキャラクターを回復させる NPC キャラクター用の完全なスクリプトを次に示します。
medic_example.verse
using { /Fortnite.com/AI}
using { /Fortnite.com/Characters}
using { /Fortnite.com/Devices}
using { /Verse.org/Colors}
using { /Verse.org/Random}
using { /Verse.org/Simulation}
using { /UnrealEngine.com/Temporary/Diagnostics}
using { /UnrealEngine.com/Temporary/SpatialMath}
# NPC 定義またはキャラクター スポナーの仕掛けの挙動スクリプト上書き内で使用できる Verse で作成した NPC の動作。
medic_example<public> := class(npc_behavior):
# キャラクターを回復する条件となる HP のしきい値。
@editable
HealingThreshold:float = 50.0
# キャラクターを回復するまでの待機時間
@editable
HealingDelay:float = 1.5
# 回復インスタンスごとにキャラクターを回復する量
@editable
HealingAmount:float = 5.0
# 回復を受けるためにキャラクターが入るボリューム。
@editable
HealVolume:mutator_zone_device = mutator_zone_device{}
# キャラクターが回復しているときに VFX を再生するための VFX スポナー。
@editable
VFXSpawner:vfx_spawner_device = vfx_spawner_device {}
# 回復中に追いかけるエージェント
var AgentToFollow:?agent = false
# 複数のエージェントが回復ボリュームにいる場合の回復するエージェントのキュー。
var AgentsToHeal<public>:queue(agent) = queue(agent){}
# HealVolume と VFXSpawner の位置を更新する速度を指定するために使用されます
UpdateRateSeconds<private>:float = 0.1
OnBegin<override>()<suspends>:void=
VFXSpawner.Disable()
HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters)
HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
if:
# この動作が関連付けられているエージェント (AI キャラクター) を取得します。
Agent := GetAgent[]
# エージェントの fort_character インターフェースを取得してフォートナイトのキャラクター固有の動作、イベント、関数、インターフェースにアクセスします。
Character := Agent.GetFortCharacter[]
# キャラクターのナビゲート可能なインターフェースを取得し、キャラクターの移動先となる特定のターゲットを設定します。
Navigatable := Character.GetNavigatable[]
# キャラクターの focus_interface を取得し、特定のターゲットまで移動した後、フォーカスするように設定します。
Focusable := Character.GetFocusInterface[]
then:
# NPC キャラクターを追い続けるように HealVolume と VFXSpawner を設定します
spawn{DeviceFollowCharacter()}
loop:
# キューで次に回復するエージェントを取得します。回復するエージェントがいる場合、AgentToHeal を呼び出して回復します。
# 回復するエージェントがいない場合、エージェントが HealVolume に入るまで待機します
if:
DequeueResult := AgentsToHeal.Dequeue[]
set AgentsToHeal = DequeueResult(0)
AgentToHeal := DequeueResult(1)
then:
PrintNPCB("Dequeued the next agent to heal")
HealCharacter(AgentToHeal, Navigatable, Focusable)
else:
PrintNPCB("AgentsToHeal is empty!")
Sleep(HealingDelay)
HealVolume.AgentEntersEvent.Await()
else:
# コードがここに来る場合、エージェントとそのインターフェースの収集時に何らかのエラーが発生しています
PrintNPCB( "Error in NPC Behavior Script on NPC Setup",
?Duration := 6.0,
?TextColor := NamedColors.Red )
# キャラクターを回復したら、HealingDelayAmount の時間だけ待機します。
# キャラクターのヘルスが HealingThreshold に達するか
# キャラクターが HealVolume を離れると終了します。
HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void=
# 回復は、そのキャラクターが HealVolume 内にいる場合にのみ行います
if:
HealVolume.IsInVolume[AgentToHeal]
CharacterToHeal := AgentToHeal.GetFortCharacter[]
then:
PrintNPCB("Character is in volume, starting healing")
NavigationTarget := MakeNavigationTarget(AgentToHeal)
branch:
Navigatable.NavigateTo(NavigationTarget)
Focusable.MaintainFocus(AgentToHeal)
VFXSpawner.Enable()
defer:
VFXSpawner.Disable()
race:
loop:
CurrentHealth := CharacterToHeal.GetHealth()
if(CurrentHealth + HealingAmount > HealingThreshold):
if (CurrentHealth < HealingThreshold):
CharacterToHeal.SetHealth(HealingThreshold)
PrintNPCB("Character has reached HealingThreshold, stopping healing")
break
else:
CharacterToHeal.SetHealth(CurrentHealth + HealingAmount)
Sleep(HealingDelay)
HealVolume.AgentExitsEvent.Await()
# キャラクターの位置で MoveTo をループすることで、キャラクターを追い続けるように
# HealVolume と VFXSpawner を設定します。
DeviceFollowCharacter()<suspends>:void=
if:
# この動作が関連付けられているエージェント (AI キャラクター) を取得します。
Agent := GetAgent[]
# エージェントの fort_character インターフェースを取得してフォートナイトのキャラクター固有の動作、イベント、関数、インターフェースにアクセスします。
Character := Agent.GetFortCharacter[]
then:
# HealVolume と VFXSpawner で MoveTo をループし、位置を NPC キャラクターに
# 合わせます
loop:
CharacterTransform := Character.GetTransform()
VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
Sleep(UpdateRateSeconds)
# エージェントが HealVolume に入ったとき、エージェントが NPC キャラクターではない場合は
# AgentsToHeal キューに追加します。
OnAgentEnters(EnteredAgent:agent):void=
PrintNPCB("Agent entered the heal volume")
if (EnteredAgent <> GetAgent[]):
set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent)
# エージェントが HealVolume を離れると、PrintNPCB がログに記録されます
OnAgentExits(ExitAgent:agent):void=
PrintNPCB("Agent exited the heal volume")
# デフォルトの期間と色を提供するカスタム ラッパー。
PrintNPCB(Msg:string,?Duration:float = 3.0, ?TextColor:color = NamedColors.Green):void =
Print("[new_npc_behavior] {Msg}", ?Color := TextColor, ?Duration := Duration)
# この関数は、NPC がデスポーンされるかワールドから排除されると実行されます。
OnEnd<override>():void =
if(Agent := GetAgent[]):
Print(medic_example_message_module.OnEndMessage(Agent))
else:
PrintNPCB("OnEnd")
queue.verse
list(t:type) := class:
Data:t
Next:?list(t)
queue<public>(t:type) := class<internal>:
要素<internal>:?list(t) = false
サイズ<public>:int = 0
Enqueue<public>(NewElement:t):queue(t) =
queue(t):
Elements := option:
list(t):
Data := NewElement
Next := Elements
Size := Size + 1
Dequeue<public>()<decides><transacts>:tuple(queue(t), t) =
List := Elements?
(queue(t){Elements := List.Next, Size := Size - 1}, List.Data)
前<public>()<decides><transacts>:t = Elements?.Data
CreateQueue<public><constructor>(InData:t where t:type) := queue(t):
Elements := option:
list(t):
Data := InData
Next := false
Size := 1
応用編
このガイドを完了したことで、特定のしきい値に満たないキャラクターを自動的に回復させるメディック キャラクターを作成する方法を習得しました。 学んだ内容を活用して、独自の特別な動作を行うオリジナルのメディック キャラクターを作成してみましょう。
-
ボリューム内に敵がいるかどうかに基づいて、ダメージを与えるボリュームと回復するボリュームを切り替えるメディックを作成できますか?
-
再生できない素材を使用してキャラクターを回復させるメディックはどうでしょうか?メディックはどうやってその素材を復元するのでしょうか?時間が経過すると復元できるのでしょうか?それとも、敵を攻撃すると復元できるのでしょうか?