
ガードをコントロールする
上記の動画を参考にさせていただきました。
Graeme Bull様、ありがとうございます🙇♂️
# 必要なモジュールのインポート
using { /Fortnite.com/Devices } # デバイス関連の機能
using { /Fortnite.com/Characters } # キャラクター関連の機能
using { /Verse.org/Simulation } # シミュレーション機能
using { /UnrealEngine.com/Temporary/Diagnostics } # デバッグ用
using { /UnrealEngine.com/Temporary/SpatialMath } # 空間計算用
using { /Verse.org/Random } # 乱数生成用
# メインデバイスクラスの定義
Hello_world_device := class(creative_device):
@editable
GuardSpawner:guard_spawner_device = guard_spawner_device{}
MaxGuards : int = 5
var CurrentGuards : int = 0
# ゲーム開始時に自動的に呼び出される関数
OnBegin<override>()<suspends>:void=
Print("Game has started")
GuardSpawner.AlertedEvent.Subscribe(OnGuardAlerted)
GuardSpawner.SpawnedEvent.Subscribe(OnGuardSpawned)
GuardSpawner.EliminatedEvent.Subscribe(OnGuardEliminated)
OnGuardAlerted(Res: device_ai_interaction_result):void=
Print("the guard was alerted!!")
#make another guard!!
if(CurrentGuards < MaxGuards):
Print("Spawn another guard!")
GuardSpawner.Spawn()
OnGuardSpawned(Guard : agent):void=
Print("a guard was spawned")
set CurrentGuards += 1
Print("there should be {CurrentGuards} guards now")
OnGuardEliminated(Res :device_ai_interaction_result):void=
set CurrentGuards -= 1
Print("there should be {CurrentGuards} guards now")
# Fortniteのクリエイティブモードで使用する各種モジュールをインポート
using { /Fortnite.com/Devices } # デバイス関連の機能をインポート
using { /Fortnite.com/Characters } # キャラクター(NPC/プレイヤー)関連の機能をインポート
using { /Verse.org/Simulation } # シミュレーション機能をインポート
using { /UnrealEngine.com/Temporary/Diagnostics } # デバッグ出力用の機能をインポート
using { /UnrealEngine.com/Temporary/SpatialMath } # 3D空間の計算機能をインポート
using { /Verse.org/Random } # 乱数生成機能をインポート
# creative_deviceを継承したカスタムデバイスクラスを定義
Hello_world_device := class(creative_device):
# UEFNエディタで編集可能なガードスポナーデバイスの参照を定義
@editable # エディタでの編集を可能にする
GuardSpawner:guard_spawner_device = guard_spawner_device{}
# ガードの最大数を5に設定
MaxGuards : int = 5
# 現在のガード数を追跡する変数(可変)
var CurrentGuards : int = 0
# ゲーム開始時に自動的に呼び出される関数を定義
OnBegin<override>()<suspends>:void=
Print("Game has started") # ゲーム開始メッセージを出力
# ガードの各イベントにハンドラーを登録
GuardSpawner.AlertedEvent.Subscribe(OnGuardAlerted) # ガードが警戒状態になった時
GuardSpawner.SpawnedEvent.Subscribe(OnGuardSpawned) # ガードが生成された時
GuardSpawner.EliminatedEvent.Subscribe(OnGuardEliminated) # ガードが排除された時
# ガードが警戒状態になった時の処理を定義
OnGuardAlerted(Res: device_ai_interaction_result):void=
Print("the guard was alerted!!")
# 現在のガード数が上限未満なら新しいガードを生成
if(CurrentGuards < MaxGuards):
Print("Spawn another guard!")
GuardSpawner.Spawn()
# ガードが生成された時の処理を定義
OnGuardSpawned(Guard : agent):void=
Print("a guard was spawned")
# ガード数をインクリメント
set CurrentGuards += 1
# 現在のガード数をログ出力
Print("there should be {CurrentGuards} guards now")
# ガードが排除された時の処理を定義
OnGuardEliminated(Res :device_ai_interaction_result):void=
# ガード数をデクリメント
set CurrentGuards -= 1
# 更新されたガード数をログ出力
Print("there should be {CurrentGuards} guards now")
# Fortniteのクリエイティブモードで使用する各種モジュールをインポート
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Random }
# creative_deviceを継承したカスタムデバイスクラスを定義
警備システム := class(creative_device):
@editable
警備員生成器:guard_spawner_device = guard_spawner_device{}
警備員最大数 : int = 5
var 現在の警備員数 : int = 0
OnBegin<override>()<suspends>:void=
Print("ゲームが開始されました")
警備員生成器.AlertedEvent.Subscribe(警備員警戒時)
警備員生成器.SpawnedEvent.Subscribe(警備員生成時)
警備員生成器.EliminatedEvent.Subscribe(警備員排除時)
警備員警戒時(結果: device_ai_interaction_result):void=
Print("警備員が警戒状態になりました!")
if(現在の警備員数 < 警備員最大数):
Print("追加の警備員を生成します!")
警備員生成器.Spawn()
警備員生成時(警備員 : agent):void=
Print("警備員が生成されました")
set 現在の警備員数 += 1
Print("現在の警備員数: {現在の警備員数}")
警備員排除時(結果 :device_ai_interaction_result):void=
set 現在の警備員数 -= 1
Print("現在の警備員数: {現在の警備員数}")
このコードについて詳しく解説します:
基本構造
Hello_world_device := class(creative_device)
creative_deviceを継承した新しいデバイスクラスを定義しています
Fortniteクリエイティブで使用できるカスタムデバイスとなります
重要なフィールド
@editable
GuardSpawner:guard_spawner_device = guard_spawner_device{}
UEFNエディタで編集可能なガードスポナーデバイスの参照
ガードの生成を制御するために使用
MaxGuards : int = 5
var CurrentGuards : int = 0
最大ガード数を5に制限
現在のガード数を追跡する変数
イベントハンドラー
OnBegin
OnBegin<override>()<suspends>:void=
ゲーム開始時に自動的に実行
3つのイベントハンドラーを登録:
AlertedEvent: ガードが警戒状態になった時
SpawnedEvent: 新しいガードが生成された時
EliminatedEvent: ガードが排除された時
OnGuardAlerted
OnGuardAlerted(Res: device_ai_interaction_result):void=
ガードが警戒状態になった時の処理
現在のガード数が上限未満なら新しいガードを生成
OnGuardSpawned
OnGuardSpawned(Guard : agent):void=
新しいガードが生成された時の処理
カウンターをインクリメント
現在のガード数をログ出力
OnGuardEliminated
OnGuardEliminated(Res :device_ai_interaction_result):void=
ガードが排除された時の処理
カウンターをデクリメント
更新されたガード数をログ出力
機能概要
このデバイスは以下のような警備システムを実装しています:
最大5体までのガードを管理
ガードが警戒状態になると、追加のガードを呼び出し(上限まで)
ガードの数を常に追跡し、適切に管理
`device_ai_interaction_result`型について
基本構造
device_ai_interaction_result := struct<epic_internal>:
# 相互作用を開始したエージェント(オプショナル)
Source<native><public>:?agent
# 相互作用の対象となったエージェント(オプショナル)
Target<native><public>:?agent
主な用途
AIの相互作用結果を表現するための構造体です
以下のような情報を含みます:
Source (発信源): アクションを起こしたエージェント
Target (対象): アクションの対象となったエージェント
実例での使用
このコードでは以下の2つの場面で使用されています:
警備員警戒時:
警備員警戒時(結果: device_ai_interaction_result):void=
警備員が何かを検知して警戒状態になった時の情報を受け取ります
誰が(Source)何を(Target)検知したのかの情報を含みます
警備員排除時:
警備員排除時(結果 :device_ai_interaction_result):void=
警備員が排除された時の情報を受け取ります
誰が(Source)どの警備員を(Target)排除したのかの情報を含みます
このように、AIの相互作用に関する情報を整理して渡すための型として使用されています。
結果という引数名に特に意味はない
このコードでは`device_ai_interaction_result`型の引数名として`結果`を使用していますが、これは単なる変数名で、以下のような別の名前でも問題なく動作します:
# 例1:より具体的な名前をつける場合
警備員警戒時(相互作用情報: device_ai_interaction_result):void=
# 例2:英語のままの場合
警備員警戒時(result: device_ai_interaction_result):void=
# 例3:より詳細な説明を含める場合
警備員警戒時(警戒状態発生情報: device_ai_interaction_result):void=
実際、このコード内では引数の`結果`を直接参照していないため、どのような名前をつけても機能的な影響はありません。引数名は、コードの可読性や理解のしやすさを考慮して選択するのが良いでしょう。
この場合、以下のような命名が適切かもしれません:
警備員警戒時(警戒イベント: device_ai_interaction_result):void=
これは、この引数が警戒状態が発生したイベントの情報を含んでいることをより明確に示しています。
1. モジュールインポート部分
using { /Fortnite.com/Devices } がなかったら?
Fortniteのゲーム内で使える様々なデバイス(仕掛け)の機能が使えなくなります。例えば、トリガー、ボタン、ダメージを与える仕掛けなどが使えません。
気づき: ゲーム内で仕掛けを使いたい場合は、using { /Fortnite.com/Devices } を使って、デバイスの機能を読み込む必要があります。
using { /Fortnite.com/Characters } がなかったら?
キャラクターに関する機能(体力、移動など)が使えなくなります。
気づき: キャラクターを操作したり、状態を管理したりしたい場合は、using { /Fortnite.com/Characters } を使う必要があります。
using { /Verse.org/Simulation } がなかったら?
ゲームの基本的な機能(プレイヤーの参加、ゲームの開始・終了など)が使えなくなります。
気づき: ゲームの基本的な流れを作りたい場合は、using { /Verse.org/Simulation } を使う必要があります。
using { /UnrealEngine.com/Temporary/Diagnostics } がなかったら?
Print関数が使えなくなり、デバッグ用のメッセージをログに出力できなくなります。これは、コードが正しく動いているか確認するのに不便です。
気づき: Print関数などを使って、コードの動きを確認したい場合は、using { /UnrealEngine.com/Temporary/Diagnostics } を使う必要があります。
using { /UnrealEngine.com/Temporary/SpatialMath } がなかったら?
3次元空間での計算(ベクトル演算など)ができなくなります。例えば、オブジェクトの位置や方向を計算したり、移動させたりするのが難しくなります。
気づき: 3次元空間での計算をしたい場合は、using { /UnrealEngine.com/Temporary/SpatialMath } を使う必要があります。
using { /Verse.org/Random } がなかったら?
ランダムな数値を生成する関数(GetRandomFloat など)が使えなくなり、ランダムな動きや結果を作れなくなります。
気づき: ランダムな要素を取り入れたい場合は、using { /Verse.org/Random } を使う必要があります。
もしusing { /Fortnite.com/UI }があったら?
ユーザーインターフェース(UI)を操作する機能(ボタン、テキストボックスなど)が使えるようになります。
気づき: 画面上にUIを表示したい場合は、using { /Fortnite.com/UI } を使う必要があります。
気づき: using を使うことで、他の人が作った便利な機能を自分のコードに取り込むことができます。必要な機能に応じて、適切なモジュールをインポートする必要があります。モジュールとは、関連する機能(例えば、数学計算、ファイル操作、ネットワーク通信など)をまとめたものです。
2. クラス定義部分
警備システム := class(creative_device): の := って何?普通の = と何が違うの?
:= は、新しく変数やクラスを「定義」するときに使います。= は、すでに定義されている変数に値を「代入」するときに使います。
気づき: Verse言語では、変数を定義するときと、変数に値を代入するときで、使う記号が異なります。
もし creative_device を継承していなかったら?
このクラスをUEFNエディタ上で配置することができなくなります。つまり、ゲーム内で使うことができません。
気づき: ゲーム内で使うオブジェクトを作るためには、creative_device を継承する必要があります。
もしクラス名が 警備システム ではなく、SecuritySystem だったら?
英語の名前になりますが、クラスの役割は同じです。どちらの名前でも問題ありません。
気づき: クラス名は、その役割がわかるような名前であれば、日本語でも英語でも構いません。ただし、他の人が見たときに理解しやすい名前を付けることが重要です。
もしクラス名が MyDevice だったら?
これでは何をするクラスなのか、全くわかりませんね。クラス名は、その役割がわかるような名前にすることが重要です。
気づき: クラス名は、他の人が見たときに、その役割を理解できるような名前にする必要があります。
もしクラスの定義がなかったら?
警備システムの設計図が存在しないことになり、警備システムを作成できなくなります。
気づき: クラスは、オブジェクトの設計図です。オブジェクトを作るためには、まずクラスを定義する必要があります。
気づき: クラスはオブジェクトの設計図です。creative_device を継承することで、UEFNエディタ上で配置可能なオブジェクトを作ることができます。
3. 変数定義部分
@editable がなかったら?
UEFNエディタ上で、変数の値を変更できなくなります。コードを直接書き換える必要があり、大変不便です。
気づき: @editable を使うことで、UEFNエディタから簡単に変数の値を変更できるようになります。
警備員生成器:guard_spawner_device = guard_spawner_device{} のように、型と初期値を両方書く必要があるのはなぜ?
Verse言語では、変数を定義するときに、型と初期値を両方書くことで、変数の型を明確にし、初期値を設定することができます。
気づき: 型と初期値を両方書くことで、コードの可読性と安全性を高めることができます。
もし 警備員生成器 が guard_spawner_device 型ではなく、trigger_device 型だったら?
警備員を生成することができなくなります。トリガーデバイスは、プレイヤーの操作などを検知するためのデバイスであり、警備員を生成する機能はありません。
気づき: 適切な型の変数を使うことが重要です。
警備員最大数 : int = 5 は、なぜ 5 という初期値なの?
これは、ゲームの設計者が決めた値です。ゲームの難易度などを考慮して、適切な値を設定する必要があります。
気づき: 初期値は、ゲームの設計に基づいて設定する必要があります。
もし 警備員最大数 が 100 だったら?
ゲーム内に最大100体の警備員を出現させることができます。ただし、数が多すぎると、ゲームのパフォーマンスに影響が出る可能性があります。
気づき: 変数の値は、ゲームのパフォーマンスにも影響を与える可能性があるため、適切な値を設定する必要があります。
var 現在の警備員数 : int = 0 に var がついているのはなぜ?警備員最大数 にはついていないのに。
var がついている変数は、後から値を変更できる変数です。var がついていない変数は、後から値を変更できない変数(定数)です。現在の警備員数は、警備員が生成されたり排除されたりするたびに値が変わるので、var をつける必要があります。
気づき: 後から値を変更する可能性がある変数には var をつける必要があります。
もし現在の警備員数という変数がなかったら?
ゲーム開始時と現状で何人警備員がいるのかを管理することができなくなります。そのため、最大数を超えて警備員がスポーンされてしまうかもしれません。
気づき: 適切な変数を定義し、それらを使って管理することでコードが煩雑になることを防げます。
気づき: 変数は、オブジェクトの状態を記録したり、値を設定したりするために使います。@editable を使うことで、UEFNエディタから変数の値を簡単に変更できるようになります。
4. OnBegin メソッド
OnBegin<override>()<suspends>:void= の <override> って何?
<override> は、このメソッドが creative_device クラスで定義されている OnBegin メソッドを上書きしていることを示します。
気づき: 親クラスのメソッドを上書きすることで、独自の処理を追加することができます。
OnBegin<override>()<suspends>:void= の <suspends> って何?
<suspends> は、このメソッドが非同期処理を含む可能性があることを示します。非同期処理とは、処理の完了を待たずに、次の処理に進むことができる処理のことです。
気づき: <suspends> キーワードを使うことで、時間がかかる処理を非同期で実行し、ゲーム全体のパフォーマンスを向上させることができます。
もし OnBegin メソッドがなかったら?
ゲーム開始時に、警備員生成器のイベントを購読することができず、警備員が生成されません。
気づき: OnBegin メソッドは、ゲーム開始時に必要な初期化処理を行うために重要です。
もし Print("ゲームが開始されました") がなかったら?
ゲームが開始されたことがログに出力されません。デバッグの際に、ゲームが開始されたかどうかを確認するのが難しくなります。
気づき: Print 関数を使うことで、デバッグ用のメッセージをログに出力することができます。
警備員生成器.AlertedEvent.Subscribe(警備員警戒時) って何をしているの?
警備員生成器 の AlertedEvent (警備員が警戒状態になったときに発生するイベント)を購読しています。Subscribe の後ろの括弧に、イベントが発生したときに呼び出す関数(ここでは 警備員警戒時)を指定します。
気づき: イベントを購読することで、特定のイベントが発生したときに、指定した関数を呼び出すことができます。
もし 警備員生成器.SpawnedEvent.Subscribe(警備員生成時) がなかったら?
警備員が生成されたことが検知できず、現在の警備員数 を増やすことができません。
気づき: 警備員が生成されたことを知るためには、SpawnedEvent を購読する必要があります。
もし 警備員生成器.EliminatedEvent.Subscribe(警備員排除時) がなかったら?
警備員が排除されたことが検知できず、現在の警備員数 を減らすことができません。
気づき: 警備員が排除されたことを知るためには、EliminatedEvent を購読する必要があります。
気づき: OnBegin メソッドでは、ゲーム開始時に必要な初期化処理を行います。イベントを購読することで、特定のイベントが発生したときに、指定した関数を呼び出すことができます。
5. 警備員警戒時 メソッド
警備員警戒時(結果: device_ai_interaction_result):void= の 結果: device_ai_interaction_result って何?
これは、AlertedEvent から渡される情報です。device_ai_interaction_result は、AI(ここでは警備員)とのインタラクションの結果を表す型です。
気づき: イベントから、イベントに関する情報を受け取ることができます。
もし Print("警備員が警戒状態になりました!") がなかったら?
警備員が警戒状態になったことがログに出力されません。
気づき: Print 関数を使うことで、コードの動きを確認することができます。
if(現在の警備員数 < 警備員最大数): の条件式がなかったら?
警備員最大数 を超えて、警備員が生成されてしまう可能性があります。
気づき: 条件式を使うことで、特定の条件を満たす場合のみ、処理を実行することができます。
もし 警備員生成器.Spawn() がなかったら?
警備員が警戒状態になっても、追加の警備員が生成されません。
気づき: Spawn メソッドを呼び出すことで、警備員を生成することができます。
もし警備員警戒時メソッドがAlertedEventから呼ばれるイベントハンドラとして定義されていなかったら?
警備員が警戒状態になっても、このメソッドは実行されず、結果として追加の警備員が生成されません。
気づき: イベントが発生した時に特定の処理を実行するためには、イベントハンドラを定義し、イベントに登録する必要があります。
気づき: 警備員警戒時 メソッドでは、警備員が警戒状態になったときに、追加の警備員を生成する処理を行います。
6. 警備員生成時 メソッド
警備員生成時(警備員 : agent):void= の 警備員 : agent って何?
これは、SpawnedEvent から渡される情報です。agent は、生成された警備員を表す型です。
気づき: イベントから、イベントに関する情報を受け取ることができます。
もし Print("警備員が生成されました") がなかったら?
警備員が生成されたことがログに出力されません。
気づき: Print 関数を使うことで、コードの動きを確認することができます。
set 現在の警備員数 += 1 の set って何?なぜ必要なの?
set は、変数に値を代入するときに使います。現在の警備員数 は var で定義されているので、値を変更するためには set を使う必要があります。
気づき: var で定義された変数に値を代入する場合は、set を使う必要があります。
+= って何?
+= は、左側の変数に、右側の値を加算して代入する、という意味です。例えば、A += 1 は、A = A + 1 と同じ意味です。
気づき: += などの複合代入演算子を使うことで、コードを簡潔に書くことができます。
もし set 現在の警備員数 += 1 がなかったら?
警備員が生成されても、現在の警備員数 が増えません。
気づき: 変数の値を変更するためには、set を使って、変数に値を代入する必要があります。
もし警備員生成時メソッドがSpawnedEventから呼ばれるイベントハンドラとして定義されていなかったら?
警備員が生成されても、このメソッドは実行されず、結果として現在の警備員数を適切に更新することができません。
気づき: イベントが発生した時に特定の処理を実行するためには、イベントハンドラを定義し、イベントに登録する必要があります。
気づき: 警備員生成時 メソッドでは、警備員が生成されたときに、現在の警備員数 を増やす処理を行います。
7. 警備員排除時 メソッド
警備員排除時(結果 :device_ai_interaction_result):void= の 結果 : device_ai_interaction_result って何?
これは、EliminatedEvent から渡される情報です。device_ai_interaction_result は、AI(ここでは警備員)とのインタラクションの結果を表す型です。
気づき: イベントから、イベントに関する情報を受け取ることができます。
set 現在の警備員数 -= 1 の -= って何?
-= は、左側の変数から、右側の値を減算して代入する、という意味です。例えば、A -= 1 は、A = A - 1 と同じ意味です。
気づき: -= などの複合代入演算子を使うことで、コードを簡潔に書くことができます。
もし set 現在の警備員数 -= 1 がなかったら?
警備員が排除されても、現在の警備員数 が減りません。
気づき: 変数の値を変更するためには、set を使って、変数に値を代入する必要があります。
もし警備員排除時メソッドがEliminatedEventから呼ばれるイベントハンドラとして定義されていなかったら?
警備員が排除されても、このメソッドは実行されず、結果として現在の警備員数を適切に更新することができません。
気づき: イベントが発生した時に特定の処理を実行するためには、イベントハンドラを定義し、イベントに登録する必要があります。
気づき: 警備員排除時 メソッドでは、警備員が排除されたときに、現在の警備員数 を減らす処理を行います。
8. 全体を通して
もしコメントが一切なかったら?
コードが何をしているのか理解するのが難しくなります。特に、他の人が書いたコードを読む場合、コメントがないと、コードの意図を理解するのに時間がかかります。
気づき: コメントは、コードの意図を説明するために重要です。
もし変数名や関数名がもっと短かったら?
例えば、現在の警備員数 が 数、警備員警戒時 が 警戒 だったら、コードが読みにくくなります。
気づき: 変数名や関数名は、その役割がわかるような、適切な長さの名前にする必要があります。
もしコードがもっと長かったら?
例えば、if 文の中に、さらに if 文がたくさん入れ子になっていたら、コードが読みにくくなります。
気づき: コードは、できるだけ簡潔に、わかりやすく書くことが重要です。
もし、device_ai_interaction_result という型を知らなかったら?
イベントハンドラ 警備員警戒時 や 警備員排除時 の引数 結果 が何を表しているのか理解できず、これらのメソッドの役割を理解するのが難しくなります。
気づき: コードを理解するためには、使用されている型についての知識も必要です。必要に応じて、公式ドキュメントなどを参照して調べることが重要です。
気づき: コード全体を通して、変数、関数、イベントなどを適切に使うことで、ゲーム内のオブジェクトの動作を制御しています。コメントや適切な名前付けは、コードの可読性を高めるために重要です。