このページでは、エンジン プラグインでカスタム C コードを実行するカスタム フロー ノードの作成方法について説明します。
注: C でなく Lua でカスタム フロー ノードを実装することもできます。詳細については、「Lua でカスタム フロー ノードを作成する」を参照してください。
カスタム フロー ノードを作成するには
ノードに必要な機能を正確に特定します。どの種類の出力を生成する必要がありますか。ジョブを実行するにはどの入力データが必要ですか。このノードはアップストリーム ノードからの受信イベントによってトリガする必要がありますか。それとも、出力値がダウンストリーム ノードで必要とされている場合のみトリガされる 「クエリー」ノードにする必要がありますか。ノードの役割と要件を適切に処理したら、インタフェースを定義できます。
フロー グラフでノードを評価するときに呼び出されるトリガ関数を記述します。
エンジンにトリガ関数を登録および登録解除します。
拡張子が .flow_node_definition のデータ リソース ファイル内で、予測される入力の種類や生成される出力など、ノードの特性を定義します。次の「ノードを定義する」を参照してください。
次のセクションでは次に示す 3 つの点を個別に見ていきますが、トリガ関数およびノード定義は相互に依存していることに注意してください。たとえば、ノード定義内で、入力データと出力データの特定のセットを提供するようエディタに指示している場合は、これらの入力および出力を正しく処理するようトリガ関数を設定する必要があります。また、C コードをエンジン プラグイン内で実行する必要があるため、これらのプラグインの仕組みについて理解する必要があります。「エンジンを拡張する」を参照してください。
カスタム フロー ノードの記述に関連するすべての内容を示すエンドツーエンドの例については、GitHub のサンプル プラグイン リポジトリ内の samples\flow_nodes_sample_plugin フォルダを参照することをお勧めします。次の例では、システムのさまざまな機能を示す複数のフロー ノードを定義します。
sample_flow_node には、入力値の選択、出力値の設定、出力イベントのトリガを行う基本的な例を示します。また、複数の評価にわたって状態情報をノード内に維持する方法も示します。この場合は、ノードを評価するたびにカウントされる max_run_count カウンタを使用します。
profiler_flow_node は、エンジンで使用可能な、他のシステムにノードを関連付ける方法を示します。この場合は、ProfilerApi を使用して、パフォーマンス情報の記録を開始および停止します。
dragon_flow_nodes は、連携する複数のノードを使用する興味深い事例を示します。特に、これらのノードでドラゴンのカスタム データ タイプを定義し、カスタム フロー ノード接続を介して相互に渡す方法に注目してください。
ノードのトリガ関数は、FlowFunction 署名に対応する C 関数でなければなりません。
typedef void(* FlowFunction) (struct FlowTriggerContext *tc, const struct FlowData *fd, const struct FlowParameters *fp)
たとえば、次のようになります。
extern "C" void my_custom_trigger_function(struct FlowTriggerContext* tc, const struct FlowData *fd, const struct FlowParameters *fp) { ... }
トリガ関数の一般的なジョブは、入力イベントの取得、エンジンから送信されたデータ値と状態情報の入力、必要な計算の実行、出力データ値の適切な更新です。1 つまたは複数の出力イベントをトリガして、フロー グラフの処理を継続することもできます。
一部のノードは、ノードにさまざまな動作を行うように要求する、複数の入力イベントによってトリガすることができます。ノードに複数の入力イベントが設定されていて、トリガされた入力イベントを把握する必要がある場合は、エンジンからトリガ関数に渡される FlowData ポインタを使用できます。FlowData::event_index メンバーは、ノードの評価をトリガしたイベントのゼロから始まるインデックスを提供します。
switch (fd->event_index) { case 0: // the first defined input event was triggered break; case 1: // the second defined input event was triggered break; }
エンジンは FlowParameters オブジェクトを介して、トリガ関数にノードのすべての入力および出力パラメータに対するアクセスを許可します。FlowParameters は、ノードに設定された各入力パラメータおよび各出力パラメータのポインタまたは参照を含むメモリ ブロックです。
入力または出力パラメータをそれぞれ表すために FlowParameters で使用されるオブジェクトのタイプは、ノード定義内でこのパラメータに設定されたデータ タイプに応じて決まります。たとえば、ノード定義内で「ユニット」として設定された入力パラメータは、const CApiUnitRef* としてトリガ関数に渡されます。ノード定義内と FlowParameters 内の変数タイプ間のマッピングを示す表については、「カスタム C フロー ノードの記述形式」を参照してください。
FlowParameters に入力または出力の「イベント」は含まれないことに注意してください。含まれるのは、データのパラメータのみです。
FlowParameters からパラメータ データを取得するには、ノードのデータ記述と一致する独自の構造体を定義することをお勧めします。この構造体には、ノードの各入力パラメータ タイプのポインタを順番に格納し、その後に各出力パラメータ タイプの参照を順番に格納する必要があります。その後、構造体のインスタンスに FlowParameters 入力パラメータをキャストして、メンバーにアクセスします。
たとえば、次のコードは 2 つの入力パラメータおよび 1 つの出力パラメータを読み取る構造体を定義します。
const struct NodeData { const float *scale; const Vector3 *size; Vector3 &scaled_size; } &node_data = (const NodeData&)*fp; node_data.scaled_size = (*node_data.size) * (*node_data.scale);
入力パラメータは const ですが、出力パラメータは異なることに注意してください。出力パラメータには、ノード定義内で設定された既定値がエンジンによって自動的に設定されます。上記のように、トリガ関数はユーザが渡す必要がある新しい値を使用して、出力パラメータを更新する必要があります。
ノードで dynamic および static データ パラメータを使用して、評価の間に追加されたデータを追跡することもできます。入力および出力パラメータと同様に、これらのパラメータはノードの説明内で定義し、トリガ関数の FlowParameters オブジェクトから取得する必要があります。
トリガ関数は、出力パラメータの場合と同様に、動的パラメータの値を変更できます。こうすると、次回にエンジンがノードを評価するときに、FlowParameters で新しい値が渡されます。
通常は、評価が完了したときにカスタム ノードから出力イベントを発行する必要があります。こうすると、フローを操作しているユーザは、ノードを他のダウンストリーム ノードに追従させることができます。
ノードのトリガ関数から出力イベントを起動するには、FlowNodesApi::trigger_out_event() 関数を呼び出します。この関数は、エンジンがノードのトリガ関数に最初に渡した FlowTriggerContext および FlowData に渡す必要があります。また、トリガする出力イベントのゼロから始まるインデックスも渡す必要があります。
たとえば、異なる状況で呼び出される可能性がある 2 つの出力イベントがノードに設定されている場合は、次のような操作を実行できます。
if (/* some condition */) { _flow_nodes_api->trigger_out_event(tc, fd, 0); } else { _flow_nodes_api->trigger_out_event(tc, fd, 1); }
エンジンにトリガ関数を登録すると、その関数がノード定義に関連付けられます。これにより、フロー グラフ内でノードが評価されるときに呼び出される関数がエンジンに指定されます。
関数を登録するには、FlowNodesApi::setup_trigger_function() を呼び出します。この関数に次の情報を渡します。
ノードの名前。注: この名前は、ノードのデータ記述の name フィールドと一致する必要があります。
トリガ関数。
トリガ関数を登録するには、setup_game() 関数を使用することをお勧めします。たとえば、次のようになります。
static struct FlowNodesApi *_flow_nodes_api = 0; static void setup_game(GetApiFunction get_engine_api) { _flow_nodes_api = (FlowNodesApi *)get_engine_api(FLOW_NODES_API_ID); _flow_nodes_api->setup_trigger_function("my_custom_node", my_custom_trigger_function); }
ノードをエディタで使用できるようにする必要がなくなった場合(通常は、ゲームがシャットダウンした場合)は、ノード名を指定して FlowNodesApi::unregister_trigger_function() を呼び出し、ノードを登録解除する必要があります。
static void shutdown_game() { _flow_nodes_api->unregister_flow_node("my_custom_node"); }
.flow_node_definitions 拡張子を持つ SJSON データ ファイル内でカスタム C フロー ノードを定義する必要があります。これらのデータ ファイルはプロジェクトのリソース フォルダ内の任意の場所に格納することができます。また、フロー グラフで必要となったときにエンジン内のメモリにロードされている場合は、必要に応じて、プロジェクト内の他のデータ ファイルと一緒にメモリに出し入れすることができます。
プラグインがロードされるときにプロジェクトに .flow_node_definitions ファイルを自動的に追加するプラグインには、resources 拡張機能を設定すると便利なことがあります。詳細については、「プロジェクト コンテンツを拡張する」を参照してください。
プロジェクトには .flow_node_definitions ファイルを必要な数だけ含めることができるため、プロジェクトまたはプラグインにとって適切な方法でノード定義を整理することができます。
.flow_node_definitions ファイル形式の完全なリファレンスについては、「カスタム フロー ノードの記述形式」を参照してください。
注: 各ノードには、同じ .flow_node_definitions ファイル内で一意の名前ではなく、エンジンにロードされた「すべての」ノード間で一意の名前を付ける必要があります。