戻る
フォートナイトの Speedway Race テンプレートを Verse Persistence などで改善した方法
フォートナイト チーム
Verse Persistence (Verse における持続データ) の導入により、フォートナイト クリエイティブの「Design a Speedway Race」テンプレートの島を UEFN にアップグレードする機会がもたらされました。シネマティックスや風景の追加などのアップデートから、トリガーから Verse への移行まで、このテンプレート開発の過程を皆さんと共有できることを嬉しく思います。Verse Persistence を使用した Speedway Race へようこそ!
「Design a Speedway Race」テンプレートは、当初、新しく作成されたレース トラック アセットを使用し、優れた QOL 機能を備えた個性的なレース体験を構築するために作成されました。
最新のアップデートでは、総合的なアプローチを採用し、マップ全体の多くの機能を置き換え、アップグレードしました。Epic Games が UEFN の強力な機能を活用して行ったアップデートの一部を確認しましょう。
オリジナルのプロジェクトの従来の昼/夜のサイクルは、より高度なフォートナイト バトル ロイヤル チャプター 4 のライティングに置き換えられました。この新しいサイクルにより、Lumen を使用して、よりソフトなシャドウとリアルなグローバル イルミネーションを作成できるようになりました。また、滝などの機能を追加し、トラック自体をより視覚的に面白くしました。
Verse Persistence によってもたらされた解決策により、ゲーム トラッキングをまたいだデータ追跡が可能になりました。この機能により、プレイヤーのライフタイムの統計を監視し、ローカル ランキングを作成することができました。
私たちの目標は、周回を終えたときにプレイヤーのベスト ラップ タイムを更新し、レースを終えるたびにポイントと勝利を記録することでした。
ラップ タイムの更新は、レース マネージャーの仕掛け のLapCompletedEvent を待機することで、プレイヤーが周回を終えるたびに検出されるので、簡単でした。レースがスタートすると、プレイヤーごとに実行されるタイマーの仕掛けを開始します。WaitForPlayerToFinishLap 関数は、プレイヤーが周回を終えるのを待機し、ラップ タイムを計算し、統計テーブルを更新し、次のラップ記録のためにタイマーをリセットします。
これを解決するために、ArraySync 関数を使用しました。ArraySync は、渡された配列の各要素に対して非同期関数を呼び出し、それらの関数がすべて完了するのを待機します。プレイヤーの配列と WaitForPlayerToFinishRace 関数を渡すことで、それぞれのプレイヤーに対して関数を呼び出し、全員がゴールするまで待つことができます。
これにより、レースを終えたときの順位に基づいてプレイヤーにポイントと勝利を付与し、それに応じて統計テーブルを更新することができます。ArraySync が終了すると、すべてのプレイヤーがレースを終えたことがわかるため、ゲームを終了できます。
各プレイヤーの永続的な統計はすでに確保できていたため、それらの統計を取得して、ビルボードを使用して表示することができました。試合前の待機エリアでは、各プレイヤーのビルボードの仕掛けとプレイヤー参照の仕掛けを追加し、統計とそのときに使用したコスチュームの両方を紹介しました。ただし、課題として残ったのは、パフォーマンスに基づいたこれらのプレイヤーのソートです。
これを実現するために、マージ ソート アルゴリズムを実装しました。マージ ソートは一般的な分割統治アルゴリズムで、配列を 2 つに再帰的に分割し、各サブ配列をソートし、それらをマージして元に戻します。また、アルゴリズムに比較関数を渡す機能を追加し、さまざまなプレイヤーの統計ごとに比較関数を作成しました。
プレイヤーのソートが必要なときはいつでも、各プレイヤーの統計テーブルから統計を取得し、それらをすべて配列に追加し、その配列と比較関数の両方をマージ ソート アルゴリズムに渡すことができます。どの比較関数を渡すかを選択することで、プレイヤーの配列を任意の統計でソートして返すことができます。新しいテンプレートにはマージ ソートのアルゴリズムとテスト ファイルの両方が含まれています!そのためアルゴリズムを自分でテストしたり、独自の関数に合わせてアルゴリズムをカスタマイズすることができます。
ソートの仕組みが整ったので、ランキングの最終的な仕上げを行いました。ゲームの最初のラウンドでは、プレイヤーは自分のプレイヤー参照とビルボードがあるゲーム前の待機エリアにスポーンされます。このプレイヤー参照を各プレイヤーのライフタイム ポイントでソートし、各プレイヤーの統計をプレイヤーの目の前のビルボードに表示します。これにより、プレイヤーはレース前に競争相手を把握し、勝利するには誰に注意すべきかを知ることができます。
プレイし続ければ、ランキングの頂点に立てるかもしれません!
そのために、CircuitInfo という名前の 2 つ目のプレイヤーのウィーク マップ変数を作成しました。この CircuitInfo 変数は、ゲーム終了時やプレイヤーが退場したときにリセットするために必要なすべての情報を保持しています。
2 つ目のウィーク マップ変数を使用することで、リセットされるべき情報 (サーキット情報) と、永続的に保持すべき情報 (プレイヤーの統計情報) を明確にしました。
プレイヤーごとに、以下の情報を CircuitInfo プレイヤー ウィーク マップ変数に記録します。
Verse の仕掛けの OnBegin 関数 (各ラウンドのスタート時に実行される) では、アクティブ プレイヤーをすべてイテレートし、前回完了したラウンドの最高値を持続データから取得することで、現在のラウンドを特定します。これは 1 ラウンドにつき 1 回だけ行い、ラウンド情報をセッションのウィーク マップ変数に記録します。そのため、プロジェクト内のすべての Verse コードは、再計算する必要なく、いつでもこの値にアクセスできます。
プレイヤーがレースを完了すると、レース マネージャーの仕掛けの RaceCompletedEvent を待機することで検出され、そのプレイヤーに関連付けられた CircuitInfo ウィーク マップ変数に、ゴール順位と現在のラウンドが記録されます。この情報は次の 2 つの状況ではリセットされます。
更新したテンプレートでは、パルス トリガーをシーケンサーに置き換えて、オープニングのシネマティックを実現しました。シーケンサーを使用することで、さまざまなカメラ、ヘッドアップ ディスプレイ (HUD) 要素、アクティブ プレイヤーの数に応じて調整されるダイナミックなラインナップ ビューを追加することができましました。
パルス トリガーの設定と同様に、重要な瞬間に仕掛けをシーケンスに接続し、次のプレイヤーのスコアを表示するタイミングやイントロをカットするタイミングを決定できるようにしました。
このテンプレートは、デザインを強化する新しい機能がリリースされるたびに更新していく予定です。それまでは、UEFN の [Template (テンプレート)] タブからテンプレートをダウンロードし、そのコンポーネントを探索し、機能をプロジェクトに組み込みましょう。皆さんのレースやランキングなどが、形になるのを楽しみにしています!
「Design a Speedway Race」テンプレートは、当初、新しく作成されたレース トラック アセットを使用し、優れた QOL 機能を備えた個性的なレース体験を構築するために作成されました。
最新のアップデートでは、総合的なアプローチを採用し、マップ全体の多くの機能を置き換え、アップグレードしました。Epic Games が UEFN の強力な機能を活用して行ったアップデートの一部を確認しましょう。
ビジュアル デザイン
UEFN の風景モードでは、レースのルーツに立ち返り、新しい風景編集ツールを使用して、オフロード トラックを作成することができました。オリジナルの島の背景にあった岩のアセットでできた山々は、より自然な風景デザインに置き換えました。オリジナルのプロジェクトの従来の昼/夜のサイクルは、より高度なフォートナイト バトル ロイヤル チャプター 4 のライティングに置き換えられました。この新しいサイクルにより、Lumen を使用して、よりソフトなシャドウとリアルなグローバル イルミネーションを作成できるようになりました。また、滝などの機能を追加し、トラック自体をより視覚的に面白くしました。
Verse Persistence:ランキングの改善
オリジナルのマップでは、プレイヤー参照の仕掛けを使用したタワーに、1 位のプレイヤーとそのプレイヤーのポイント数が表示されていました。ただし、このデータはセッションをまたいで保持されませんでした。Verse Persistence によってもたらされた解決策により、ゲーム トラッキングをまたいだデータ追跡が可能になりました。この機能により、プレイヤーのライフタイムの統計を監視し、ローカル ランキングを作成することができました。
プレイヤー統計をトラッキングする
プレイヤーのライフタイムの勝利数、ベスト ラップ タイム、レース完走ごとの獲得ポイントを追跡する、持続可能なプレイヤー統計テーブル クラスを開発しました。また、PlayerStatsMap という永続化可能なウイーク マップを使用して、プレイヤーをプレイヤーの統計テーブルにマッピングし、ラウンドやセッションをまたいでこれらの統計を保持できるようにしました。そして、統計マネージャー クラスを作成し、プレイヤーごとのこれらの統計の初期化、取得、更新を行うようにしました。私たちの目標は、周回を終えたときにプレイヤーのベスト ラップ タイムを更新し、レースを終えるたびにポイントと勝利を記録することでした。
ラップ タイムの更新は、レース マネージャーの仕掛け のLapCompletedEvent を待機することで、プレイヤーが周回を終えるたびに検出されるので、簡単でした。レースがスタートすると、プレイヤーごとに実行されるタイマーの仕掛けを開始します。WaitForPlayerToFinishLap 関数は、プレイヤーが周回を終えるのを待機し、ラップ タイムを計算し、統計テーブルを更新し、次のラップ記録のためにタイマーをリセットします。
勝利数とポイントを記録する
勝利数とポイントの記録は、より複雑であることが判明しました。同様の「WaitForPlayerToFinishRace」関数を使えば、レース マネージャーの「RaceCompletedEvent」を待機することで、いつプレイヤーがゴールしたかを把握できます。ただし、すべてのプレイヤーがゴールしたときだけゲームを終了させたかったものの、レース マネージャーにはこれを追跡したり、ゴールしたときにゲームを終了させる手段がありませんでした。これを解決するために、ArraySync 関数を使用しました。ArraySync は、渡された配列の各要素に対して非同期関数を呼び出し、それらの関数がすべて完了するのを待機します。プレイヤーの配列と WaitForPlayerToFinishRace 関数を渡すことで、それぞれのプレイヤーに対して関数を呼び出し、全員がゴールするまで待つことができます。
これにより、レースを終えたときの順位に基づいてプレイヤーにポイントと勝利を付与し、それに応じて統計テーブルを更新することができます。ArraySync が終了すると、すべてのプレイヤーがレースを終えたことがわかるため、ゲームを終了できます。
結果を表示する
統計を記録するのも必要ですが、その統計をプレイヤーに表示する方法も必要でした。私たちは、表示されるレベル内のランキングを作成し、ライフタイム ポイントでプレイヤーをソートしてトップ プレイヤーを強調表示したいと考えました。各プレイヤーの永続的な統計はすでに確保できていたため、それらの統計を取得して、ビルボードを使用して表示することができました。試合前の待機エリアでは、各プレイヤーのビルボードの仕掛けとプレイヤー参照の仕掛けを追加し、統計とそのときに使用したコスチュームの両方を紹介しました。ただし、課題として残ったのは、パフォーマンスに基づいたこれらのプレイヤーのソートです。
これを実現するために、マージ ソート アルゴリズムを実装しました。マージ ソートは一般的な分割統治アルゴリズムで、配列を 2 つに再帰的に分割し、各サブ配列をソートし、それらをマージして元に戻します。また、アルゴリズムに比較関数を渡す機能を追加し、さまざまなプレイヤーの統計ごとに比較関数を作成しました。
プレイヤーのソートが必要なときはいつでも、各プレイヤーの統計テーブルから統計を取得し、それらをすべて配列に追加し、その配列と比較関数の両方をマージ ソート アルゴリズムに渡すことができます。どの比較関数を渡すかを選択することで、プレイヤーの配列を任意の統計でソートして返すことができます。新しいテンプレートにはマージ ソートのアルゴリズムとテスト ファイルの両方が含まれています!そのためアルゴリズムを自分でテストしたり、独自の関数に合わせてアルゴリズムをカスタマイズすることができます。
ソートの仕組みが整ったので、ランキングの最終的な仕上げを行いました。ゲームの最初のラウンドでは、プレイヤーは自分のプレイヤー参照とビルボードがあるゲーム前の待機エリアにスポーンされます。このプレイヤー参照を各プレイヤーのライフタイム ポイントでソートし、各プレイヤーの統計をプレイヤーの目の前のビルボードに表示します。これにより、プレイヤーはレース前に競争相手を把握し、勝利するには誰に注意すべきかを知ることができます。
プレイし続ければ、ランキングの頂点に立てるかもしれません!
スタート ラインでのレーサーの順序
フォートナイト クリエイティブ バージョンのマップでは、各ゲームのスタート時にプレイヤーがランダムな順序で配置されます。ランキング上位を目指すプレイヤーのモチベーションを高めるため、スタート ラインの順番は前回のラウンドでの順位に基づいて設定しました。ラウンド情報をトラッキングする
ランキングに使用した持続データとは異なり、レーサーの順序とサーキット情報は、すべてのラウンドをまたいで持続する必要がありましたが、すべてのゲーム セッションをまたいで持続する必要はありませんでした。Verse のセッション ウィーク マップ変数はラウンドごとにデータをリセットするため、プレイヤーごとにこの情報を格納し、ゲーム終了後にリセットする必要があります。そのために、CircuitInfo という名前の 2 つ目のプレイヤーのウィーク マップ変数を作成しました。この CircuitInfo 変数は、ゲーム終了時やプレイヤーが退場したときにリセットするために必要なすべての情報を保持しています。
2 つ目のウィーク マップ変数を使用することで、リセットされるべき情報 (サーキット情報) と、永続的に保持すべき情報 (プレイヤーの統計情報) を明確にしました。
プレイヤーごとに、以下の情報を CircuitInfo プレイヤー ウィーク マップ変数に記録します。
- ゴール順
- 前回完了したラウンド
ラウンド固有のロジック
ラウンド固有のロジック (第 1 ラウンドでない場合、前回のゴール順でプレイヤーをソートするなど) を適用し、プレイヤー データをリセットするタイミングを把握するために、自分がどのラウンドにいるのかを知る必要があります。現在のラウンドを取得する API は現時点ではありません。そのため、各プレイヤーの持続データに現在のラウンドを記録する必要があります。Verse の仕掛けの OnBegin 関数 (各ラウンドのスタート時に実行される) では、アクティブ プレイヤーをすべてイテレートし、前回完了したラウンドの最高値を持続データから取得することで、現在のラウンドを特定します。これは 1 ラウンドにつき 1 回だけ行い、ラウンド情報をセッションのウィーク マップ変数に記録します。そのため、プロジェクト内のすべての Verse コードは、再計算する必要なく、いつでもこの値にアクセスできます。
プレイヤーがレースを完了すると、レース マネージャーの仕掛けの RaceCompletedEvent を待機することで検出され、そのプレイヤーに関連付けられた CircuitInfo ウィーク マップ変数に、ゴール順位と現在のラウンドが記録されます。この情報は次の 2 つの状況ではリセットされます。
- ゲーム中にプレイヤーが退場する。プレイ空間の PlayerRemovedEvent をサブスクライブして、プレイヤーがいつ退場したかを把握し、ResetCircuitInfo 関数を呼び出します。
- 各ラウンドのスタート時に、前回完了したラウンドを計算する。最終ラウンドであった場合は、ResetCircuitInfo を呼び出してプレイヤーのデータをリフレッシュします。Verse の仕掛けの編集可能なプロパティにゲームの総ラウンド数を設定することで、ラウンド数を把握することができます。これは島クリエイターがラウンドの設定と一致することを確認するために行われます。
セッション ウィーク マップを使用すべき場合とプレイヤー ウィーク マップを使用すべき場合
この新しい Speedway Race は、コードでセッション ウィーク マップ変数とプレイヤー ウィーク マップ変数を使用する違いと理由を示す優れた例です。- セッション ウィーク マップ変数は、シングルトンや、毎回再計算したくない現在のラウンドのデータを格納するうえで役立ちます。
- プレイヤー ウィーク マップ変数は、複数のラウンドやゲーム セッションをまたいで保持される必要があるものの、個々のプレイヤーに関連付けられている必要がある情報向けに設計されています。
オープニング シーケンスへのパルス トリガー
Speedway Race テンプレートのオリジナル バージョンでは、レースの「レディ - セット - ゴー」の部分を編成するために、パルス トリガーという仕掛けを利用しました。パルス トリガーは、トリガーを作動させてテキストを表示したり、スタート ラインのライトを点灯させたりすることで、設定した時間内に一連のイベントを再生しました。更新したテンプレートでは、パルス トリガーをシーケンサーに置き換えて、オープニングのシネマティックを実現しました。シーケンサーを使用することで、さまざまなカメラ、ヘッドアップ ディスプレイ (HUD) 要素、アクティブ プレイヤーの数に応じて調整されるダイナミックなラインナップ ビューを追加することができましました。
パルス トリガーの設定と同様に、重要な瞬間に仕掛けをシーケンスに接続し、次のプレイヤーのスコアを表示するタイミングやイントロをカットするタイミングを決定できるようにしました。
今後について
UEFN の進化とともに、このテンプレートも進化させていきたいと考えています。Epic Games の目標は、UEFN を使ってより高度なコンテンツを作り続け、最新の機能を活用して楽しく魅力的な島を構築できるようにすることです。このテンプレートは、デザインを強化する新しい機能がリリースされるたびに更新していく予定です。それまでは、UEFN の [Template (テンプレート)] タブからテンプレートをダウンロードし、そのコンポーネントを探索し、機能をプロジェクトに組み込みましょう。皆さんのレースやランキングなどが、形になるのを楽しみにしています!