UDN
Search public documentation:

MasteringUnrealScriptDelegatesJP
English Translation
中国翻译
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

第 12 章 - デリゲート

デリゲートとは、インスタンス内部の関数参照で、関数と変数という 2 つのプログラミング概念の組み合わせです。変数が特定タイプの値をどのような方法で保持し、実行時にその値がいかに変化していくかは既にご存知のことでしょう。値を保持し、実行時にそれを変更できるという点で、デリゲートは変数のような一面がありますが、デリゲートの場合、この値とはクラス内で宣言された別の関数です。それと同時に、デリゲートは実行可能であることから関数のようにも振る舞います。適正な状況の下で用いられるデリゲートが強大な力を発揮するのは、この変数と関数の組み合わせという特性によるものです。

12.1 - 概要

デリゲートは、実行時にコードの動的かつ迅速な実行が求められる場合によく使用されます。従来のメソッドは敏捷性がなく、時には制約を伴います。次のコードを見てみましょう。

  var int GlobalVar;
  
  function Foo(float value)
  {
     GlobalVar = value;
     Bar();
  }
  
  function Bar()
  {
     switch (GlobalVar)
     {
        case 0:
           DoThis();
           break;
        case 1:
           DoThat();
           break;
        default:
           DoDefault();
           break;
     }
  }
  

これはランタイムのコード実行を動的に変更する方法である、と見なすことができますが、迅速に行う方法ではありません。それは、条件が追加されると Bar() を管理するための労力が増えるためです。次のコードを見てみましょう。

  delegate Bar();
  
  function Foo(float value)
  {
     switch (value)
     {
        case 0:
           Bar = DoThis();
           break;
        case 1:
           Bar = DoThat();
           break;
        default:
           Bar = DoDefault();
           break;
     }
  
     Bar();
  }
  

2 つの問題を解決したので、前の例より良くなっています。最初にグローバル変数を外し、よって Bar() の実行時にそれをチェックする必要性もなくなりました。しかし、switch ステートメントがまだ存在するので、前例と同様の管理問題に悩みます。管理が難しかった Bar() の代わりに、こちらでは Foo() の管理が難しくなりました。次のコードを見てみましょう。

  delegate Bar();
  
  function Foo(delegate<Bar> BarDelegate)
  {
     Bar = BarDelegate;
     Bar();
  }
  

switch ステートメントを外したので、もっと良くなりました。今後追加される条件の数に関係なく、Foo() も Bar() も管理する必要はありません。

12.2 - デリゲートの宣言

デリゲートは関数と方法で宣言しますが、function というキーワードの代わりに delegate を使用します。

  delegate Foo();
  

このクラスに Foo() という名前のデリゲートを含めました。

デリゲートのパラメータ

関数と同様に、デリゲートはパラメータを持つことができます。デリゲートと関数を組み合わせて使用するときは、関数にもデリゲートと同じパラメータを含める必要があります。次のコードを見てみましょう。

  delegate Foo(const float Bar, const float Doh);
  
  function FooBoom(const float Bar, const float Doh);
  
  function FooFail(const float Bar);
  

この場合、FooBoom() を Foo() に代入するのは有効ですが、FooFail() を Foo() に代入するのは無効です。このルールには例外が 1 つだけあり、それはオプションのパラメータです。次のコードを見てみましょう。

  delegate Foo(const float Bar, const float Doh, optional float Moe);
  
  function FooBoom(const float Bar, const float Doh);
  

この場合も Foo() に FooBoom() を代入できますが、そのときは FooBoom() 内で Moe を使用できません。同様に、デリゲートに戻り値のパラメータを含めることもできます。

デフォルトの動作

デリゲートの本体の定義では、デリゲートが関数に代入されていないときのデフォルトの動作を指定します。次のコードを見てみましょう。

  delegate Foo()
  {
     `Log(“Default behavior.”);
  }
  
  function Bar()
  {
     `Log(“Non default behavior.”);
  }
  
  function Bing()
  {
     Foo = Bar;
     Foo();
     Foo = none;
     Foo();
  }
  

これを実行するとスクリプトログに次のように書き込みます。

  ScriptLog: Non default behavior.
  ScriptLog: Default behavior.
  

12.3 - 変数としてのデリゲート

デリゲートを変数のように使用することができます。浮動小数や整数のように演算上で使用することはできませんが、代入や比較は可能です。その構文は、UnrealScript (Unreal スクリプト) の他の変数を割り当てる場合とまったく同じです。次のコードを見てみましょう。

  delegate Foo();
  
  function Bar();
  
  function PostBeginPlay()
  {
     Foo = Bar;
  }
  

現在参照中の関数を確認するために、デリゲートの比較を活用できる場合があります。次のコードを見てみましょう。

  delegate Foo();
  
  function Bar();
  
  function Rod();
  
  function PostBeginPlay()
  {
     Foo = Bar;
  
     if (Foo == Bar)
        `Log(“Foo is assigned to Bar()”);
  
     Foo = Rod;
  
     if (Foo != Bar)
        `Log(“Foo is not assigned to Bar()”);
  }
  

比較関数をこのように用いることで、他のグローバル変数を使わずにデリゲートの参照先を追跡できます。

12.4 - デリゲートを関数に渡す

変数と同様に、デリゲートを関数パラメータ内で使用することもできます。これは、関数やインスタンス間でデリゲートを渡したいときに便利です。次のコードを見てみましょう。

  delegate Foo();
  
  function Bar();
  
  function PassDelegate()
  {
     ReceiveDelegate(Bar);
  }
  
  function ReceiveDelegate(delegate<Foo> FooDelegate)
  {
     Foo = FooDelegate;
  }
  

このデリゲートの代入方法は、デリゲート本体が他のクラスからプロテクト (保護) またはプライベート化されている場合に大切です。デリゲートを private または protected 指定されと、通常他のクラスからはアクセスできません。次のコードを見てみましょう。

  class Pizza extends Object;
  
  private delegate Eat();
  
  function EatMe()
  {
     Eat();
  }
  
  function HowToEat(delegate<Eat> EatDelegate)
  {
     Eat = EatDelegate;
  }
  
  class Mushroom extends Object;
  
  function SpitOut()
  {
     `Log(“I spit out the mushrooms, as they are disgusting.”);
  }
  
  function EatPizza(Pizza pizza)
  {
     if (pizza != none)
     {
        pizza.HowToEat(SpitOut);
        pizza.EatMe();
     }
  }
  

12.5 - デリゲートとメモリ

デリゲートが、ワールド内で別のアクタ インスタンスに存在する関数を参照するときは、そのインスタンスを破壊するのが安全策です。ただし、別のオブジェクト インスタンスにある関数を参照する場合は、デリゲートを none に設定する必要があります。UnrealScript ではオブジェクト インスタンスをオンデマンドで破壊できないので、すべての循環参照を消去しておかなければ、オブジェクト インスタンスのガーベジコレクションを実行することができず、レベル変更時またはゲーム終了時にメモリリークが発生するので気をつけましょう。

12.6 - UISCENE および UIOBJECT デリゲート

UIScenes とその内部で使用される UIObjects は、デリゲートを利用して構成要素の機能を簡単にカスタマイズする手段を提供しています。MOD 作者はこの状況下でデリゲートを最もよく使うことになるので、以下にこれらのクラス内にあるデリゲートの一覧とその説明を示します。

UISCENE デリゲート

  • OnSceneActivated(UIScene ActivatedScene, bool bInitialActivation) - シーンがアクティブ シーンになると呼び出されます。ActivatedScene はアクティブになった UIScene です。シーンが初めてアクティブになった場合、bInitialActivation はTrue に設定されます。

  • OnSceneDeactivated(UIScene DeactivatedScene) - シーンが非アクティブになると呼び出されます。DeactivatedScene は、非アクティブになった UIScene です。

  • OnTopSceneChanged(UIScene NewTopScene) - この UIScene が最上位のシーンとして使用されているときに、別の UIScene が次に最上位シーンになるときに呼び出されます。NewTopScene は新たに最上位シーンになる UIScene です。UIScenes は相互に重ねることができ、この階層化の特質を使って異なるシーンをまとめて組み合わせることができます。例えば、ほとんど変化しない背景の UIScene を作成し、対話型の UIScene をその上に積み上げることができます。

  • bool ShouldModulateBackgroundAlpha(out float AlphaModulationPercent) - これを使用して、UIScenes の親シーンをレンダリングするときの透明度量を変更できます。AlphaModulationPercent は、下位のシーンをレンダリングするときに、アルファ値を変調するのに使われる値です。下位のシーンのレンダリンクにアルファ変調が適用される場合は True を返します。

UIOBJECT デリゲート

  • OnCreate(UIObject CreatedWidget, UIScreenObject CreatorContainer) - UIObject の作成時に呼び出されます。CreatedWidget は作成された UIObject、UIScreenObject はウィジェットの作成元のコンテナです。

  • OnValueChanged(UIObject Sender, int PlayerIndex) - この UIObject の値が変更されると呼び出されます。データ値を格納する UIObjects のみを対象とします。Sender はこのデリゲートを呼び出した UIObject、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • bool OnRefreshSubscriberValue(UIObject Sender, int BindingIndex) - UIObject が RefreshSubscriberValue の呼び出しを受け取ると呼び出されます。Sender はこのデリゲートを呼び出した UIObject、BindingIndex は UIObjects が複数のデータストアバインディングを持つ場合に更新されるデータストアバインディングを示し、その使用法は、このデリゲートを実装するクラスに依存します。この UIObject が値を手動で更新する場合は True を返します。

  • OnPressed(UIScreenObject EventObject, int PlayerIndex) - UIObject が選択されると呼び出されます。これが実装されていない UIObject タイプもあります。EventObject は、このデリゲートを呼び出した UIScreenObject、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnPressRepeat(UIScreenObject EventObject, int PlayerIndex) - ウィジェットが押され、ユーザーがボタンを押下状態にしているときに呼び出されます。これが実装されていないウィジェットタイプもあります。EventObject は、このデリゲートを呼び出した UIScreenObject です。PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnPressRelease(UIScreenObject EventObject, int PlayerIndex) - ウィジェットの選択が解除されると呼び出されます。これが実装されていないウィジェットタイプもあります。EventObject は、このデリゲートを呼び出した UIScreenObject です。PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • bool OnClicked(UIScreenObject EventObject, int PlayerIndex) - ウィジェットの選択が解除されると呼び出されます。これが実装されていないウィジェットタイプもあります。このデリゲートと OnPressRelease との違いは、対応するキーの押下を受け取った UIObject 上で呼び出される点にあります。OnPressRelease は、キーが解放されたときにカーソルの下にある UIObject 上で呼び出されるので、そのオブジェクトがキーの押下を受け取った UIObject と同じでないことがあります。EventObject はこのデリゲートを呼び出した UIObject です。PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnDoubleClick(UIScreenObject EventObject, int PlayerIndex) - ウィジェットがダブルクリックイベントを受け取ると呼び出されます。これが実装されていないウィジェットタイプもあります。EventObject は、このデリゲートを呼び出した UIScreenObject です。

  • bool OnQueryToolTip(UIObject Sender, out UIToolTip CustomToolTip) - 子のクラスまたはコンテナでこれを使用して、表示される標準のツールヒントをオーバーライドすることができます。Sender はツールヒントを表示する UIObject です。CustomToolTip は表示されるツールヒントです。ツール ヒントが表示される場合は True を返し、ツールヒントが表示されない場合は False を返します。

  • bool OnOpenContextMenu(UIObject Sender, int PlayerIndex, out UIContextMenu CustomContextMenu) - スクリプトでこれを使用して、カスタムのコンテキストメニュー (ユーザーが右クリックするとポップアップ表示されるメニュー) を表示することができます。Sender はコンテキストメニューを表示する UIObject です。PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。CustomContextMenu は、表示されるカスタム コンテキストメニューです。カスタム コンテキストメニューが表示される場合は True を返し、コンテキストメニューが表示されない場合は False を返します。

  • OnCloseContextMenu(UIContextMenu ContextMenu, int PlayerIndex) - システムが現在アクティブなコンテキストメニューを閉じる必要があるときに呼び出されます。ContextMenu は閉じられるコンテキストメニューです。PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnContextMenuItemSelected(UIContextMenu ContextMenu, int PlayerIndex, int ItemIndex) - ユーザーがコンテキストメニューから項目を選択すると呼び出されます。ContextMenu は、このデリゲートを呼び出したコンテキストメニューです。PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。ItemIndex は、コンテキストメニューの MenuItems 配列のインデックスです。

  • OnUIAnimEnd(UIObject AnimTarget, int AnimIndex, UIAnimationSeq AnimSeq) - UI アニメーションが終了すると必ず呼び出されます。AnimTarget はこのデリゲートを呼び出した UIObject、AnimIndex はアニメーションのインデックス、UIAnimationSeq はアニメーション シーケンスです。

12.7 - Unreal Engine 3 および Unreal Tournament 3 における他のデリゲート

Unreal Engine 3 および Unreal Tournament 3 内のデリゲートは、さまざまな機会で役に立つ手掛かりを与えてくれるので一見の価値があります。このサブセクションには、Unreal Engine 3 および Unreal Tournament 3 に存在するデリゲートのリストを提供します。

AUDIOCOMPONENT

  • OnAudioFinished(AudioComponent AC) - AudioComponent が現在の SoundCue の再生を終了すると (再生が終わったか、または Stop() が呼び出されて) 呼び出されます。AC は、このデリゲートを呼び出した AudioComponent の参照です。

GAMEINFO

  • bool CanUnpause() - これは、ゲームの一時停止解除 (unpause) を可能にするかどうかについてより具体的な条件を実装する必要があるときに利用できます。デフォルト状態ではただのトグルです。

GAMEVIEWPORTCLIENT

  • bool HandleInputKey(int ControllerId, name Key, EInputEvent EventType, float AmountDepressed, optional bool bGamepad) - これを使用して、ビューポートから受け取ったキー入力イベントを子クラスで処理することができます。イベント処理のために interactions 配列にキーイベントが渡される前に呼び出されます。ControllerId は、イベントキーをトリガしたコントローラー参照、Key は押下げられたキー、EventType は派生したイベントのタイプです。AmountDepressed はアナログタイプのコントローラーの場合に使用され、bGamepad はゲームパッドデバイスからのイベントの場合に True になります。

  • bool HandleInputAxis(int ControllerId, name Key, float Delta, float DeltaTime, bool bGamepad) - これを使用して、ビューポートから受け取った座標軸入力イベントを子クラスで処理することができます。イベント処理のために interactions 配列に座標軸イベントが渡される前に呼び出されます。ControllerId は、イベントキーをトリガしたコントローラー参照、Key は関連キー、Delta は移動デルタ (間隔)、DeltaTime は最後に座標軸が更新されてから経過した時間 (秒)、bGamepad はゲームパッドデバイスからのイベントの場合に True になります。

  • bool HandleInputChar(int ControllerId, string Unicode) - これを使用して、ビューポートから受け取った文字入力イベントを子クラスで処理することができます。イベント処理のために interactions 配列に文字イベントが渡される前に呼び出されます。ControllerId はイベントをトリガしたコントローラーを参照し、Unicode は入力された文字です。

INTERACTION

  • bool OnReceivedNativeInputKey(int ControllerId, name Key, EInputEvent EventType, optional float AmountDepressed = 1.f, optional bool bGamepad) - GameViewportClient.HandleInputKey と同じですが、GameViewportClient からのネイティブ呼び出しの場合のみ呼び出されます。

  • bool OnReceivedNativeInputAxis(int ControllerId, name Key, float Delta, float DeltaTime, optional bool bGamepad) - GameViewportClient.HandleInputAxix と同じですが、GameViewportClient からのネイティブ呼び出しの場合のみ呼び出されます。

  • bool OnReceivedNativeInputChar(int ControllerId, string Unicode) - GameViewportClient.HandleInputChar と同じですが、GameViewportClient からのネイティブ呼び出しの場合のみ呼び出されます。

  • OnInitialize() - ネイティブ実装された Init() 関数内から、(ネイティブの初期化が終了した後に) 呼び出されます。

ONLINEACCOUNTINTERFACE

  • OnCreateOnlineAccountCompleted(EOnlineAccountCreateStatus ErrorStatus) - アカウント作成ルーチンの完了後に呼び出されます。ErrorStatus は、アカウント作成に成功したかどうかを宣言します。

ONLINECONTENTINTERFACE

  • OnContentChange() - 任意のユーザーに関するコンテンツが変更されると呼び出されます。

  • OnReadContentComplete(bool bWasSuccessful) - コンテンツの読取り要求が完了すると呼び出されます。読取りに成功した場合、bWasSuccessful は True に設定されます。

  • OnQueryAvailableDownloadsComplete(bool bWasSuccessful) - コンテンツのダウンロード クエリーが完了すると呼び出されます。クエリーが成功した場合、bWasSuccessful は True に設定されます。

ONLINEGAMEINTERFACE

  • OnCreateOnlineGameComplete(bool bWasSuccessful) - オンライン ゲーム作成ルーチンが完了すると呼び出されます。ゲーム作成に成功した場合、bWasSuccessful は True に設定されます。

  • OnDestroyOnlineGameComplete(bool bWasSuccessful) - オンライン ゲームの破棄ルーチンが完了すると呼び出されます。ゲーム破棄に成功した場合、bWasSuccessful は True に設定されます。

  • OnFindOnlineGamesComplete(bool bWasSuccessful) - オンライン ゲームの検索ルーチンが完了すると呼び出されます。ゲーム検索ルーチンに成功した場合、bWasSuccessful は True に設定されます。

  • OnCancelFindOnlineGamesComplete(bool bWasSuccessful) - オンライン ゲームの検索ルーチンがキャンセルされると呼び出されます。ゲーム検索ルーチンのキャンセルに成功した場合、bWasSuccessful は True に設定されます。

  • OnJoinOnlineGameComplete(bool bWasSuccessful) - オンライン ゲームの参加ルーチンが完了すると呼び出されます。ゲーム参加に成功した場合、bWasSuccessful は True に設定されます。

  • OnRegisterPlayerComplete(bool bWasSuccessful) - プレーヤー登録ルーチンが完了すると呼び出されます。登録に成功した場合、bWasSuccessful は True に設定されます。

  • OnUnregisterPlayerComplete(bool bWasSuccessful) - プレーヤー登録解除ルーチンが完了すると呼び出されます。登録解除に成功した場合、bWasSuccessful は True に設定されます。

  • OnStartOnlineGameComplete(bool bWasSuccessful) - ゲームステートが started に変更されると呼び出されます。この非同期ルーチンに成功した場合、bWasSuccessful は True に設定されます。

  • OnEndOnlineGameComplete(bool bWasSuccessful) - ゲームステートが ended に変更されると呼び出されます。この非同期ルーチンに成功した場合、bWasSuccessful は True に設定されます。

  • OnArbitrationRegistrationComplete(bool bWasSuccessful) - ゲームがアービトレーション登録を完了すると呼び出されます。この非同期ルーチンに成功した場合、bWasSuccessful は True に設定されます。

  • OnGameInviteAccepted(OnlineGameSettings GameInviteSettings) - ユーザーがゲームへの招待を受け入れると呼び出されます。これを使用して、招待を受け入れる前に既存ステートをコード内でクリーンアップすることができます。

ONLINENEWSINTERFACE

  • OnReadGameNewsCompleted(bool bWasSuccessful) - ニュース読取りルーチンが完了すると呼び出されます。このルーチンに成功した場合、bWasSuccessful は True に設定されます。

  • OnReadContentAnnouncementsCompleted(bool bWasSuccessful) - コンテンツ発表ルーチンが完了すると呼び出されます。このルーチンに成功した場合、bWasSuccessful は True に設定されます。

ONLINEPLAYERINTERFACE

  • OnLoginChange() - ログインが変更されると呼び出されます。

  • OnLoginCancelled() - ログイン要求が取り消されると呼び出されます。

  • OnMutingChange() - ミュートリストが変更されると呼び出されます。

  • OnFriendsChange() - フレンズリストが変更されると呼び出されます。

  • OnLoginFailed(byte LocalUserNum, EOnlineServerConnectionStatus ErrorCode) - 何からの理由でログインが失敗すると呼び出されます。LocalUserNum はコントローラー ID の参照です。ErrorCode は発生したエラーを示します。

  • OnLogoutCompleted(bool bWasSuccessful) - ログアウトが完了すると呼び出されます。この非同期呼び出しが適切に終了した場合、bWasSuccessful は True に設定されます。

  • OnReadProfileSettingsComplete(bool bWasSuccessful) - 最後のプロファイル設定の読取り要求が完了すると呼び出されます。この非同期呼び出しが適切に終了した場合、bWasSuccessful は True に設定されます。

  • OnWriteProfileSettingsComplete(bool bWasSuccessful) - 最後のプロファイル設定の書き込み要求が完了すると呼び出されます。この非同期呼び出しが適切に終了した場合、bWasSuccessful は True に設定されます。

  • OnReadFriendsComplete(bool bWasSuccessful) - フレンズの読取り要求が完了すると呼び出されます。この読取り要求が適切に終了した場合、bWasSuccessful は True に設定されます。

  • OnKeyboardInputComplete(bool bWasSuccessful) - キーボード入力要求が完了すると呼び出されます。この非同期呼び出しが適切に終了した場合、bWasSuccessful は True に設定されます。

  • OnAddFriendByNameComplete(bool bWasSuccessful) - 名前によるフレンズ追加が完了すると呼び出されます。この非同期呼び出しが適切に終了した場合、bWasSuccessful は True に設定されます。

  • OnFriendInviteReceived(byte LocalUserNum, UniqueNetId RequestingPlayer, string RequestingNick, string Message) - ローカルプレーヤーのフレンズ招待が到着すると呼び出されます。LocalUserNum はローカルユーザーの参照、RequestingPlayer は当該ローカルユーザーに招待状を送信したプレーヤーを一意に識別する ID、RequestingNick は要求を送信したプレーヤーのニックネーム、Message は追加メッセージです。

  • OnReceivedGameInvite(byte LocalUserNum, string InviterName) - ローカルユーザーがゲームへの招待を受け取ると呼び出されます。LocalUserNum はローカル ユーザーの参照、InviterName は招待側の名前です。

  • OnJoinFriendGameComplete(bool bWasSuccessful) - ローカルユーザーがフレンズのゲーム参加を完了すると呼び出されます。セッションが検出され参加が完了した場合、bWasSuccessful は True に設定されます。

  • OnFriendMessageReceived(byte LocalUserNum, UniqueNetId SendingPlayer, string SendingNick, string Message) - ローカルユーザーのフレンズメッセージが到着すると呼び出されます。LocalUserNum はローカルユーザーの参照、RequestingPlayer は当該ローカルユーザーに招待状を送信したプレーヤーを一意に識別する ID、RequestingNick は要求を送信したプレーヤーのニックネーム、Message は追加メッセージです。

ONLINEPLAYERINTERFACEEX

  • OnDeviceSelectionComplete(bool bWasSuccessful) - デバイス選択要求が完了すると呼び出されます。デバイス選択が正常に終了した場合、bWasSuccessful は True に設定されます。

  • OnUnlockAchievementComplete(bool bWasSuccessful) - 成績のロック解除要求が完了すると呼び出されます。ロック解除が正常に終了した場合、bWasSuccessful は True に設定されます。

  • OnProfileDataChanged() - プレーヤーのプロファイルデータに対する外部からの変更が完了すると呼び出されます。

ONLINESTATSINTERFACE

  • OnReadOnlineStatsComplete(bool bWasSuccessful) - オンライン統計の読取りが完了すると呼び出されます。この非同期呼び出しが適切に終了した場合、bWasSuccessful は True に設定されます。

  • OnFlushOnlineStatsComplete(bool bWasSuccessful) - オンライン統計の消去が完了すると呼び出されます。この非同期呼び出しが適切に終了した場合、bWasSuccessful は True に設定されます。

  • OnRegisterHostStatGuidComplete(bool bWasSuccessful) - ホスト統計の GUID 登録が完了すると呼び出されます。この非同期呼び出しが適切に終了した場合、bWasSuccessful は True に設定されます。

ONLINESTATSREAD

  • OnStatsReadComplete() - 統計読取りが完了すると呼び出されます。

ONLINESTATSWRITE

  • OnStatsWriteComplete() - 統計書き出しが完了すると呼び出されます。

ONLINESYSTEMINTERFACE

  • OnLinkStatusChange(bool bIsConnected) - ネットワークリンクステータスが変化すると呼び出されます。何らかの接続が検出された場合、bIsConnected は True に設定されます。

  • OnExternalUIChange(bool bIsOpening) - 外部 UI 表示がそのステートを変更すると呼び出されます。UI 開いている場合、IsOpening は True に設定されます。

  • OnControllerChange(int ControllerId, bool bIsConnected) - コントローラーの接続ステートが変化すると呼び出されます。ControllerId は、接続ステートが変わったコントローラーの参照です。コントローラーが接続されている場合、bIsConnected は True に設定されます。

  • OnConnectionStatusChange(EOnlineServerConnectionStatus ConnectionStatus) - オンラインサーバーの接続ステートが変化すると呼び出されます。ConnectionStatus には、新規接続ステータスが格納されます。

  • OnStorageDeviceChange() - ストレージデバイスの変更が検出されると呼び出されます。

ONLINEVOICEINTERFACE

  • OnPlayerTalking(UniqueNetId Player) - プレーヤーがローカルまたはリモートで会話しているときに呼び出されます。各アクティブトーカー (会話者)、各フレームごとに 1 度呼び出されます。Player は会話中のプレーヤー参照です。

  • OnRecognitionComplete() - 指定プレーヤーの音声認識が完了すると呼び出されます。この次に GetRecognitionResults() を呼び出して、認識された言葉を取得することができます。

PARTICLESYSTEMCOMPONENT

  • OnSystemFinished(ParticleSystemComponent Psystem) - パーティクル システムが指定パーティクルエフェクトの「再生」を終了すると呼び出されます。Psystem は自己を参照しているので、このデリゲートを別のインスタンス内でオーバーライドすると、デリゲートを呼び出した ParticleSystemComponent にアクセスできます。

PLAYERCONTROLLER

  • bool CanUnpause() - プレーヤーのコントローラーがゲームを一時停止解除できる条件を、異なるロジックを用いて確定する必要がある場合は、これをオーバーライドします。

UICOMBOBOX

  • UIEditBox CreateCustomComboEditbox(UIComboBox EditboxOwner) - 異なるロジックを用いて編集ボックスを作成する必要がある場合、これをオーバーライドします。EditboxOwner は、デリゲートを呼び出した UIComboBox です。作成された編集ボックスを返します。

  • UIToggleButton CreateCustomComboButton(UIComboBox ButtonOwner) - 異なるロジックを用いてトグルコンボボタンを作成する必要がある場合、これをオーバーライドします。ButtonOwner は、デリゲートを呼び出した UIComboBox です。作成されたトグルボタンを返します。

  • UIList CreateCustomComboList(UIComboBox ListOwner) - 異なるロジックを用いてリストを作成する必要がある場合、これをオーバーライドします。ListOwner はデリゲートを呼び出した UIComboBox です。作成されたリストを返します。

UICOMP_DRAWCOMPONENTS

  • OnFadeComplete(UIComp_DrawComponents Sender) - フェードが完了すると呼び出されます。Sender はデリゲートを呼び出した UIComp_DrawComponent です。

UIDATAPROVIDER

  • OnDataProviderPropertyChange(UIDataProvider SourceProvider, optional name PropTag) - プロパティが変更されると呼び出されます。代わりに使用できる別のコールバックがあるので、これはデータプロバイダとデータを所有するデータストアの間で使用されるように設計されています。SourceProvider は、デリゲートを呼び出した UIDataProvider、PropTag は変更されたプロパティの名前です。

UIDATASTORE

  • OnDataStoreValueUpdated(UIDataStore SourceDataStore, bool bValuesInvalidated, name PropertyTag, UIDataProvider SourceProvider, int ArrayIndex) - このデータストアが公開する値が更新されると呼び出されます。これを使用して、このデータストアの値が更新されたときに、データストアのサブスクライバに通知ことができます。SourceDataStore はデリゲートを呼び出したデータストアです。bValuesInvalidated は、すべてのデータ値が無効であるために完全更新が必要な場合に True になります。PropertyTag は更新されたデータフィールドのタグ、SourceProvider は変更されたデータを含むデータストア、ArrayIndex は、データフィールドがデータコレクションの場合は変更された配列要素を参照し、その他の場合は INDEX_NONE (-1) になります。

UIDATASTORE_GAMESTATE

  • OnRefreshDataFieldValue() - データフィールドが更新されると呼び出されます。

UIEDITBOX

  • bool OnSubmitText(UIEditBox Sender, int PlayerIndex) - 編集ボックスにフォーカスがある間に、ユーザーが Enter を押すかまたは UIKey_SubmitText にバインドされた他のアクションを実行すると呼び出されます。Sender はこのデリゲートを呼び出した編集ボックス、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。終了時に編集ボックスをクリアしたい場合は True を返します。

UIEVENT

  • AllowEventActivation(int ControllerIndex, UIScreenObject InEventOwner, Object InEventActivator, bool bActivateImmediately, out const array IndicesToActivate) - UILIST

  • OnSubmitSelection(UIList Sender, optional int PlayerIndex = GetBestPlayerIndex()) - リストにフォーカスがある間に、ユーザーが Enter を押すかまたは UIKey_SubmitText にバインドされた他のアクションを実行すると呼び出されます。Sender はこのデリゲートを呼び出したリスト、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントを生成したプレーヤーを参照しています。

  • OnListElementsSorted(UIList Sender) - リストの要素がソートされた後で呼び出されます。Sender はこのデリゲートを呼び出したリストです。

UIOPTIONLISTBASE

  • UIOptionListButton CreateCustomDecrementButton(UIOptionListBase ButtonOwner) - 独自のデクリメント (減分) ボタンを作成したい場合はこれをオーバーライドします。ButtonOwner はオプションで、このデリゲートを呼び出したリストベースです。作成した UIOptionListButton を返します。

  • UIOptionListButton CreateCustomIncrementButton(UIOptionListBase ButtonOwner) - 独自のインクリメント (増分) ボタンを作成したい場合はこれをオーバーライドします。ButtonOwner はオプションで、このデリゲートを呼び出したリストベースです。作成した UIOptionListButton を返します。

UISCREENOBJECT

  • NotifyActiveSkinChanged() - アクティブスキンが変更されると呼び出されます。このウィジェットのスタイルを再度適用し、すべての子に通知をプロパゲートします。このデリゲートは、メンバー関数に実際に代入された場合しか呼び出されません。

  • bool OnRawInputKey(const out InputEventParameters EventParms) - これを使用して、UnrealScript で実際の入力キー名を使って入力に応答することができます。このウィジェットが応答する入力キーイベントを受け取り、ウィジェットがイベントを処理できるステートにある場合に呼び出されます。ウィジェットが入力を受け取る対象となるキーとそのステートは、UI エディタのキーバインドダイアログ (F8) で管理します。このデリゲートは Kismet の前に呼び出されます。EventParams には入力イベント情報を格納します。戻り値の True は、この入力キーが処理され、以降の処理がすべて停止されることを示します。

  • bool OnRawInputAxis(const out InputEventParameters EventParms) - OnRawInputKey と同じ。

  • OnProcessInputKey(const out SubscribedInputEventParameters EventParms) - これを使用して、UnrealScript で UI の入力エイリアスを使って入力に応答することができます。このウィジェットが応答する入力キーイベントを受け取り、ウィジェットがイベントを処理できるステートにある場合に呼び出されます。ウィジェットが入力を受け取る対象となるキーとそのステートは、UI エディタのキーバインドダイアログ (F8) で管理します。このデリゲートは、Kismetの前、および Native (ネイティブ) コードが入力を処理する前に呼び出されます。EventParams にはイベント情報を格納します。戻り値の True は、このキーが処理され、以降の処理が停止されることを示します。

  • OnProcessInputAxis(const out SubscribedInputEventParameters EventParms) - OnProcessInputKey と同じ。

  • NotifyPositionChanged(UIScreenObject Sender) - UIScreenObject の位置が変更されると呼び出されます。Sender は、位置が変更された UIScreenObject です。

  • NotifyResolutionChanged(const out Vector2D OldViewportsize, const out Vector2D NewViewportSize) - この UIScreenObject をレンダリングするビューポートが解像度を変更すると呼び出されます。OldViewportSize は前の解像度、NewViewportSize は新しい解像度です。

  • NotifyActiveStateChanged(UIScreenObject Sender, int PlayerIndex, UIState NewlyActiveState, optional UIState PreviouslyActiveState) - すべてのアクティベーションロジックが発生した後に、UIScreenObject の UIState が変更されると呼び出されます。Sender はステートが変更された UIScreenObject、PlayerIndex は Engine.GamePlayers 内のインデックスで、このステートをアクティブにしたプレーヤーを参照しています。NewlyActiveState は現在アクティブなステート、PreviouslyActiveState は UIScreenObject の前のステートです。

  • NotifyVisibilityChanged(UIScreenObject SourceWidget, bool bIsVisible) - UIScreenObject の視認性が変更されると呼び出されます。SourceWidget は視認性が変更されたウィジェットで、UIScreenObject が見える場合は bIsVisible が True に設定されます。

  • OnPreRenderCallBack() - レンダリングの前に呼び出されます。

UISCROLLBAR

  • OnScrollActivity(UIScrollbar Sender, float PositionChange, optional bool bPositionMaxed = false) - スクロールアクティビティが検出されると呼び出されます。Sender はイベントを送信した UIScrollBar、PositionChange はスクロールボタンによって変更される単位値の数です。マーカーが最大位置に達した場合は bPositionMaxed は True になります。戻り値は現在のところ使用されません。

  • OnClickedScrollZone(UIScrollbar Sender, float PositionPerc, int PlayerIndex) - ユーザーがスクロールゾーンの任意の位置をクリックすると呼び出されます。Sender はイベントを送信した UIScrollBar、PositionPerc は 0.f から 1.f までの範囲の値で、インクリメントボタンとデクリメントボタンの間のクリック位置を表します。0.F はデクリメントボタンに近く、1.F はインクリメントボタンに近い位置です。PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

UISCROLLBARMARKERBUTTON

  • OnButtonDragged(UIScrollbarMarkerButton Sender, int PlayerIndex) - ユーザーがボタンを押して、マウスを使ってそれをドラッグすると呼び出されます。Sender はこのデリゲートを呼び出した UIScrollbarMarkerButton、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

UITABBUTTON

  • IsActivationAllowed(UITabButton Sender, int PlayerIndex) - これを使用して、他の UI ウィジェットにこのボタンのアクティブ化をオーバーライドさせることができます。Sender はアクティブになった UITabButton、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

UITABCONTROL

  • OnPageActivated(UITabControl Sender, UITabPage NewlyActivePage, int PlayerIndex) - 新規ページがアクティブになると呼び出されます。Sender はこのデリゲートを呼び出した UITabControl、NewlyActivePage は現在アクティブな UITabPage、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnPageInserted(UITabControl Sender, UITabPage NewPage, int PlayerIndex) - 新規ページが挿入されると呼び出されます。Sender はこのデリゲートを呼び出した UITabControl、NewPage は新たに挿入された UITabPage、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnPageRemoved(UITabControl Sender, UITabPage OldPage, int PlayerIndex) - ページが削除されると呼び出されます。Sender はこのデリゲートを呼び出した UITabControl、OldPage は削除される予定の UITabPage、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

UITOOLTIP

  • ActivateToolTip(UIToolTip Sender) - ツールヒントがアクティブになるときに呼び出されます。Sender はデリゲートを呼び出した UIToolTip です。

  • DeactivateToolTip() - ツールヒントが非アクティブになるときに呼び出されます。

  • bool CanShowToolTip(UIToolTip Sender) - ツールヒントが表示可能かどうかを自己確認する必要があるときに呼び出されます。これを使用して、他のウィジェットでツールヒントが表示されないようにすることができます。Sender は対象の UIToolTip です。ツールヒントを表示したい場合は True を返します。

ONLINEGAMEINTERFACEIMPL

  • OnFindOnlineGamesComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnFindOnlineGamesComplete() と同じ。

  • OnCreateOnlineGameComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnCreateOnlineGameComplete() と同じ。

  • OnDestroyOnlineGameComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnDestroyOnlineGameComplete() と同じ。

  • OnCancelFindOnlineGamesComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnCancelFindOnlineGamesComplete() と同じ。

  • OnJoinOnlineGameComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnJoinOnlineGameComplete() と同じ。

  • OnRegisterPlayerComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnRegisterPlayerComplete() と同じ。

  • OnUnregisterPlayerComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnUnregisterPlayerComplete() と同じ。

  • OnStartOnlineGameComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnStartOnlineGameComplete() と同じ。

  • OnEndOnlineGameComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnEndOnlineGameComplete() と同じ。

  • OnArbitrationRegistrationComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnArbitrationRegistrationComplete() と同じ。

  • OnGameInviteAccepted(OnlineGameSettings GameInviteSettings) - Engine.OnlineGameInterface.OnGameInviteAccepted() と同じ。

ONLINEGAMEINTERFACEGAMESPY

  • OnGameInviteAccepted(OnlineGameSettings GameInviteSettings) - Engine.OnlineGameInterface.OnGameInviteAccepted() と同じ。

  • OnRegisterPlayerComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnRegisterPlayerComplete() と同じ。

  • OnUnregisterPlayerComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnUnregisterPlayerComplete() と同じ。

ONLINESUBSYSTEMGAMESPY

  • OnLoginChange() - Engine.OnlinePlayerInterface.OnLoginChange() と同じ。

  • OnLoginCancelled() - Engine.OnlinePlayerInterface.OnLoginCancelled() と同じ。

  • OnMutingChange() - Engine.OnlinePlayerInterface.OnMutingChange() と同じ。

  • OnFriendsChange() - Engine.OnlinePlayerInterface.OnFriendsChange() と同じ。

  • OnLoginFailed(byte LocalUserNum,EOnlineServerConnectionStatus ErrorCode) - Engine.OnlinePlayerInterface.OnLoginFailed() と同じ。

  • OnLogoutCompleted(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnLogoutCompleted() と同じ。

  • OnReadProfileSettingsComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnReadProfileSettingsComplete() と同じ。

  • OnWriteProfileSettingsComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnWriteProfileSettingsComplete() と同じ。

  • OnReadFriendsComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnReadFriendsComplete() と同じ。

  • OnPlayerTalking(UniqueNetId Player) - Engine.OnlineVoiceInterface.OnPlayerTalking() と同じ。

  • OnRecognitionComplete() - Engine.OnlineVoiceInterface.OnRecognitionComplete() と同じ。

  • OnReadOnlineStatsComplete(bool bWasSuccessful) - Engine.OnlineStatsInterface.OnReadOnlineStatsComplete() と同じ。

  • OnFlushOnlineStatsComplete(bool bWasSuccessful) - Engine.OnlineStatsInterface.OnFlushOnlineStatsComplete() と同じ。

  • OnLinkStatusChange(bool bIsConnected) - Engine.OnlineSystemInterface.OnLinkStatusChange() と同じ。

  • OnExternalUIChange(bool bIsOpening - Engine.OnlineSystemInterface.OnExternalUIChange() と同じ。

  • OnControllerChange(int ControllerId, bool bIsConnected) - Engine.OnlineSystemInterface.OnControllerChange() と同じ。

  • OnConnectionStatusChange(EOnlineServerConnectionStatus ConnectionStatus) - Engine.OnlineSystemInterface.OnConnectionStatusChange() と同じ。

  • OnStorageDeviceChange() - Engine.OnlineSystemInterface.OnStorageDeviceChange() と同じ。

  • OnCreateOnlineAccountCompleted(EOnlineAccountCreateStatus ErrorStatus) - Engine.OnlineAccountInterface.OnCreateOnlineAccountCompleted() と同じ。

  • OnKeyboardInputComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnKeyboardInputComplete() と同じ。

  • OnAddFriendByNameComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnAddFriendByNameComplete() と同じ。

  • OnFriendInviteReceived(byte LocalUserNum, UniqueNetId RequestingPlayer, string RequestingNick, string Message) - Engine.OnlinePlayerInterface.OnFriendInviteReceived() と同じ。

  • OnReceivedGameInvite(byte LocalUserNum, string InviterName) - Engine.OnlinePlayerInterface.OnReceivedGameInvite() と同じ。

  • OnJoinFriendGameComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnJoinFriendGameComplete() と同じ。

  • OnFriendMessageReceived(byte LocalUserNum, UniqueNetId SendingPlayer, string SendingNick, string Message) - Engine.OnlinePlayerInterface.OnJoinFriendGameComplete() と同じ。

  • OnRegisterHostStatGuidComplete(bool bWasSuccessful) - Engine.OnlineStatsInterface.OnRegisterHostStatGuidComplete() と同じ。

  • OnReadGameNewsCompleted(bool bWasSuccessful) - Engine.OnlineNewsInterface.OnReadGameNewsCompleted() と同じ。

  • OnReadContentAnnouncementsCompleted(bool bWasSuccessful) - Engine.OnlineNewsInterface.OnReadContentAnnouncementsCompleted() と同じ。

ONLINESUBSYSTEMLIVE

  • OnLoginChange() - Engine.OnlinePlayerInterface.OnLoginChange() と同じ。

  • OnLoginCancelled() - Engine.OnlinePlayerInterface.OnLoginCancelled() と同じ。

  • OnMutingChange() - Engine.OnlinePlayerInterface.OnMutingChange() と同じ。

  • OnFriendsChange() - Engine.OnlinePlayerInterface.OnFriendsChange() と同じ。

  • OnLoginFailed(byte LocalUserNum,EOnlineServerConnectionStatus ErrorCode) - Engine.OnlinePlayerInterface.OnLoginFailed() と同じ。

  • OnLogoutCompleted(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnLogoutCompleted() と同じ。

  • OnKeyboardInputComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnKeyboardInputComplete() と同じ。

  • OnLinkStatusChange(bool bIsConnected) - Engine.OnlineSystemInterface.OnLinkStatusChange() と同じ。

  • OnExternalUIChange(bool bIsOpening) - Engine.OnlineSystemInterface.OnExternalUIChange() と同じ。

  • OnControllerChange(int ControllerId, bool bIsConnected) - Engine.OnlineSystemInterface.OnControllerChange() と同じ。

  • OnConnectionStatusChange(EOnlineServerConnectionStatus ConnectionStatus) - Engine.OnlineSystemInterface.OnConnectionStatusChange() と同じ。

  • OnStorageDeviceChange() - Engine.OnlineSystemInterface.OnStorageDeviceChange() と同じ。

  • OnFindOnlineGamesComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnFindOnlineGamesComplete() と同じ。

  • OnCreateOnlineGameComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnCreateOnlineGameComplete() と同じ。

  • OnDestroyOnlineGameComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnDestroyOnlineGameComplete() と同じ。

  • OnCancelFindOnlineGamesComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnCancelFindOnlineGamesComplete() と同じ。

  • OnJoinOnlineGameComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnJoinOnlineGameComplete() と同じ。

  • OnRegisterPlayerComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnRegisterPlayerComplete() と同じ。

  • OnUnregisterPlayerComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnUnregisterPlayerComplete() と同じ。

  • OnReadProfileSettingsComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnReadProfileSettingsComplete() と同じ。

  • OnWriteProfileSettingsComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnWriteProfileSettingsComplete() と同じ。

  • OnDeviceSelectionComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterfaceEx.OnDeviceSelectionComplete() と同じ。

  • OnUnlockAchievementComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterfaceEx.OnUnlockAchievementComplete() と同じ。

  • OnProfileDataChanged() - Engine.OnlinePlayerInterfaceEx.OnProfileDataChanged() と同じ。

  • OnStartOnlineGameComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnStartOnlineGameComplete() と同じ。

  • OnEndOnlineGameComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnEndOnlineGameComplete() と同じ。

  • OnArbitrationRegistrationComplete(bool bWasSuccessful) - Engine.OnlineGameInterface.OnArbitrationRegistrationComplete() と同じ。

  • OnReadFriendsComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnReadFriendsComplete() と同じ。

  • OnGameInviteAccepted(OnlineGameSettings InviteSettings) - Engine.OnlineGameInterface.OnGameInviteAccepted() と同じ。

  • OnContentChange() - Engine.OnlineContentInterface.OnContentChange() と同じ。

  • OnReadContentComplete(bool bWasSuccessful) - Engine.OnlineContentInterface.OnReadContentComplete() と同じ。

  • OnQueryAvailableDownloadsComplete(bool bWasSuccessful) - Engine.OnlineContentInterface.OnQueryAvailableDownloadsComplete() と同じ。

  • OnPlayerTalking(UniqueNetId Player) - Engine.OnlineVoiceInterface.OnPlayerTalking() と同じ。

  • OnRecognitionComplete() - Engine.OnlineVoiceInterface.OnRecognitionComplete() と同じ。

  • OnReadOnlineStatsComplete(bool bWasSuccessful) - Engine.OnlineStatsInterface.OnReadOnlineStatsComplete() と同じ。

  • OnFlushOnlineStatsComplete(bool bWasSuccessful) - Engine.OnlineStatsInterface.OnFlushOnlineStatsComplete() と同じ。

  • OnAddFriendByNameComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnAddFriendByNameComplete() と同じ。

  • OnFriendInviteReceived(byte LocalUserNum, UniqueNetId RequestingPlayer, string RequestingNick, string Message) - Engine.OnlinePlayerInterface.OnFriendInviteReceived() と同じ。

  • OnReceivedGameInvite(byte LocalUserNum,string InviterName) - Engine.OnlinePlayerInterface.OnReceivedGameInvite() と同じ。

  • OnJoinFriendGameComplete(bool bWasSuccessful) - Engine.OnlinePlayerInterface.OnJoinFriendGameComplete() と同じ。

  • OnFriendMessageReceived(byte LocalUserNum, UniqueNetId SendingPlayer, string SendingNick, string Message) - Engine.OnlinePlayerInterface.OnFriendMessageReceived() と同じ。

  • OnRegisterHostStatGuidComplete(bool bWasSuccessful) - Engine.OnlineStatsInterface.OnRegisterHostStatGuidComplete() と同じ。

UTBOT

  • bool CustomActionFunc(UTBot B) - ボットが CustomAction ステート内にあるときに呼び出されます。B はこのデリゲートを呼び出したボットです。

UTDATASTORE_ONLINESTATS

  • OnStatsReadComplete(bool bWasSuccessful) - 統計読取りが完了すると呼び出されます。この非同期呼び出しが適切に終了した場合、bWasSuccessful は True に設定されます。

UTDEPLOYEDACTOR

  • OnDeployableUsedUp(actor ChildDeployable) - デプロイ (配備) されたアクタが破壊されようとするときに呼び出されます。ChildDeployable は自己破壊するアクタです。

UTDRAWMAPPANEL

  • OnActorSelected(Actor Selected, UTPlayerController SelectedBy) - ノードがダブルクリックされると呼び出されます。Selected は選択されたアクタ、SelectedBy は選択を行った UTPlayerController です。

UTEXPLOSIONLIGHT

  • OnLightFinished(UTExplosionLight Light) - ライトが終了し、光を放たなくなったときに呼び出されます。Light はこのデリゲートを呼び出したアクタです。

UTKACTOR

  • OnBreakApart() - 物理アクタが分裂するときに呼び出されます。

  • bool OnEncroach(actor Other) - 物理アクタがエンクローチ (侵害) されるときに呼び出されます。Other は、この物理アクタを侵害するアクタです。

UTMISSIONGRI

  • OnBinkMovieFinished() - ムービー再生が終了すると呼び出されます。

UTSCOREBOARDPANEL

  • OnSelectionChange(UTScoreboardPanel TargetScoreboard, UTPlayerReplicationInfo PRI) - 選択が変更されると呼び出されます。TargetScoreboard はこのデリゲートを呼び出したスコアボード、PRI は選択されたプレーヤー複製情報iです。

UTSIMPLEIMAGELIST

  • bool OnDrawItem(UTSimpleImageList SimpleList, int ItemIndex, float Xpos, out float Ypos) - アイテムが描かれるときに呼び出されます。SimpleList はデリゲートを呼び出したリスト、ItemIndex は List 配列のインデックス、Xpos はアイテムの描画位置の X 座標値、Ypos は同じ位置の Y 座標です。デフォルトの方法でアイテムを描画する場合は False を返します。

  • OnItemChosen(UTSimpleImageList SourceList, int SelectedIndex, int PlayerIndex) - リスト内の項目が選択されたときに呼び出されます。SourceList はこのデリゲートを呼び出したリスト、SelectedIndex は新たに選択されたインデックス、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnSelectionChange(UTSimpleImageList SourceList, int NewSelectedIndex) - 選択インデックスが変更されると呼び出されます。SourceList はデリゲートを呼び出したリスト、NewSelectedIndex は新規選択されたインデックスです。

UTSIMPLELIST

  • bool OnDrawItem(UTSimpleList SimpleList, int ItemIndex, float XPos, out float Ypos) - アイテムが描かれるときに呼び出されます。SimpleList はデリゲートを呼び出したリスト、ItemIndex は List 配列のインデックス、Xpos はアイテムの描画位置の X 座標値、Ypos は同じ位置の Y 座標です。デフォルトの方法でアイテムを描画する場合は False を返します。

  • bool OnDrawSelectionBar(UTSimpleList SimpleList, float Ypos) - 選択バーが描かれるときに呼び出されます。SimpleList はデリゲートを呼び出したリスト、Ypos はアイテムを描画する位置の Y 座標です。デフォルトの方法で選択バーを描画する場合は False を返します。

  • bool OnPostDrawSelectionBar(UTSimpleList SimpleList, float YPos, float Width, float Height) - 選択バーが描かれた後で呼び出されます。SimpleList はデリゲートを呼び出したリスト、Ypos は選択バーが描かれた位置の Y 座標、Width は描かれた選択バーの幅、Height は同じくその高さです。戻り値を使用していません。

  • OnItemChosen(UTSimpleList SourceList, int SelectedIndex, int PlayerIndex) - リスト内の項目が選択されたときに呼び出されます。SourceList はこのデリゲートを呼び出したリスト、SelectedIndex は新たに選択されたインデックス、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnSelectionChange(UTSimpleList SourceList, int NewSelectedIndex) - 選択インデックスが変更されると呼び出されます。SourceList はデリゲートを呼び出したリスト、NewSelectedIndex は新規選択されたインデックスです。

UTSKELCONTROL_CANTILEVERBEAM

  • vector EntireBeamVelocity() - ビーム全体の移動速度を返します。

UTSKELCONTROL_TURRETCONSTRAINED

  • OnTurretStatusChange(bool bIsMoving) - Turret (砲台) のステータスが変更されると呼び出されます。移動していると判断された場合、bIsMoving は True に設定されます。

UTSLOWVOLUME

  • OnDeployableUsedUp(actor ChildDeployable) - UTGame.UTDeployedActor.OnDeployableUsedUp() と同じ。

UTTABPAGE.UC

  • OnTick(float DeltaTime) - ティックのたびに呼び出されます。DeltaTime は、各ティックイベントの間隔 (秒) です。

UTUIFRONTEND_BINDKEYS360

  • MarkDirty() - プロファイルを dirty とマークする目的で呼び出されます。

UTUIFRONTEND_BINDKEYSPC

  • MarkDirty() - プロファイルを dirty とマークする目的で呼び出されます。

UTUIFRONTEND_BINDKEYSPS3

  • MarkDirty() - プロファイルを dirty とマークする目的で呼び出されます。

UTUIFRONTEND_BOTSELECTION

  • OnAcceptedBots() - ユーザーが現在のボット選択設定を受け入れると呼び出されます。

UTUIFRONTEND_SETTINGSPANELS

  • OnMarkProfileDirty(optional bool bDirty = true) - ユーザー以外の誰かによってプロファイルが編集され、オプション値が変更されると呼び出されます。プロファイル dirty とマークするには bDirty を True に設定します。

  • OnNotifyOptionChanged(UIScreenObject InObject, name OptionName, int PlayerIndex) - ユーザーがオプションリスト内のいずれかのオプションを変更すると呼び出されます。InObject はこのデリゲートを呼び出した UIScreenObject、OptionName は変更されたオプションの名前、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。---+++UTUIFRONTEND_WEAPONPREFERENCE

  • MarkDirty() - プロファイルを dirty とマークする目的で呼び出されます。

UTUIMENULIST

  • OnSubmitSelection(UIObject Sender, optional int PlayerIndex = GetBestPlayerIndex()) - フォーカスがリストにあるときに、ユーザーが Enter キーまたは UIKey_SubmitListSelection にバインドされている他のアクションボタンを押すと呼び出されます。Sender はこのデリゲートを呼び出した UIObject、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

UTUIOPTIONLIST

  • OnOptionFocused(UIScreenObject InObject, UIDataProvider OptionProvider) - オプションがフォーカスを得ると呼び出されます。InObject はこのデリゲートを呼び出した UIScreenObject、OptionProvider はオプションのデータ プロバイダです。

  • OnOptionChanged(UIScreenObject InObject, name OptionName, int PlayerIndex) - オプションが変更されると呼び出されます。InObject は、このデリゲートを呼び出した UIScreenObject、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnAcceptOptions(UIScreenObject InObject, int PlayerIndex) - オプションリストの [Accept] (適用) ボタンが押されたときに呼び出されます。InObject は、このデリゲートを呼び出した UIScreenObject、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

UTUIPANEL_MAPCYCLE

  • OnMapSelected() - ユーザーがこのページでマップを選択すると呼び出されます。

UTUIPANEL_SINGLEMAP

  • OnMapSelected() - ユーザーがこのページでマップを選択すると呼び出されます。

UTUIPRESSBUTTON

  • OnBeginPress(UIScreenObject InObject, int InPlayerIndex) - ユーザーがボタンを押すと同時に呼び出されます。InObject は、このデリゲートを呼び出した UIScreenObject、InPlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnEndPress(UIScreenObject InObject, int InPlayerIndex) - ユーザーがボタンの上で左マウスボタンを解放すると呼び出されます。InObject は、このデリゲートを呼び出した UIScreenObject、InPlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

UTUISCENE

  • OnShowAnimationEnded() - シーンの Show animation (アニメーション表示) が終了したときに呼び出されます。

  • OnHideAnimationEnded() - シーンの Hide animation (アニメーション非表示) が終了したときに呼び出されます。

  • OnSceneOpened(UIScene OpenedScene, bool bInitialActivation) - 最上位のシーンを非表示にした後でシーンが開いたときに呼び出されます。OpenedScene はこのデリゲートを呼び出したシーンです。開いたシーンが最初にアクティブになったときは、bInitialActivation が True に設定されます。

UTUISCENE_MESSAGEBOX

  • OnSelection(UTUIScene_MessageBox MessageBox, int SelectedOption, int PlayerIndex) - 利用可能な選択肢からユーザーが項目を選択すると呼び出されます。MessageBox はこの関数を呼び出した UTUIScene_MessageBox、SelectionOption は選択された項目、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnClosed() - メッセージ ボックスが完全に閉じられると呼び出されます。

  • bool OnMBInputKey(const out InputEventParameters EventParms) - メッセージボックスが任意の入力を受け取ると呼び出されます。EventParams には入力イベント情報を格納します。入力データが処理済みで、それ以上の処理が不要な場合は True を返します。

UTUISCENE_SAVEPROFILE

  • OnSaveFinished() - プロファイルの保存が完了すると呼び出されます。

UTUITABPAGE_CHARACTERPART

  • transient OnPartSelected(ECharPart PartType, string InPartID) - ユーザーがこのページでパーツを選択すると呼び出されます。PartType には選択されたパーツの情報、PartID は指定 PartType の ID です。

  • transient OnPreviewPartChanged(ECharPart PartType, string InPartID) - ユーザーがこのページ上で選択パーツを変更すると呼び出されます。PartType には選択されたパーツの情報、PartID は指定 PartType の ID です。

UTUITABPAGE_FINDQUICKMATCH

  • OnSearchComplete(bool bWasSuccessful) - 検索が完了すると呼び出されます。この非同期呼び出しが正常に完了した場合、bWasSuccessful は True に設定されます。

UTUITABPAGE_GAMEMODESELECTION

  • OnGameModeSelected(string InGameMode, string InDefaultMap, string GameSettingsClass, bool bSelectionSubmitted) - ゲーム モードがこのページから選択されると呼び出されます。InGameMode は選択されたゲームモード、InDefaultMap は選択されたゲームモードのデフォルト マップ、GameSettingsClass はゲーム設定のクラス名です。選択項目が送信されている場合、bSelectionSubmitted は True に設定されます。

UTUITABPAGE_MAPSELECTION

  • OnMapSelected() - ユーザーがこのページでマップを選択すると呼び出されます。

UTUITABPAGE_MUTATORS

  • OnAcceptMutators(string InEnabledMutators) - ユーザーが現在のミューテーターセットを受け入れると呼び出されます。InEnabledMutators は承諾されたミューテーターのリストです。

UTUITABPAGE_OPTIONS

  • OnAcceptOptions(UIScreenObject InObject, int PlayerIndex) - 現在のオプションが受け入れられると呼び出されます。InObject は、このデリゲートを呼び出した UIScreenObject、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnOptionChanged(UIScreenObject InObject, name OptionName, int PlayerIndex) - This is called when one of the options on the page has changed. ページ上のオプションの 1 つが変更されると呼び出されます。InObject はこのデリゲートを呼び出した UIScreenObject、OptionName はオプションの名前、PlayerIndex は Engine.GamePlayers 内のインデックスで、イベントをトリガしたプレーヤーを参照しています。

  • OnOptionFocused(UIScreenObject InObject, UIDataProvider OptionProvider) - オプションの 1 つがフォーカスを得ると呼び出されます。InObject はこのデリゲートを呼び出した UIScreenObject、OptionProvider はフォーカスを得たオプションのデータ プロバイダです。

UTUITABPAGE_SERVERBROWSER

  • transient OnBack() - ユーザーが戻るときに呼び出されます。

  • transient OnSwitchedGameType() - ユーザーがコンボボックスを使ってゲームタイプを変更すると呼び出されます。

  • transient OnPrepareToSubmitQuery(UTUITabPage_ServerBrowser Sender) - ユーザーがサーバークエリーを実行するときに呼び出されます。Sender はこのデリゲートを呼び出した UTUITabPage_ServerBrowser です。

UTUITABPAGE_SERVERFILTER

  • transient OnSwitchedGameType() - ユーザーがゲームタイプを変更すると呼び出されます。

チュートリアル 12.1 - ランダムイベントミューテーター、パート I: 入門 & クラスの初期設定

以下の一連のチュートリアルに従い、対戦中にプレーヤーのランダムイベントを作成するミューテーターを作成します。

1. 任意のテキストエディタを開き、UTMutator_RandomEvents.UC という名前の新規ファイルを作成します。

2. スクリプトのクラス宣言から開始します。ミューテーターを作成するので、Engine 内の Mutator クラスのサブクラスを作成します。先頭行は次のようになります。

  class UTMutator_RandomEvent extends UTMutator;
  

3. 作業を進める前に、Unreal Tournament 3 をもっと面白くするイベントを検討する必要がありますが、時間を節約するために、ここでは私のアイデアをいくつか紹介します。

  • すべてのプレーヤーがリディーマー (redeemer) を受け取る。
  • プレーヤー全員を調べて、現在の体力が 50 以上の場合にアーマーを与える。
  • マップ上のすべてのアイテムの再スポーンを強制する。

4. defaultproperties ブロックも作成しましょう。このミューテーターにはグローバル変数がないので、中身がないように見えます。

  defaultproperties
  {
     Name=”Default__UTMutator_RandomEvent”
  }
  

ここでこのクラスをコンパイルできます。

チュートリアル 12.2 - ランダムイベントミューテーター、パート II: タイミングロジック

次に、タイミングコードを書く必要があります。ミューテーター本体の任務は、60 秒ごとにランダムイベントをトリガすることです。

1. PostBeginPlay() 関数のオーバーライドから始めます。PostBeginPlay() はレベルが初期化されて準備が完了し、ゲーム開始の前、ロード直後に呼び出されることを思い出してください。

  function PostBeginPlay()
  {
     super.PostBeginPlay();
  }
  

2. ランダムイベントのトリガにはイベントタイマーを使います。

  function PostBeginPlay()
  {
     super.PostBeginPlay();
  
     SetTimer(60.f, true);
  }
  

3. 次に、Timer という名前の新しい関数を作成します。

  function Timer()
  {
  }
  

ミューテーターが PostBeginPlay() を呼び出すと、60 秒ごとにトリガする新しいタイマーを作成して割り当てます。タイマー停止の指示を受けるまで連続ループするように、True 引数が提供されています。デフォルトでは、SetTimer() はこの関数を呼び出したインスタンス内で Timer() という関数を呼び出します。

チュートリアル 12.3 - ランダムイベントミューテーター、パートIII: デリゲートの使用

1. デリゲート関数を作成したので、次は Timer 関数を変更します。

  delegate RandomEvent();
  
  function Timer()
  {
     RandomEvent();
  }
  

Timer() 関数が呼び出されると、デリゲート RandomEvent() が呼び出されます。

2. 次に、前述した個々のランダムイベントのロジックを処理する 3 つの関数を作成し、実行されるイベントの変更ロジックにも対処します。

  function GiveBonusArmor()
  {
  }
  
  function GiveRedeemerToAll()
  {
  }
  
  function ForceRespawn()
  {
  }
  

3. 次に、イベントのランダムな選択を処理するために、Timer() 関数を変更します。

  function Timer()
  {
     switch (Rand(3))
     {
        case 0:
           RandomEvent = GiveBonusArmor;
           break;
  
        case 1:
           RandomEvent = GiveRedeemerToAll;
           break;
  
        case 2:
           RandomEvent = ForceRespawn;
           break;
  
        default:
           RandomEvent = GiveBonusArmor;
           break;
     }
  
     RandomEvent();
  }
  

上記のコードから、Timer() が呼び出されるたびに、switch 内でランダム呼び出しが実行されるのが分かります。ランダム化の結果に応じて RandomEvent() をいずれかの関数に割り当ててから、RandomEvent() を呼び出してデリゲートを割り当てた関数を順に呼び出しています。

チュートリアル 12.4 - ランダムイベントミューテーター、パート IV: GIVEBONUSARMOR()

1. WorldInfo インスタンス内にあるイテレータ (反復子) 関数を使うと、ワールド内に既存するすべてのポーンを反復処理することができます。この反復子を使って、マップ内のすべてのプレーヤーポーンを見つけます。GiveBonusArmor() 関数を次のように変更しましょう。

  function GiveBonusArmor()
  {
     local UTPawn P;
  
     foreach WorldInfo.AllPawns(class'UTPawn', P)
     {
     }
  }
  

これにより、GiveBonusArmor() 関数が実行されると、レベル内で UTPawn クラスか、または UTPawn の子クラスのメンバーであるポーンをすべて反復処理し、その結果をローカル変数 P に出力します。

2. 次に、反復子から得られたポーンをフィルタして、目的の条件に一致するものを選択する必要があります。この条件とは、ポーンがボーナスアーマーの報酬を受けるには、その体力が 50 以上でなければならない、というものです。そこで、次のように条件文の if ステートメントを追加します。

  function GiveBonusArmor()
  {
     local UTPawn P;
  
     foreach WorldInfo.AllPawns(class'UTPawn', P)
     {
        if (P != none && P.Health >= 50)
        {
        }
     }
  }
  

反覆子が P を返す場合、これが none であることは通常あり得ないのですが、常に none チェックを行うのは良いことです。これが良い習慣である理由は、このチェックはわずかなコストで Access None エラーを完璧に防止するためです。none チェックの次に、ポーンの体力をチェックしています。

3. Unreal Tournament 3 にはプレーヤーに 3 種類のアーマーを与えることができます。体力の多いプレーヤーに与えるアーマーを増やすことができます。

  function GiveBonusArmor()
  {
     local UTPawn P;
  
     foreach WorldInfo.AllPawns(class'UTPawn', P)
     {
        if (P != none && P.Health >= 50)
        {
           P.ThighpadArmor = Max(class'UTArmorPickup_Thighpads'.default.ShieldAmount, P.ThighpadArmor);
  
           if (P.Health >= 80)
              P.VestArmor = Max(class'UTArmorPickup_Vest'.default.ShieldAmount, P.VestArmor);
  
           if (P.Health >= 90)
              P.HelmetArmor = Max(class'UTArmorPickup_Helmet'.default.ShieldAmount, P.HelmetArmor);
        }
     }
  }
  

これにより、最初の体力要件 (50 以上) を満したプレーヤーには、常にサイパッド (太ももパッド) が与えられます。体力が 80 点以上の場合はベストアーマーも提供し、90 点以上のプレーヤーにはヘルメットアーマーも提供します。

4. サウンドも追加して、プレーヤーがボーナスを受け取るときに何が聞こえるようにします。次のように関数を変更しましょう。

  function GiveBonusArmor()
  {
     local UTPawn P;
     local SoundCue S;
  
     foreach WorldInfo.AllPawns(class'UTPawn', P)
     {
        if (P != none && P.Health >= 50)
        {
           P.ThighpadArmor = Max(class'UTArmorPickup_Thighpads'.default.ShieldAmount, P.ThighpadArmor);
           S = class'UTArmorPickup_Thighpads'.default.PickupSound;
  
           if (P.Health >= 80)
           {
              P.VestArmor = Max(class'UTArmorPickup_Vest'.default.ShieldAmount, P.VestArmor);
              S = class'UTArmorPickup_Vest'.default.PickupSound;
           }
  
           if (P.Health >= 90)
           {
              P.HelmetArmor = Max(class'UTArmorPickup_Helmet'.default.ShieldAmount, P.HelmetArmor);
              S = class'UTArmorPickup_Helmet'.default.PickupSound;
           }
  
           if (S != none)
              P.PlaySound(S);
        }
     }
  }
  

ローカル SoundCue 変数を追加し、アーマーを与えるときにこれを設定できるようにしました。プレーヤーが種々の要件を満たした結果に合わせて、この変数を再生したい SoundCue の 1 つに設定しています。最後に、変数 S に割り当てられたサウンドキューがあるかどうかをチェックし (デフォルト変数に、常に none 以外の値を割り当てる必要はない点を思い出してください)、割り当てられている場合は、ボーナスを与えたポーンにそれを再生するように指示しています。

チュートリアル 12.5 - ランダムイベントミューテーター、パート V: GIVEREDEEMERTOALL

1. GiveBonusArmor() 関数と同様に、この関数もワールド内のすべてのポーンを反復処理する関数の作成から開始しましょう。

  function GiveRedeemerToAll()
  {
     locale UTPawn P;
  
     foreach WorldInfo.AllPawns(class'UTPawn', P)
     {
     }
  }
  

2. プレーヤーは全員リディーマーを受け取るので、その条件を書く必要はありません。リディーマーを与えるだけにします。これにはリディーマーのインベントリ項目をスポーンしてから、各ポーンにそれを提供しなければならず、そこでリディーマーをスポーンし、変数に参照を割り当てて使用できるようにします。上記の関数を次のように変更します。

  function GiveRedeemerToAll()
  {
     local UTPawn P;
     local UTWeap_Redeemer_Content R;
  
     foreach WorldInfo.AllPawns(class'UTPawn', P)
     {
        R = Spawn(class'UTWeap_Redeemer_Content');
     }
  }
  

ただし、これだけではあまり成果は得られません。リディーマーがどこからスポーンされるか分からないので、運のい 1 人だけが偶然にもそれらをすべて獲得してしまう可能性があります (実際には、プレーヤーは武器そのものはではなく、武器のピックアップを収集するのでその可能性は恐らくありませんが)。

3. リディーマーをスポーンしたので、次はこれをプレーヤーに与える必要があります。最初に、リディーマーを与えるプレーヤーが存在するかどうかを実際にチェックします。ポーンの存在を調べるついでに、それが有効なポーンであるかどうかも調べておきましょう。それから、適切なパラメータを指定してリディーマーをスポーンします。

  function GiveRedeemerToAll()
  {
     local UTPawn P;
     local UTWeap_Redeemer_Content R;
  
     foreach WorldInfo.AllPawns(class'UTPawn', P)
     {
        if (P != none && P.bCanPickupInventory && P.Health > 0 && P.Controller != none)
           R = Spawn(class'UTWeap_Redeemer_Content', P,, P.Location, P.Rotation);
     }
  }
  

条件節はかなり複雑に見えるので、ここで確認しておきましょう。

  • P は none であるかどうか?
  • P はインベントリアイテムを収集できるかどうか?
  • P に体力があるか?
  • P は有効なコントローラーを持っているか?

4. ここで漸くプレーヤーにリディーマーを与えることができます。これは次のように行います。

  function GiveRedeemerToAll()
  {
     local UTPawn P;
     local UTWeap_Redeemer_Content R;
  
     foreach WorldInfo.AllPawns(class'UTPawn', P)
     {
        if (P != none && P.bCanPickupInventory && P.Health > 0 && P.Controller != none)
        {
           R = Spawn(class'UTWeap_Redeemer_Content', P,, P.Location, P.Rotation);
  
           if (R != none)
           {
              if (WorldInfo.Game.PickupQuery(P, class'UTWeap_Redeemer_Content', R))
                 R.GiveTo(P);
              else
                 R.Destroy();
           }
        }
     }
  }
  

もう一度、R が実際にスポーンされたかどうかをチェックします。さまざまな理由でスポーンが失敗に終わることがあり、R が none になる可能性があるためです。Access None エラーを回避するために R の有効性を確認します。最後にもう一度チェックし、ポーンがリディーマーを収集できるかどうかを確認します。収集できる場合は、ポーンにこれを渡して、スポーンしたリディーマーを破壊します。

チュートリアル 12.6 - ランダムイベントミューテーター、パート VI: FORCERESPAWN

1. 最初に実行すべきことの 1 つは、動的配列を作成し、それにピックアップファクトリの参照を挿入する作業です。ミューテーター クラスにグローバル動的配列を追加することから始めましょう。

  class UTMutator_RandomEvent extends UTMutator;
  
  private var array<UTPickupFactory> PickupFactories;
  

このグローバル変数をプライベートにした理由は、他のクラスが配列を変更できないようにするためです。

2. ピックアップファクトリ参照を格納するグローバル変数ができたので、次に PostBeginPlay() を変更し、この動的配列に挿入します。

  function PostBeginPlay()
  {
     local UTPickupFactory pickup_factory;
  
     super.PostBeginPlay();
     SetTimer(60.f, true);
  
     foreach AllActors(class'UTPickupFactory', pickup_factory)
     {
     }
  }
  

これは UTPickupFactory またはそのサブクラスのいずれかに属するすべてのアクタを対象に、レベル全体を反復処理します。

3. この反復処理内で、各結果を検証してから格納することにします。

  function PostBeginPlay()
  {
     local UTPickupFactory pickup_factory;
  
     super.PostBeginPlay();
     SetTimer(60.f, true);
  
     foreach AllActors(class'UTPickupFactory', pickup_factory)
     {
        if (pickup_factory != none)
           PickupFactories.AddItem(pickup_factory);
     }
  }
  

使用するピックアップファクトリの動的配列をこのように設定した訳ですが、設定の理由はこの AllActors 反復子が非常に遅く、強制的に再スポーンさせたいときに常にこれを使うと無駄になるためです。

4. 次に、ForceRespawn 関数を書きます。

  function ForceRespawn()
  {
     local int i;
  
     for (i = 0; i < PickupFactories.length; ++i)
     {
     }
  }
  

5. 最後に、すべてのピックアップファクトリをリセットします。

  function ForceRespawn()
  {
     locale int i;
  
     for (i = 0; i < PickupFactories.length; ++i)
        if (PickupFactories[i] != none)
           PickupFactories[i].Reset();
  }
  

チュートリアル 12.6 - ランダムイベントミューテーター、テスティング

1. コードをコンパイルして、Unreal Tournament 3 を起動します。

2. ログインするか、オフラインプレイを選択します。

3. [Instant Action] (インスタントアクション) ゲームを選択します。

4. ゲームタイプに [Deathmatch] (デスマッチ) を選択し、任意のマップを選択します。

5. [Settings] (設定) パネルに進み、[Mutators] ボタンを押します。


図 12.1 - Mutators ボタンを押すと、Mutator の選択および設定用の画面が開きます。

6. [Enabled Mutators] (有効なミューテーター) のリストに [UTMutator_RandomEvent] ミューテーターを追加します。


図 12.2 - ミューテーターが追加されました。

7. しばらく待機すると、3 つのランダムイベントのいずれかが発生するのがわかります。ここでは報酬としてシールドが与えられました。


図 12.3 - プレーヤーにシールドが与えられました。

このチュートリアルでは、1 つのインスタンスにおけるデリゲートの作成および使用方法に触れました。この状態から Mutator を機能拡張して、いずれ発生するであろうイベントを簡単に作成することもできます。タイミングロジックの保守はあまり気にかける必要がないので、この方法で柔軟に事を運ぶことができます。

チュートリアル 12.8 - 武器のミューテーター、パート I: 入門 & クラスの初期設定

このチュートリアルでは、プレーヤーが発射タイプを変更できる武器を作成します。各発射タイプはそれぞれ既存の武器 (ロケットランチャー、フラックキャノン (高射砲)、ショックライフル、バイオライフル) をシミュレートします。ロケットランチャー (rocket launcher) スタイルは即発性の爆薬、フラックキャノン (flak cannon) スタイルは対空砲火 (flak) も発射する即発性の爆薬、ショックライフル (shock rifle) スタイルは即発性のショックコンボ、バイオライフルスタイルは goo blob (ねばねばした塊) も落とす即発性の粘着性爆薬になります。

1. 最初に、..\MasteringUnrealScript\Classes フォルダに、新規 Unrealscript ファイル (UTWeap_MultiEnforcer.uc、MultiEnforcer_Base.uc、MultiEnforcer_Bio.uc、MultiEnforcer_Flak.uc、MultiEnforcer_Rocket.uc および MultiEnforcer_Shock.uc) を作成します。

2. UTWeap_MultiEnforcer のクラスおよびデフォルトプロパティを宣言します。新しい武器の設定に必要な作業を出来るだけ少なくしたいので、UTWeap_Enforcer のサブクラスにします。

  class UTWeap_MultiEnforcer extends UTWeap_Enforcer
  
  defaultproperties
  {
     Name=”Default__ UTWeap_MultiEnforcer”
  }
  

3. MultiEnforcer_Base のクラスおよびデフォルトプロパティを宣言します。

  class MultiEnforcer_Base extends Object;
  
  defaultproperties
  {
     Name=”Default__MultiEnforcer_Base"
  }
  

4. MultiEnforcer_Bio のクラスおよびデフォルトプロパティを宣言します。基本的な機能は MultiEnforcer_Base 内で処理するので、MultiEnforcer_Base のサブクラスにします。

  class MultiEnforcer_Bio extends MultiEnforcer_Base;
  
  defaultproperties
  {
     Name="Default__MultiEnforcer_Bio"
  }
  

5. MultiEnforcer_Flak のクラスおよびデフォルトプロパティを宣言します。

  class MultiEnforcer_Flak extends MultiEnforcer_Base;
  
  defaultproperties
  {
     Name="Default__MultiEnforcer_Flak"
  }
  

6. MultiEnforcer_Rocket のクラスおよびデフォルトプロパティを宣言します。

  class MultiEnforcer_Rocket extends MultiEnforcer_Base;
  
  defaultproperties
  {
     Name="Default__MultiEnforcer_Rocket"
  }
  

7. MultiEnforcer_Shock のクラスおよびデフォルトプロパティを宣言します。

  class MultiEnforcer_Shock extends MultiEnforcer_Base;
  
  defaultproperties
  {
     Name="Default__MultiEnforcer_Shock"
  }
  

8. 新しいスクリプトをすべて保存します。

チュートリアル 12.8 - 武器のミューテーター、パート II: UTWEAP_MULTIENFORCER の設定

このクラスは武器そのものを表し、武器に関連することをほぼすべて処理します。これには、プレーヤーが武器を構えているようにメッシュを表示するなどのビジュアル関係、武器により生成されるサウンドエフェクト、武器の発射の管理などが含まれます。UTWeap_Enforcer をサブクラス化したので、作業の大半は既に完了しています。そこで、こちらが意図したとおりに武器が動作するように変更する作業に時間を費やすことができます。

1. この武器は 4 つの発射クラス (MultiEnforcer_Bio、MultiEnforcer_Flak、MultiEnforcer_Rocket および MultiEnforcer_Shock) に依存します。すべて MultiEnforcer_Base のサブクラスなので、MultiEnforcer_Base との従属関係を設定するだけです。

  class UTWeap_MultiEnforcer extends UTWeap_Enforcer
     dependson(MultiEnforcer_Base);
  

2. 発射タイプのデータを格納するために、グローバル変数をいくつか確保する必要があります。

  var private array<MultiEnforcer_Base> FireTypes;
  var private int CurrentIndex;
  var const array< class<MultiEnforcer_Base> > FireTypeClasses;
  

FireTypes は、MultiEnforcer_Base のオブジェクトインスタンスを保持するプライベート配列で、MultiEnforcer_Base の子クラスも保持します。FireTypes がプライベートなのは、他のクラスのアクセスをブロックするためです。CurrentIndex は、FireTypes 内の現在のインデックスを保持する整数で、これでプレーヤーが使用したい発射タイプを設定します。CurrentIndex がプライベートなのは、他のクラスのアクセスをブロックするためです。FireTypeClasses は、武器が使用できる発射タイプのクラスを保持する配列です。これは定数であり、実行中にこの配列を変更する必要はありません。「> >」の間の空白文字に注意してください。この空白がなければ UnrealScript コンパイラはエラーを生成するので忘れないでください。

3. デフォルトプロパティの定義を追加しましょう。

  defaultproperties
  {
     CurrentIndex=0
     FireTypeClasses(0)=class'MultiEnforcer_Rocket'
     FireTypeClasses(1)=class'MultiEnforcer_Shock'
     FireTypeClasses(2)=class'MultiEnforcer_Flak'
     FireTypeClasses(3)=class'MultiEnforcer_Bio'
     InventoryGroup=3
     ItemName="MultiEnforcer"
     PickupMessage="MultiEnforcer"
     FiringStatesArray(1)="WeaponSwitching"
     Name="Default__UTWeap_MultiEnforcer"
     ObjectArchetype=UTWeap_Enforcer'UTGame.Default__UTWeap_Enforcer'
  }
  

CurrentIndex は最初 0 に設定します。FireTypeClasses の配列項目はすべて実行時に一定なので、ここで定義しておきます。InventoryGroup は親クラスで定義される変数で、この武器の所属グループを決定します。ItemName は武器の名前です。PickupMessage は、武器の収集時に出るメッセージで、ここではとりあえず 「MultiEnforcer」と呼んでいます。FiringStatesArray は発射モードの実行時の武器のステート名のリストです。WeaponSwitching という二次的な発射ステートも作成し、この配列に含めておきます。Name はこのオブジェクトの名前で、他のクラスで参照する場合に使用します。ObjectArchetype はこのオブジェクトの親クラスで、他のデフォルトプロパティの大半はここから派生させます。

4. 武器の初期設定を処理する PostBeginPlay() 関数の作成を始めましょう。

  function PostBeginPlay()
  {
     super.PostBeginPlay();
  }
  

5. この武器が適切に動作するように、PostBeginPlay() に発射タイプのオブジェクトのインスタンスを作成する必要があります。

  function PostBeginPlay()
  {
     local int i;
  
     super.PostBeginPlay();
  
     if (FireTypeClasses.length > 0)
     {
        for (i = 0; i < FireTypeClasses.length; ++i)
           if (FireTypeClasses[i] != none)
              FireTypes.AddItem(new FireTypeClasses[i]);
     }
  }
  

ここでは、最初に FireTypeClasses 配列に何か項目が含まれているかどうかを調べます。何も存在しなければ、その先に進む意味がないからです。次に、配列を反復処理して各発射タイプの新しいオブジェクト インスタンスを作成すると同時に、それらを FireTypes 配列に追加します。ここではキーワード new が使われていますが、その理由は最終的にはこれらのクラスを Actor ではなくオブジェクトから派生する (それによって Spawn() を使用できる) ためです。

6. 最終的にデリゲートを使用して発射タイプ オブジェクトをバインドするので、メモリリークしない方法でこれをクリーンアップする必要があります(セクション 12.5 のデリゲートとメモリに関する説明を参照してください)。そのために、Destroyed() 関数をオーバーライドします。Destroyed() は、Destroy() によりインスタンスが破壊されたとき、または Unreal Engine によってインスタンスが破壊されたときに自動的に呼び出されます。

  simulated function Destroyed()
  {
     super.Destroyed();
  }
  

7. オブジェクト インスタンスを破壊することはできないので、ここで必要なのは、インスタンス参照を消去し、Unreal Engine のガーベジコレクションによってメモリからそれらをすべて削除できるようにすることです。

  simulated function Destroyed()
  {
     local int i;
  
     super.Destroyed();
  
     if (FireTypes.length > 0)
     {
        for (i = 0; i < FireTypes.length; ++i)
           FireTypes[i] = none;
     }
  }
  

8. メモリ問題の処理が済んだので、武器の発射方式の変更に進みます。Enforcer はヒット (命中) 時に武器をスキャンして設定され、そのため武器がトレースを完了すると ProcessInstantHit() を呼び出します。ここではこれをオーバーライドして、ヒットの処理が必要なときの武器の振る舞い方を新規ロジックとして追加 します。

  simulated function ProcessInstantHit(byte FiringMode, ImpactInfo Impact)
  {
  }
  

この特殊なケースでは親側のロジックを発生させる必要がないので、スーパークラスの呼び出しは追加していません。

9. 次にデリゲートを宣言します。このデリゲートは、武器がヒットを処理する必要があるときに呼び出されます。

  delegate OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact);
  
  simulated function ProcessInstantHit(byte FiringMode, ImpactInfo Impact)
  {
     OnProcessInstantHit(self, FiringMode, Impact);
  }
  

10. この武器が発射されたときに何か起きるかを処理したので、次のタスクとしてプレーヤーが発射タイプを変更する方法を処理するコードを追加します。デフォルト プロパティで、2 番目の発射ステートを WeaponSwitching に 変更する行を追加したのを覚えていますか?このデフォルト プロパティは、この二次的な発射モードがアクティブになると武器が入るステートの名前です。通常これは右マウスボタンにバインドされているので、プレーヤーによる発射タイプの切り替えは、これを使って処理します。

  simulated state WeaponSwitching
  {
  }
  

11. このステート内で多少の管理作業を行う必要があるので、UTWeap_Enforcer 内の WeaponBursting の内部に同じ関数のサブセットを追加します。

  simulated state WeaponSwitching
  {
     simulated function TrackShotCount();
  
     simulated function RefireCheckTimer();
  
     simulated function bool TryPutDown()
     {
        bWeaponPutDown = true;
        return true;
     }
  }
  

12. 武器はタイマーを用いてアニメーションを処理します。タイマーを使えば、例えば Tick() 内で実施されるコスト高の追跡に頼ることなく、UnrealScript の内部にイベントベースのアーキテクチャを作成できます。プレーヤーが発射タイプを変更したときに、プレーヤーがエンフォーサーを下に置き、弾倉を交換して再充填する動作を発生させたいのですが、丁度よいことに、Unreal Tournament 3 にはエンフォーサーによるこのようなアニメーションがあります。

  simulated state WeaponSwitching
  {
     simulated function TrackShotCount();
  
     simulated function RefireCheckTimer();
  
     simulated function bool TryPutDown()
     {
        bWeaponPutDown = true;
        return true;
     }
  
     simulated function BeginState(name PrevStateName)
     {
        TimeWeaponPutDown();
     }
  }
  

TimeWeaponPutDown() は親クラス内にある関数で、タイマーのトリガを設定するだけでなく、武器を置くアニメーションを再生します。この関数は UTWeapon および Weapon に実装されていて、簡単に説明すると、武器を置くアニメーションを処理し、アニメーションが終了すると WeaponIsDown() を呼び出します。

13. 武器を下ろすと WeaponIsDown() がトリガされるようになったので、次は再充填のアニメーションと同時に武器が元の位置に戻るようにします。この段階で、発射タイプ変更ステートも処理する必要があります。

  simulated state WeaponSwitching
  {
     simulated function TrackShotCount();
     simulated function RefireCheckTimer();
  
     simulated function bool TryPutDown()
     {
        bWeaponPutDown = true;
        return true;
     }
  
     simulated function WeaponIsDown()
     {
        ClearTimer('WeaponIsDown');
        bLoaded = false;
        TimeWeaponEquipping();
        CurrentIndex++;
  
        if (CurrentIndex >= FireTypes.length)
           CurrentIndex = 0;
  
        AssignFireType();
     }
  
     simulated function BeginState(name PrevStateName)
     {
        TimeWeaponPutDown();
     }
  }
  

ここでは、このステートに固有の WeaponIsDown() 関数を追加しました。この関数が呼び出されると同時に、関数に関連付けられたタイマーをクリアします。bLoaded は False に設定されていますが、それは UTWeap_Enforcer でこれを False に設定すると武器の再充填アニメーションが再生されるためです。次に、TimeWeaponPutDown() と同じである TimeWeaponEquipping() を呼び出し、アニメーションを再生し、タイマーに基づいて関数をトリガします。この関数 WeaponEquipped() と名付けています。さらに、CurrentIndex 変数をインクリメントして使用中の発射タイプのインデックスをずらし、CurrentIndex が FireTypes 配列の長さと同じかそれ以上になるとリセットしています。これにより、プレーヤーが使用するループ選択システムを作成します。次は AssignFireType() という未定義の関数を呼び出しますが、他の場所で CurrentIndex に基づく発射タイプの割り当てを処理しなければならない可能性があるので、この箇所は別の関数内で行いました。

14. 最後に、武器の再充填アニメーションが終了すると、アクティブステートに戻る必要があります。これを行わなければ武器はこのステートから永久に出られなくなり、再び発射できなくなります。

  simulated state WeaponSwitching
  {
     simulated function TrackShotCount();
     simulated function RefireCheckTimer();
  
     simulated function bool TryPutDown()
     {
        bWeaponPutDown = true;
        return true;
     }
  
     simulated function WeaponIsDown()
     {
        ClearTimer('WeaponIsDown');
        bLoaded = false;
        TimeWeaponEquipping();
        CurrentIndex++;
  
        if (CurrentIndex >= FireTypes.length)
           CurrentIndex = 0;
  
        AssignFireType();
     }
  
     simulated function WeaponEquipped()
     {
        ClearTimer('WeaponEquipped');
        bLoaded = true;
        GotoState('Active');
     }
  
     simulated function BeginState(name PrevStateName)
     {
        TimeWeaponPutDown();
     }
  }
  

関連タイマーをクリアするだけの WeaponEquipped() という新しい関数を追加し、bLoaded を True に設定してから (つまり、プレーヤーが別の武器に替えてからこれに戻ったときに、再充填アニメーションを再生しないようにする)、アクティブステートに進みます。

15. 次は AssignFireType() 関数の定義です。この関数が行うのは、CurrentIndex が FireTypes 内のインデックスとして有効に使用できるかどうか、およびその CurrentIndex が参照している FireType が有効かどうかのチェックです。

  function AssignFireType()
  {
     if (CurrentIndex >= 0 && CurrentIndex < FireTypes.length && FireTypes[CurrentIndex] != none && FireTypes[CurrentIndex].WeaponClass != none)
     {
     }
  }
  

16. このチェックには申し分ないのですが、これが実際に適用されるためにここでデリゲートに代入する必要があります。

  function AssignFireType()
  {
     if (CurrentIndex >= 0 && CurrentIndex < FireTypes.length && FireTypes[CurrentIndex] != none && FireTypes[CurrentIndex].WeaponClass != none)
        OnProcessInstantHit = FireTypes[CurrentIndex].OnProcessInstantHit;
  }
  

武器を最初に作成したときは、CurrentIndex が 0 でも発射タイプに割り当てられていないことを思い出してください。そのため、PostBeginPlay() 関数でも AssignFireType() を呼び出してこれを行います。

  simulated function PostBeginPlay()
  {
     local int i;
  
     super.PostBeginPlay();
  
     if (FireTypeClasses.length > 0)
     {
        for (i = 0; i < FireTypeClasses.length; ++i)
           if (FireTypeClasses[i] != none)
              FireTypes.AddItem(new FireTypeClasses[i]);
     }
  
     AssignFireType();
  }
  

17. 以上で必要なロジックがすべて完成しましたが、この武器がどの発射タイプに設定されたかをプレーヤーに伝える術がありません。幸いにも、HUD に情報を表示するために利用できるフックが、Weapon 内で作成されています。

  simulated function ActiveRenderOverlays(HUD H)
  {
     super.ActiveRenderOverlays(H);
  }
  

18. 選択されている発射タイプをプレーヤーに知らせるために、発射タイプが再現する武器のアイコンを描画します。このアイコンが描かれるのは、弾倉位置の HUD の上です。そのため、弾倉ボーンの位置を取得し、HUD 上に投射して有効な HUD 座標を獲得してから、レンダリングする必要があります。最初に、最初の武器メッシュの骨格メッシュ コンポーネントにアクセスします。

  simulated function ActiveRenderOverlays(HUD H)
  {
     local SkeletalMeshComponent SkelMesh;
  
     super.ActiveRenderOverlays(H);
  
     if (H != none && H.Canvas != none)
     {
        SkelMesh = SkeletalMeshComponent(Mesh);
  
        if (SkelMesh != none)
        {
        }
     }
  }
  

19. 骨格メッシュにアクセスしたので、次は Bullet5 というボーンの位置を取得し (Unreal エディタを起動し、Animation エディタ内で骨格メッシュを参照すると、ボーンの名前が分かります)、HUD に投射します。アイコンを描くときに解像度に基づいてパーセント値を割り出し、最適な幅と高さを計算すると、最高の結果が得られます。先に計算しておいた位置に基づいてアイコンの中心を決めてから、画面にアイコンを描きます。Weapon クラスにはアイコンの座標が格納され、アイコン自体は合成アイコンテクスチャの内部に格納されます。

  simulated function ActiveRenderOverlays(HUD H)
  {
     local SkeletalMeshComponent SkelMesh;
     local vector BoneLocation;
     local float i_w;
     local float i_h;
     local color CanvasColor;
  
     super.ActiveRenderOverlays(H);
  
     if (H != none && H.Canvas != none)
     {
        SkelMesh = SkeletalMeshComponent(Mesh);
  
        if (SkelMesh != none)
        {
           BoneLocation = H.Canvas.Project(SkelMesh.GetBoneLocation('Bullet5', 0));
           i_w = H.Canvas.ClipX * 0.05f;
           i_h = H.Canvas.ClipY * 0.05f;
  
           CanvasColor = H.Canvas.DrawColor;
           H.Canvas.DrawColor =  class'UTHUD'.default.WhiteColor;
           H.Canvas.SetPos(BoneLocation.x - (i_w * 0.5f), BoneLocation.y - (i_h * 0.5f));
           H.Canvas.DrawTile(class'UTHUD'.default.IconHudTexture, i_w, i_h, FireTypes[CurrentIndex].WeaponClass.default.IconCoordinates.U, FireTypes[CurrentIndex].WeaponClass.default.IconCoordinates.V, FireTypes[CurrentIndex].WeaponClass.default.IconCoordinates.UL, FireTypes[CurrentIndex].WeaponClass.default.IconCoordinates.VL);
           H.Canvas.DrawColor = CanvasColor;
        }
     }
  }
  

チュートリアル 12.8 - 武器のミューテーター、パート III: MULTIENFORCER_BASE

基本クラスそのものは複雑ではなく、あくまでも他のクラスがサブクラス化するための基本クラスに過ぎません。

1. UTWeap_MultiEnforcer が画面にアイコンをレンダリングするときに、Weapon クラスの発射タイプを要求しますが、これをグローバル定数変数として定義し、サブクラスのデフォルト プロパティで定義できるようにします。

  var const class<UTWeapon> WeaponClass;
  

2. UTWeap_MultiEnforcer がそのデリゲートへの割り当てに使う関数の定義が必要です。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact);
  

チュートリアル 12.8 - 武器のミューテーター、パート IV: MULTIENFORCER_BIO

これはバイオ発射タイプです。弾が何かに命中すると、bio goo (粘性のバイオ薬剤) が発生し、bio goo を落とします。

1. 最初に、デフォルト プロパティに WeaponClass を設定します。

  defaultproperties
  {
     WeaponClass=class'UTWeap_BioRifle_Content'
     Name="Default__MultiEnforcer_Bio"
  }
  

2. 次に OnProcessInstantHit() 関数をオーバーライドします。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
  }
  

3. インパクトから有効な HitActor を得られたかどうかのチェックが必要です。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     if (Impact.HitActor != none)
     {
     }
  }
  

4. 爆発のパーティクルエフェクトの作成から始めましょう。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local EmitterSpawnable es;
  
     if (Impact.HitActor != none)
     {
        es = Weapon.Spawn(class'EmitterSpawnable',,, Impact.HitLocation);
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_BioRifle.Particles.P_WP_Bio_Alt_Blob_POP');
     }
  }
  

Weapon を使って EmitterSpawnable をスポーンしますが、これは Spawn() 関数が Objectではなく Actor にあるからです。

5. 次に、爆発音を追加します。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local EmitterSpawnable es;
  
     if (Impact.HitActor != none)
     {
        Weapon.PlaySound(SoundCue'A_Weapon_BioRifle.Weapon.A_BioRifle_FireAltImpactExplode_Cue',,,, Impact.HitLocation);
        es = Weapon.Spawn(class'EmitterSpawnable',,, Impact.HitLocation);
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_BioRifle.Particles.P_WP_Bio_Alt_Blob_POP');
     }
  }
  

6. 次に、放射状の (radial) ダメージを追加します。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local EmitterSpawnable es;
  
     if (Impact.HitActor != none)
     {
        Weapon.PlaySound(SoundCue'A_Weapon_BioRifle.Weapon.A_BioRifle_FireAltImpactExplode_Cue',,,, Impact.HitLocation);
        es = Weapon.Spawn(class'EmitterSpawnable',,, Impact.HitLocation);
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_BioRifle.Particles.P_WP_Bio_Alt_Blob_POP');
  
        Weapon.HurtRadius(class'UTProj_BioGlob'.default.Damage, class'UTProj_BioGlob'.default.DamageRadius, class'UTProj_BioGlob'.default.MyDamageType, class'UTProj_BioGlob'.default.MomentumTransfer, Impact.HitLocation);
     }
  }
  

7. 最後に、bio goo をスポーンしてこの発射タイプを完成させます。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local int g;
     local UTProj_BioShot NewGlob;
     local EmitterSpawnable es;
  
     if (Impact.HitActor != none)
     {
        Weapon.PlaySound(SoundCue'A_Weapon_BioRifle.Weapon.A_BioRifle_FireAltImpactExplode_Cue',,,, Impact.HitLocation);
        es = Weapon.Spawn(class'EmitterSpawnable',,, Impact.HitLocation);
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_BioRifle.Particles.P_WP_Bio_Alt_Blob_POP');
  
        for (g = 0; g < 6; ++g)
        {
           NewGlob = Weapon.Spawn(class'UTProj_BioGlobling',,, Impact.HitLocation);
  
           if (NewGlob != None)
              NewGlob.Velocity = (FRand() * 150.f) * (VRand() * 0.8f);
        }
  
        Weapon.HurtRadius(class'UTProj_BioGlob'.default.Damage, class'UTProj_BioGlob'.default.DamageRadius, class'UTProj_BioGlob'.default.MyDamageType, class'UTProj_BioGlob'.default.MomentumTransfer, Impact.HitLocation);
     }
  }
  

チュートリアル 12.8 - 武器のミューテーター、パート V: MULTIENFORCER_FLAK

これはフラック発射タイプです。弾が何かに命中すると、高射砲弾 (flak shell) を爆発させて破片を一面に放ちます。

1. 最初に、デフォルト プロパティに WeaponClass 変数を設定します。

  defaultproperties
  {
     WeaponClass=class'UTWeap_FlakCannon'
     Name="Default__MultiEnforcer_Flak"
  }
  

2. 次に OnProcessInstantHit() 関数をオーバーライドします。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
  }
  

3. インパクトから有効な HitActor を得られたかどうかのチェックが必要です。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     if (Impact.HitActor != none)
     {
     }
  }
  

4. 爆発のパーティクル エフェクトを追加します。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local EmitterSpawnable es;
  
     if (Impact.HitActor != none)
     {
        es = Weapon.Spawn(class'EmitterSpawnable',,, Impact.HitLocation);
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_FlakCannon.Effects.P_WP_Flak_Alt_Explosion');
     }
  }
  

Weapon を使って EmitterSpawnable をスポーンしますが、これは Spawn() 関数が Objectではなく Actor にあるからです。

5. 次に、再生するサウンドを組み込みます。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local EmitterSpawnable es;
  
     Weapon.PlaySound(SoundCue'A_Weapon_FlakCannon.Weapons.A_FlakCannon_FireAltImpactExplodeCue',,,, Impact.HitLocation);
  
     if (Impact.HitActor != none)
     {
        es = Weapon.Spawn(class'EmitterSpawnable',,, Impact.HitLocation);
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_FlakCannon.Effects.P_WP_Flak_Alt_Explosion');
     }
  }
  

6. 次に、放射状の (radial) ダメージを追加します。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local EmitterSpawnable es;
  
     Weapon.PlaySound(SoundCue'A_Weapon_FlakCannon.Weapons.A_FlakCannon_FireAltImpactExplodeCue',,,, Impact.HitLocation);
  
     if (Impact.HitActor != none)
     {
        es = Weapon.Spawn(class'EmitterSpawnable',,, Impact.HitLocation);
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_FlakCannon.Effects.P_WP_Flak_Alt_Explosion');
     }
  
     Weapon.HurtRadius(class'UTProj_FlakShell'.default.Damage, class'UTProj_FlakShell'.default.DamageRadius, class'UTProj_FlakShell'.default.MyDamageType, class'UTProj_FlakShell'.default.MomentumTransfer, Impact.HitLocation);
  }
  

7. 最後に、至る場所に飛び出すランダムな高射砲弾を生成します。これには、反復子を設定して発射物が 5 つ生成されるようにします。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local int i;
     local UTProj_FlakShard NewChunk;
     local EmitterSpawnable es;
  
     if (Impact.HitActor != none)
     {
        Weapon.PlaySound(SoundCue'A_Weapon_FlakCannon.Weapons.A_FlakCannon_FireAltImpactExplodeCue',,,, Impact.HitLocation);
  
        es = Weapon.Spawn(class'EmitterSpawnable',,, Impact.HitLocation);
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_FlakCannon.Effects.P_WP_Flak_Alt_Explosion');
  
        for (i = 0; i < 5; ++i)
        {
           NewChunk = Weapon.Spawn(class'UTProj_FlakShard',,, Impact.HitLocation);
  
           if (NewChunk != None)
           {
              NewChunk.bCheckShortRangeKill = false;
              NewChunk.Init((FRand() * 150.f) * (VRand() * 0.8f));
           }
        }
  
        Weapon.HurtRadius(class'UTProj_FlakShell'.default.Damage, class'UTProj_FlakShell'.default.DamageRadius, class'UTProj_FlakShell'.default.MyDamageType, class'UTProj_FlakShell'.default.MomentumTransfer, Impact.HitLocation);
     }
  }
  

チュートリアル 12.8 - 武器のミューテーター、パート VI: MULTIENFORCER_ROCKET

これはロケット発射タイプです。弾が何かに命中すると、強烈な爆発を生成します。

1. 最初に、デフォルト プロパティに WeaponClass を設定します。

  defaultproperties
  {
     WeaponClass=class'UTWeap_RocketLauncher'
     Name="Default__MultiEnforcer_Rocket"
  }
  

2. 次に OnProcessInstantHit() 関数をオーバーライドします。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
  }
  

3. インパクトから有効な HitActor を得られたかどうかのチェックが必要です。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     if (Impact.HitActor != none)
     {
     }
  }
  

4. 爆発のパーティクル エフェクトをスポーンします。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local EmitterSpawnable es;
  
     if (Impact.HitActor != none)
     {
        es = Weapon.Spawn(class'Engine.EmitterSpawnable',,, Impact.HitLocation, Rotator(Impact.HitNormal));
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_RocketLauncher.Effects.P_WP_RocketLauncher_RocketExplosion');
     }
  }
  

Weapon を使って EmitterSpawnable をスポーンしますが、これは Spawn() 関数が Objectではなく Actor にあるからです。

5. 次に、大きな爆発音を再生します。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local EmitterSpawnable es;
  
     if (Impact.HitActor != none)
     {
        es = Weapon.Spawn(class'Engine.EmitterSpawnable',,, Impact.HitLocation, Rotator(Impact.HitNormal));
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_RocketLauncher.Effects.P_WP_RocketLauncher_RocketExplosion');
  
        Weapon.PlaySound(SoundCue'A_Weapon_RocketLauncher.Cue.A_Weapon_RL_Impact_Cue',,,, Impact.HitLocation);
     }
  }
  

6. この発射タイプを完成させるために、放射状にダメージを与ええます。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     local EmitterSpawnable es;
  
     if (Impact.HitActor != none)
     {
        es = Weapon.Spawn(class'Engine.EmitterSpawnable',,, Impact.HitLocation, Rotator(Impact.HitNormal));
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_RocketLauncher.Effects.P_WP_RocketLauncher_RocketExplosion');
  
        Weapon.PlaySound(SoundCue'A_Weapon_RocketLauncher.Cue.A_Weapon_RL_Impact_Cue',,,, Impact.HitLocation);
        Weapon.HurtRadius(class'UTProj_Rocket'.default.Damage, class'UTProj_Rocket'.default.DamageRadius, class'UTProj_Rocket'.default.MyDamageType, class'UTProj_Rocket'.default.MomentumTransfer, Impact.HitLocation);
     }
  }
  

チュートリアル 12.8 - 武器のミューテーター、パート VII: MULTIENFORCER_SHOCK

これはショック発射タイプです。弾が何かに命中すると、強烈なショックコンボ爆発を生成します。

1. 最初に、デフォルト プロパティに WeaponClass を設定します。

  defaultproperties
  {
     WeaponClass=class'UTWeap_ShockRifle'
     Name="Default__MultiEnforcer_Shock"
  }
  

2. 次に OnProcessInstantHit() 関数をオーバーライドします。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
  }
  

3. インパクトから有効な HitActor を得られたかどうかのチェックが必要です。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     if (Impact.HitActor != none)
     {
     }
  }
  

4. コンボ爆発のパーティクル エフェクトをスポーンします。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     if (Impact.HitActor != none)
     {
        Weapon.Spawn(Class'UTGame.UTEmit_ShockCombo',,, Impact.HitLocation);
     }
  }
  

パーティクル エフェクトの生成には Weapon を使用する必要がありますが、これは Spawn() が Objectではなく Actor のネイティブ関数だからです。Impact は、多数の有益なデータを格納する struct で、ここでは HitLocation を使用しています。

5. 爆弾音がなければつまらないので、ここでサウンドを再生します。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     if (Impact.HitActor != none)
     {
        Weapon.Spawn(Class'UTGame.UTEmit_ShockCombo',,, Impact.HitLocation);
        Weapon.PlaySound(SoundCue'A_Weapon_ShockRifle.Cue.A_Weapon_SR_ComboExplosionCue',,,, Impact.HitLocation);
     }
  }
  

6. この発射タイプを完成させるために、放射状のダメージも与ええます。

  function OnProcessInstantHit(UTWeapon Weapon, byte FiringMode, ImpactInfo Impact)
  {
     if (Impact.HitActor != none)
     {
        Weapon.Spawn(Class'UTGame.UTEmit_ShockCombo',,, Impact.HitLocation);
        Weapon.PlaySound(SoundCue'A_Weapon_ShockRifle.Cue.A_Weapon_SR_ComboExplosionCue',,,, Impact.HitLocation);
        Weapon.HurtRadius(class'UTProj_ShockBall'.default.ComboDamage, class'UTProj_ShockBall'.default.ComboRadius, class'UTProj_ShockBall'.default.ComboDamageType, class'UTProj_ShockBall'.default.ComboMomentumTransfer, Impact.HitLocation);
     }
  }
  

クラスのデフォルト値を使用して、値を上書きコピーする手間を省いています。

チュートリアル 12.8 - 武器のミューテーター、テスト実施

1. インスタンスアクションのデスマッチゲームを開始します。

2. Tab を押してコンソールを開きます。コンソールで、「giveweapon MasteringUnrealScript.UTWeap_Multienforcer」と入力します。このコマンドで武器が与えられます。


図 12.4 - このコンソールコマンドは、プレーヤーに目的の武器を即座に与えます。

3. 武器が与えられたので、[3] キーを押してそれに切り替えます。


図 12.5 - MultiEnforcer 武器がアクティブになりました。

4. 武器の選択を済ませたら、次は周囲を点検して既に機能しているかどうかを確認します。最初に HUD アイコンを見ると、ロケット発射タイプが選択されていることを示しています。確認のためにこの武器を発射してみます。


図 12.6 - 武器はロケットタイプを発射します。

5. 次に、発射タイプの切り替え機能をテストします。右クリックして別のタイプに切り替えます。


図 12.7 - 武器の発射タイプが切り替わります。

6. 上々の出来です。ショック発射タイプに切り替えられたようです。発射してみて、機能するかどうかを確認します。


図 12.8 - 今度はショックタイプを発射します。

このチュートリアルでは、プレーヤーが二次発射ボタンを使って発射タイプを切り替えできる武器を作成しました。その方法としてデリゲートを利用しましたが、もちろん他の方法も多数あります。この例から学んだ重要なレッスンは、デリゲートをオブジェクトと使用したときに、必ず参照をクリーンアップすることの大切さです。

チュートリアル 12.16 - デリゲート & KISMET、パート I: 入門 & クラスの初期設定

このチュートリアルでは、デリゲートを Kismet 内で使用できるようにする方法を説明します。具体的な作業として、レベルデザイナー向けにエフェクト ジェネレータを作成することにします。デザイナーは、Kismet ロジックによって生成するエフェクトをオンザフライで変更できます。ここで最初に考えなければならない点は、もろもろの壮大な仕掛けの中で、これをいかに機能させるかということです。Kismet やレベルのデザイナーは配置可能アクタやブラシを扱う作業によく携わるので、ベルデザイナーがレベル内に配置調整できないものを使用することはおそらく避けるべきです。これに対処するには、配置可能なエフェクト ジェネレータであること、そのエフェクトも配置可能であることが最もよく、それによっていくつかの問題を解決できます。レベルデザイナーは任意の位置にシングルエフェクト ジェネレータを配置してエフェクトのスポーン位置と回転を定義し、マップ内部にエフェクトを配置して、目的の結果が得られるようにエフェクトを調整します。レベルデザイナーはこの方法論に十分精通しています。さらに、Kismet 対応ノードを記述して、レベルデザイナーがエフェクト ジェネレータ内に出でデリゲートを設定できるようにします。Kismet ノードのリストを見たところ、アクタを「使用」するノードが存在しないので、それも作成する必要があります。つまりまとめると、effects generator クラス、effect base クラス、set effect Kismet クラス、use actor Kismet クラスおよび作成したいエフェクトのクラスを作成する必要があります。このチュートリアルでは、爆発、環状構造でスポーンする手榴弾、ジブ (gib、内臓) 生成の 3 つエフェクトを作成します。

1. 任意のテキストエディタを開き、..\MasteringUnrealScript\Classes ディレクトリに UTEffectsGenerator.uc、UTEffect.uc, SeqAct_SetEffect.uc、SeqAct_Use.uc、UTEffect_Explosion.uc、UTEffect_GrenadeRing.uc および UTEffect_Gibbage.uc という名前の新規ファイルを作成します。

2. UTEffectsGenerator のクラスおよびデフォルトプロパティを宣言します。

  class UTEffectsGenerator extends Actor
     placeable;
  
  defaultproperties
  {
     Name=”Default__UTEffectsGenerator”
  }
  

UTEffectsGenerator は、レベルデザイナーがレベル内に配置できるように配置可能 (placeable) にしています。

3. UTEffect のクラスおよびデフォルトプロパティを宣言します。

  class UTEffect extends Actor
     abstract;
  
  defaultproperties
  {
     Name=”Default__UTEffect”
  }
  

UTEffect は abstract ですが、その理由は子クラス (UTEffect_Explosion、UTEffect_GrenadeRing および UTEffect_Gibbage) を作成するためです。レベルデザイナーまたはプログラマーが UTEffect をスポーンまたは配置することはありません。

4. SeqAct_SetEffect のクラスおよびデフォルトプロパティを宣言します。

  class SeqAct_SetEffect extends SeqAct_SetSequenceVariable;
  
  defaultproperties
  {
     VariableLinks(1)=(ExpectedType=Class'Engine.SeqVar_Object',LinkDesc="Effect",PropertyName="Value",MinVars=1,MaxVars=255)
     ObjClassVersion=2
     ObjName="Set Effect"
     ObjCategory=”Effect Generator”
     Name=”Default__ SeqAct_SetEffect”
     ObjectArchetype=SeqAct_SetSequenceVariable'Engine.Default__SeqAct_SetSequenceVariable'
  }
  

同様に、SeqAct_SetEffect は Kismet で使うことになるので、SeqAct_SetSequenceVariable の子クラスになっています。VariableLinks(1) は、Unreal エディタの Kismet エディタで使用できるリンクスロットを指定し、ObjName はKismet エディタ内に現われる Kismet ノードの名前です。

5. SeqAct_Use のクラスおよびデフォルトプロパティを宣言します。

  class SeqAct_Use extends SequenceAction;
  
  defaultproperties
  {
     ObjName=”Use”
     ObjCategory=”Effect Generator”
     Name=”Default__SeqAct_Use”
     ObjectArchetype=SequenceAction'Engine.Default__SequenceAction'
  }
  

同様に、SeqAct_Use は Kismet で使うことになるので、SequenceAction の子クラスになっています。ObjName は Kismet エディタ内に現われる Kismet ノードの名前、ObjCategory は Kismet エディタ内で Kismet ノードのソートに使用されます。

6. UTEffect_Explosion のクラスおよびデフォルトプロパティを宣言します。

  class UTEffect_Explosion extends UTEffect
     placeable;
  
  defaultproperties
  {
     Name=”Default_UTEffect_Explosion”
  }
  

UTEffect_Explosion は、レベルデザイナーが配置できるように配置可能になっています。

7. UTEffect_GrenadeRing のクラスおよびデフォルトプロパティを宣言します。

  class UTEffect_GrenadeRing extends UTEffect
     placeable;
  
  defaultproperties
  {
     Name=”Default__UTEffect_GrenadeRing”
  }
  

UTEffect_GrenadeRing は、レベルデザイナーが配置できるように配置可能になっています。

8. UTEffect_Gibbage のクラスおよびデフォルトプロパティを宣言します。

  class UTEffect_Gibbage extends UTEffect
     placeable;
  
  defaultproperties
  {
     Name=”Default__UTEffect_Gibbage”
  }
  

UTEffect_Gibbage は、レベルデザイナーが配置できるように配置可能になっています。

チュートリアル 12.16 - デリゲート & KISMET、パート II: UTEFFECTSGENERATOR

1. まず、エフェクト ジェネレータが使用するデリゲート関数の宣言から始めましょう。

  delegate OnGenerateEffects(UTEffectsGenerator InGenerator);
  

このデリゲートには UTEffectsGenerator への参照を保持するパラメータが含まれていて、この場合は常にデリゲートを呼び出した UTEffectsGenerator の参照を保持します。この宣言により、他のクラスインスタンスはデリゲートの最初の呼び出し元にアクセスできます。

2. デリゲート関数を作成したので、次に実際にそれを呼び出すためのロジックを書く必要があります。

  function GenerateEffects()
  {
     OnGenerateEffects(self);
  }
  

GenerateEffects() 内に OnGenerateEffects() をカプセル化する理由は、将来への備えです。今は無意味なのですが、エフェクト生成を無効化するメソッドや他の何かなどがおそらく必要になったときに、他の子クラスを変更する手間をかけず、GenerateEffects() だけを調整するだけで済ませるようにする点を考慮しています。

3. 次は、GenerateEffects() 関数を呼び出すメソッドが必要です。この特殊なケースでは、Actor 内の UsedBy() フックを使用します。UsedBy() は、プレーヤーが [Use] ボタンを押してアクタを使用するときに呼び出されますが、ここでは Kismet と対話するための共通フックの目的のみを果たしています。

  function bool UsedBy(Pawn User)
  {
     GenerateEffects();
     return super.UsedBy(User);
  }
  

これにより、プレーヤーがエフェクト ジェネレータを使用すると、ジェネレータは GenerateEffects() を呼び出し、続いてこの関数が OnGenerateEffects() デリゲートを呼び出します。

4. 次に、デリゲートを任意に設定できるようにする手段が必要です。

  function SetEffect(delegate<OnGenerateEffects> NewEffect)
  {
     OnGenerateEffects = NewEffect;
  }
  

厳密には、このデリゲートは変数のように他のクラスからもアクセスできるので、関数内部にこれをカプセル化する理由はないのですが、このようにしておけば、万一このデリゲートを private または protected デリゲートに変更したくなった場合でも、それが使用されているクラスを変更しなくてもよい、というだけのことです。

5. レベルデザイナーはこのアクタをワールドに配置することになるので、エフェクト ジェネレータがデザイナーから見えるようにする必要があります。そこで、エディタ内部だけで見えるレンダリングコンポーネントをデフォルト プロパティに追加します。

  defaultproperties
  {
     Begin Object Class=SpriteComponent Name=Sprite ObjName=Sprite Archetype=SpriteComponent'Engine.Default__SpriteComponent'
        HiddenGame=True
            AlwaysLoadOnClient=False
            AlwaysLoadOnServer=False
            Name="Sprite"
            ObjectArchetype=SpriteComponent'Engine.Default__SpriteComponent'
       End Object
       Components(0)=Sprite
  
     Begin Object Class=ArrowComponent Name=Arrow ObjName=Arrow Archetype=ArrowComponent'Engine.Default__ArrowComponent'
        ArrowColor=(B=255,G=200,R=150,A=255)
        Name="Arrow"
        ObjectArchetype=ArrowComponent'Engine.Default__ArrowComponent'
     End Object
     Components(1)=Arrow
  
     Name=”Default__UTEffectsGenerator”
  }
  

チュートリアル 12.8 - デリゲート & Kismet、パート III: UTEFFECT

1. 最初に行わければならないのは、このクラスの前に UTEffectGenerator が常にコンパイルされるようにすることです。そこで、dependson(UTEffectsGenerator) を子の定義に追加します。

  class UTEffect extends Actor
     dependson(UTEffectsGenerator)
     abstract;
  

2. 次に、すべての子クラスのスタブ関数として機能する関数を作成する必要があります。このスタブ関数は、UTEffectsGenerator 内で定義されるデリゲートのオーバーライドにも使用されます。

  function Effect(UTEffectsGenerator InGenerator);
  

3. 次に Kismet でこのエフェクトをエフェクト ジェネレータに設定するために呼び出す関数を作成します。

  function SetEffect(UTEffectsGenerator InGenerator)
  {
     if (InGenerator != none)
        InGenerator.SetEffect(Effect);
  }
  

先にも述べたとおり、InGenerator.OnGenerateEffects = Effect としてデリゲートの代入を処理することも可能ですが、その方法ではデリゲートが private または protected に変更された場合にこのクラスからアクセスできなくなるので、あまり柔軟性があるとは言えません。そのため、念のために代入関数が使われています。

4. UTEffectsGenerator と同様、レベルデザイナーがレベル内でレンダリング コンポーネントを配置できるように、デフォルトプロパティにこれらを追加する必要があります。

  defaultproperties
  {
       Begin Object Class=SpriteComponent Name=Sprite ObjName=Sprite Archetype=SpriteComponent'Engine.Default__SpriteComponent'
        HiddenGame=True
        AlwaysLoadOnClient=False
        AlwaysLoadOnServer=False
        Name="Sprite"
        ObjectArchetype=SpriteComponent'Engine.Default__SpriteComponent'
     End Object
     Components(0)=Sprite
  
     Name="Default_UTEffect"
  }
  

チュートリアル 12.16 - デリゲート & KISMET、パート III: SEQACT_SETEFFECT

1. Kismet ノードは主に Native (ネイティブ) コードにより制御されますが、Native コードが呼び出すイベントの中には、UnrealScript で使用できるものがあります。この特別なケースでは、ノードがアクティブになると常に Native コードが Activated() イベントを呼び出すので、それをオーバーライドしてアクセスできるようにします。

  event Activated()
  {
  }
  

2. エディタ内で Kismet ノードをリンクすると、LinkedVariables (各 VariableLinks 内の) ではそれらのオブジェクト変数への参照を保持します。これらのオブジェクト変数には、UTEffectsGenerator および必要な UTEffect オブジェクトの参照を格納する必要があります。まず、これらの前提条件が正しいかどうかの確認だけを行います。

  event Activated()
  {
     if (VariableLinks.length >= 2 && VariableLinks[0].LinkedVariables.length > 0 && VariableLinks[0].LinkedVariables[0] != none && VariableLinks[1].LinkedVariables.length > 0 && VariableLinks[1].LinkedVariables[0] != none)
     {
     }
  }
  

このステートメントでは、最初に VariableLinks 配列が 2 以上かどうかをチェックします。2 つのオブジェクト (UTEffectsGenerator および UTEffect) が必要なので、2 つの VariableLinks の存在が必要です。各 VariableLinks の LinkedVariables 配列も 2 以上であることが必要です。最後に、LinkedVariables 内の最初のインデックスにオブジェクト参照が含まれていなければなりません。

3. LinkedVariables は SequenceVariables として格納されていますが、実際には SeqVar_Objects なので、まず見つかった LinkedVariables を SeqVar_Objects にタイプキャストする必要があります。これにより、SeqVar_Objects に UTEffectGenerators と UTEffect の参照が含まれることになります。

  event Activated()
  {
     local SeqVar_Object sv_obj;
  
     if (VariableLinks.length >= 2 && VariableLinks[0].LinkedVariables.length > 0 && VariableLinks[0].LinkedVariables[0] != none && VariableLinks[1].LinkedVariables.length > 0 && VariableLinks[1].LinkedVariables[0] != none)
     {
        sv_obj = SeqVar_Object(VariableLinks[0].LinkedVariables[0]);
  
        if (sv_obj != none)
        {
        }
  
        sv_obj = SeqVar_Object(VariableLinks[1].LinkedVariables[0]);
  
        if (sv_obj != none)
        {
        }
     }
  }
  

ここでは、タイプキャストの結果を一時的に格納するためのローカル変数を追加しました。これはタイプキャストが有効かどうかを実際に確認して、後でトラブルに巻き込まれないようにするためです (例えば、レベルデザイナーがリンクに何か予期できないものを割り当てるなど)。

4. SeqVar_Object へのタイプキャストに続いて、この Kismet ノードにリンクされている UTEffectsGenerator と UTEffect のレベルインスタンスの取得を試みます。UTEffectsGenerator および UTEffect の参照を保持する 2 つのローカル変数を作成し、GetObjectValue() 関数を用いて sv_obj からそれらを取得します。GetObjectValue() はオブジェクト参照を返すので、それらを適切なタイプにキャストする必要があります。

  event Activated()
  {
     local SeqVar_Object sv_obj;
     local UTEffectsGenerator ut_effects_generator;
     local UTEffect ut_effects;
  
     if (VariableLinks.length >= 2 && VariableLinks[0].LinkedVariables[0] != none && VariableLinks[1].LinkedVariables[0] != none)
     {
        sv_obj = SeqVar_Object(VariableLinks[0].LinkedVariables[0]);
  
        if (sv_obj != none)
           ut_effects_generator = UTEffectsGenerator(sv_obj.GetObjectValue());
  
        sv_obj = SeqVar_Object(VariableLinks[1].LinkedVariables[0]);
  
        if (sv_obj != none)
           ut_effects = UTEffect(sv_obj.GetObjectValue());
     }
  }
  

5. 最後に登場するのが、UTEffectsGenerator および UTEffect のレベル インスタンスです。しかし、それらの参照を使用する前に実際にまだ有効かどうかの確認を行うのが良い慣行です。そこで、参照が none かどうかのチェックを行い、none でなければ、UTEffect 参照への指図として、そのエフェクトを UTEffectsGenerator 参照上に設定します。UTEffect 内で SetEffect() 関数 を呼び出すと、この関数が UTEffectsGenerator 内で SetEffect() 関数を呼び出します。

  event Activated()
  {
     local SeqVar_Object sv_obj;
     local UTEffectsGenerator ut_effects_generator;
     local UTEffect ut_effects;
  
     if (VariableLinks.length >= 2 && VariableLinks[0].LinkedVariables[0] != none && VariableLinks[1].LinkedVariables[0] != none)
     {
        sv_obj = SeqVar_Object(VariableLinks[0].LinkedVariables[0]);
  
        if (sv_obj != none)
           ut_effects_generator = UTEffectsGenerator(sv_obj.GetObjectValue());
  
        sv_obj = SeqVar_Object(VariableLinks[1].LinkedVariables[0]);
  
        if (sv_obj != none)
           ut_effects = UTEffect(sv_obj.GetObjectValue());
  
        if (ut_effects_generator != none && ut_effects != none)
           ut_effects.SetEffect(ut_effects_generator);
     }
  }
  

以上でこの Kismet ノードの作業は終了です。

チュートリアル 12.16 - デリゲート & KISMET、パート V: SEQACT_USE

1. SeqAct_SetEffect の場合と同様に、Activated() イベントをオーバーライドします。

  event Activated()
  {
  }
  

2. SeqAct_SetEffect とほぼ同様に、VariableLinks とその LinkedVariables を調べて、この Kismet ノードにつながれている Actor インスタンスを見つけます。

  event Activated()
  {
     local SeqVar_Object sv_obj;
     local Actor a;
  
     if (VariableLinks.length >= 1 && VariableLinks[0].LinkedVariables.length > 0 && VariableLinks[0].LinkedVariables[0] != none)
     {
        sv_obj = SeqVar_Object(VariableLinks[0].LinkedVariables[0]);
  
        if (sv_obj != none)
        {
           a = Actor(sv_obj.GetObjectValue());
        }
     }
  }
  

3. アクタの参照が検出されたところで、none チェックを実行して UsedBy() 関数を呼び出します。これはユーティリティの Kismet ノードで、この関数を実装する任意の Actor に対して使用できます。

  event Activated()
  {
     local SeqVar_Object sv_obj;
     local Actor a;
  
     if (VariableLinks.length >= 1 && VariableLinks[0].LinkedVariables.length > 0 && VariableLinks[0].LinkedVariables[0] != none)
     {
        sv_obj = SeqVar_Object(VariableLinks[0].LinkedVariables[0]);
  
        if (sv_obj != none)
        {
           a = Actor(sv_obj.GetObjectValue());
  
           if (a != none)
              a.UsedBy(none);
        }
     }
  }
  

4. 必要な基本クラスと Kismet ノードが完成したので、次はエフェクトを作成して楽しみましょう。

チュートリアル 12.16 - デリゲート & KISMET、パート VI: UTEFFECT_EXPLOSION

1. 始めに、親クラス (UTEffect) にある Effect 関数をオーバーライドします。

  function Effect(UTEffectsGenerator InGenerator)
  {
  }
  

2. このエフェクトに関して実行したいのは、ロケット爆発に似た爆発のパーティクル エフェクトをスポーンし、加えて爆発音も再生することです。このエフェクトは、例えばビルの崩壊など特定の対象物の時限爆発などを行う場合に上手く機能します。そこで、パーティクルのスポーンを処理するエミッタの参照を保持するローカル変数を作成します。

  function Effect(UTEffectsGenerator InGenerator)
  {
     local EmitterSpawnable es;
  }
  

3. ここからエミッタをスポーンしてパーティクルシステムテンプレートを設定しましょう。UTEffectsGenerator の位置でエミッタをスポーンしたいので、UTEffectsGenerator の位置と回転を使用することにします。ここでも同様に、先に none チェックを行います。

  function Effect(UTEffectsGenerator InGenerator)
  {
     local EmitterSpawnable es;
  
     if (InGenerator != none)
     {
        es = Spawn(class'EmitterSpawnable',,, InGenerator.Location, InGenerator.Rotation);
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_RocketLauncher.Effects.P_WP_RocketLauncher_RocketExplosion');
     }
  }
  

4. 爆発音のエフェクトを追加すると、エフェクトが完成です。

  function Effect(UTEffectsGenerator InGenerator)
  {
     local EmitterSpawnable es;
  
     if (InGenerator != none)
     {
        InGenerator.PlaySound(SoundCue'A_Weapon_RocketLauncher.Cue.A_Weapon_RL_Impact_Cue');
        es = Spawn(class'EmitterSpawnable',,, InGenerator.Location, InGenerator.Rotation);
  
        if (es != none)
           es.SetTemplate(ParticleSystem'WP_RocketLauncher.Effects.P_WP_RocketLauncher_RocketExplosion');
     }
  }
  

5. 最初のエフェクトが完成しました。追加練習問題として、サウンドキューとパーティクル システムの両方を、エディタで構成可能にしてみることをお勧めします。

ヒント: これを行うには、サウンドキューとパーティクル システムへの参照を保持する、編集可能なグローバル変数を作成します。

チュートリアル 12.16 - デリゲート & KISMET、パート VII: UTEFFECT_GRENADERING

このエフェクトでは、UTEffectsGenerator から輪の形を描くように手榴弾を投げます。各手榴弾のステップ角度は、レベルデザイナーが設定できるようにします。

1. 始めに、エディタのグローバル変数を宣言します。

  var(GrenadeRing) int Angle;
  

これにより、レベルデザイナーがプロパティ ウィンドウを開くと [GrenadeRing] カテゴリーの内部に [Angle] が表示されます。

2. 前の手順と同様に、親クラスの Effect 関数をオーバーライドします。

  function Effect(UTEffectsGenerator InGenerator)
  {
  }
  

3. UTEffectsGenerator にスポーンリングの中央を合わせたいので、none チェックの後で Angle 値を調べて、使用できない値をレベルデザイナーが入力していないかどうか確認します。

  function Effect(UTEffectsGenerator InGenerator)
  {
     if (InGenerator != none && Angle > 0 && Angle < 65535)
     {
     }
  }
  

4. サウンドエフェクトを先に片付けましょう。

  function Effect(UTEffectsGenerator InGenerator)
  {
     if (InGenerator != none && Angle > 0 && Angle < 65535)
     {
        InGenerator.PlaySound(SoundCue'A_Weapon_RocketLauncher.Cue.A_Weapon_RL_GrenadeFire_Cue');
     }
  }
  

5. 最初にやるべきことは、レベルデザイナーが設定した角度ステップに基づいて輪を形成することです。手榴弾は投げるものなので、それらを一箇所でスポーンし、速度を変えて輪の形を描くように見せるだけにします (どうしてもそうしたければ、環状の開始位置で手榴弾をスポーンすることもできます)。Unreal Engine 内での最大回転値は 65535 なので 65535 まで数える for 反復子を追加します。各イタレーションが角度ステップを加算していきます。

  function Effect(UTEffectsGenerator InGenerator)
  {
     local int i;
  
     if (InGenerator != none && Angle > 0 && Angle < 65535)
     {
        InGenerator.PlaySound(SoundCue'A_Weapon_RocketLauncher.Cue.A_Weapon_RL_GrenadeFire_Cue');
  
        for (i = 0; i < 65535; i = i + Angle)
        {
        }
     }
  }
  

6. 完璧です。反復子を設定したので、次に個々の手榴弾をスポーンしてその回転を計算できます。反復のたびに回転値を保持するローカル変数を設定し、各反復時にローテータの Yaw 値だけを変更します。また、スポーンする手榴弾の参照を保持するローカル変数も作成します。

  function Effect(UTEffectsGenerator InGenerator)
  {
     local int i;
     local rotator r;
     local UTProj_Grenade grenade;
  
     if (InGenerator != none && Angle > 0 && Angle < 65535)
     {
        InGenerator.PlaySound(SoundCue'A_Weapon_RocketLauncher.Cue.A_Weapon_RL_GrenadeFire_Cue');
        r.Pitch = 0;
        r.Roll = 0;
  
        for (i = 0; i < 65535; i = i + Angle)
        {
           r.Yaw = i;
           grenade = Spawn(class'UTGame.UTProj_Grenade',,, InGenerator.Location, r);
        }
     }
  }
  

7. 最後に、各手榴弾の速度を設定します。ローテータの値を使い、ベクトルに変更して float で乗算し、速度値を求めます。この float 値を大きくまたは小さく設定すると、手榴弾の投擲速度が上昇または低下します。

  function Effect(UTEffectsGenerator InGenerator)
  {
     local int i;
     local rotator r;
     local UTProj_Grenade grenade;
  
     if (InGenerator != none && Angle > 0 && Angle < 65535)
     {
        InGenerator.PlaySound(SoundCue'A_Weapon_RocketLauncher.Cue.A_Weapon_RL_GrenadeFire_Cue');
        r.Pitch = 0;
        r.Roll = 0;
  
        for (i = 0; i < 65535; i = i + Angle)
        {
           r.Yaw = i;
           grenade = Spawn(class'UTGame.UTProj_Grenade',,, InGenerator.Location, r);
  
           if (grenade != none)
              grenade.Velocity = vector(r) * 300.f;
        }
     }
  }
  

以上で、手榴弾を輪を描くように投げるエフェクトが用意できました。

チュートリアル 12.16 - デリゲート & KISMET、パート VIII: UTEFFECT_GIBBAGE

このエフェクトでは、ランダムな方向に飛び出すジブを多数スポーンします。ジブの量はレベルデザイナーが制御できるようにします。

1. エディタのグローバル変数の宣言から始めましょう。

  var(Gibbage) int Amount;
  

これにより、レベルデザイナーがプロパティウィンドウを開くと、[Gibbage] カテゴリーに [Amount] 変数が表示されます。

2. 前の手順と同様に、親クラスの Effect 関数をオーバーライドします。

  function Effect(UTEffectsGenerator InGenerator)
  {
  }
  

3. ジブはすべて UTEffectsGenerator インスタンスからスポーンされるので、none チェックに続けて Amount がゼロより大きい値であるかどうかのチェックを行います。

  function Effect(UTEffectsGenerator InGenerator)
  {
     if (InGenerator != none && Amount > 0)
     {
     }
  }
  

4. ゼロから Amount 値まで反復処理する反復子を設定する必要があります。

  function Effect(UTEffectsGenerator InGenerator)
  {
     local int i;
  
     if (InGenerator != none && Amount > 0)
     {
        for (i = 0; i < Amount; ++i)
        {
  
        }
     }
  }
  

5. スポーン可能なジブの範囲を設定したので、次は使用するジブの部類をランダムに選択する別の関数を作成しましょう。GetRandomGibClass() という名前の新しい関数を作成します。これは UTGib_Human クラスを返します。

  function class<UTGib_Human> GetRandomGibClass()
  {
  }
  

6. Rand(5) をパラメータに持つ switch ステートメントを追加します。ここでは 5 種類のジブ (臓物) を使用できるので、5 を使用しています。

  function class<UTGib_Human> GetRandomGibClass()
  {
     switch(Rand(5))
     {
     }
  }
  

7. 5 種類のジブから選択できるので、ランダム値に応じて 5 つの部類をすべて返すだけにします。

  function class<UTGib_Human> GetRandomGibClass()
  {
     switch(Rand(5))
     {
        case 0:
           return class'UTGib_HumanArm';
  
        case 1:
           return class'UTGib_HumanBone';
  
        case 2:
           return class'UTGib_HumanChunk';
  
        case 3:
           return class'UTGib_HumanHead';
  
        case 4:
        default:
           return class'UTGib_HumanTorso';
     }
  }
  

8. いよいよジブをスポーンしてみましょう。ローカル変数 gib を作成し、作成したばかりのジブの一時参照をこれに格納できるようにします。

  function Effect(UTEffectsGenerator InGenerator)
  {
     local int i;
     local UTGib_Human gib;
  
     if (InGenerator != none && Amount > 0)
     {
        for (i = 0; i < Amount; ++i)
        {
           gib = Spawn(GetRandomGibClass(),,, InGenerator.Location, InGenerator.Rotation);
        }
     }
  }
  

9. Unreal Tournament 3 では、実際には物理エンジンである PhysX を用いてジブを物理的にシミュレートします。これは、現実的な物理法則が適用され、跳ね返りやワールドとの衝突が発生することを意味します。そのため、ジブの速度を設定し、PhysX でシミュレートできるように初期化する必要があります。

  function Effect(UTEffectsGenerator InGenerator)
  {
     local int i;
     local UTGib_Human gib;
  
     if (InGenerator != none && Amount > 0)
     {
        for (i = 0; i < Amount; ++i)
        {
           gib = Spawn(GetRandomGibClass(),,, InGenerator.Location, InGenerator.Rotation);
  
           if (gib != none)
           {
              gib.Velocity = Vector(RotRand()) * RandRange(200.f, 400.f);
  
              if (gib.GibMeshComp != none)
              {
                 gib.GibMeshComp.WakeRigidBody();
                 gib.GibMeshComp.SetRBLinearVelocity(gib.Velocity, false);
                 gib.GibMeshComp.SetRBAngularVelocity(VRand() * 50.f, false);
              }
           }
        }
     }
  }
  

最初にランダムな回転数に 200 から 400 の範囲のランダムな値を掛けてジブのランダム速度を設定します。次に、ジブが PhysX で必要なメッシュコンポーネントを備えているかどうかを調べます。コンポーネントがある場合、それをアクティブにして PhysX に引き継いでランダムな速度を設定できるようにします。SetRBLinearVelocity() はオブジェクトの移動速度を設定します。SetRBAngularVelocity() はオブジェクトの旋回速度を設定します。割と精巧なつくりのジブエフェクトがこれで完成です。

最初にランダムな回転数に 200 から 400 の範囲のランダムな値を掛けてジブのランダム速度を設定します。次に、ジブが PhysX で必要なメッシュコンポーネントを備えているかどうかを調べます。コンポーネントがある場合、それをアクティブにして PhysX に引き継いでランダムな速度を設定できるようにします。SetRBLinearVelocity() はオブジェクトの移動速度を設定します。SetRBAngularVelocity() はオブジェクトの旋回速度を設定します。割と精巧なつくりのジブエフェクトがこれで完成です。

チュートリアル 12.16 - デリゲート & KISMET、パート IXテストベッドの設定

1. コードをコンパイルし、Unreal エディタを起動します。

2. Unreal エディタがロードされたら、DM-Chapter12-Kismet を開きます。装飾用の静的メッシュ、ライトとプレーヤースタート地点を含むだけの、飾り気のないレベルであることが分かります。


図 12.9 - DM-CH_12_Kismet マップ

3. マップがロードされたら、次にコードパッケージ内のクラスにアクセスして新しいクラス (UTEffectsGenerator、UTEffect_Explosion、UTEffect_Gibbage、UTEffect_GrenadeRing) をマップに追加できるようにします。これらは Actor ブラウザから自動的にアクセスできるはずですが、できない場合はスクリプト パッケージをロードする必要があります。その場合は Generic ブラウザが開いていない場合はこれを開き、[Actor Classes] タブに切り替えて、[File]、[Open] の順にクリックすると、開きたいパッケージを選択するためのダイアログが開きます。


図 12.10 - File->Open コマンドを使い、スクリプトパッケージをロードします。

パッケージがロードされると、Actor ブラウザに新しいクラスが表示されるのが分かります。


図 12.11 - Actor ブラウザのクラスツリー内にクラスが現われます。

4. 次に、UTEffectGenerator を選択してマップ内のどこかに配置します。ちょうどいい感じのメッシュが右の部屋のフロア中央に配置されているので、その真上に UTEffectsGenerator を置きましょう。ActorBrowser で [UTEffectsGenerator] を選択し、レベルビューポート内でこれを配置したい位置を右クリックします。コンテキストメニューで、[Add UTEffectsGenerator Here] をクリックすると配置できます。


図 12.12 - UTEffectGenerator がマップに追加されました。

5. 新たに追加した UTEffectsGenerator は、メッシュのようにチューブの上に乗っているはずです。


図 12.13 - UTEffectGenerator アクタの配置。

6. UTEffectsGenerator の回転プロパティに手を加える必要があります。このオブジェクトが選択されているので (そうでない場合は選択してください)、F4 を押すとプロパティ ウィンドウが表示されます。


図 12.14 - UTEffectGenerator の Rotation プロパティを調整します。

7. 次に、レベル内に UTEffect インスタンスを追加します。レベルの片隅に配置し、簡単に見つけられるようにします。UTEffectsGenerator の場合と同様に、Actor ブラウザで各インスタンスを選択してから、レベルを右クリックしてコンテキストを使って追加します。


図 12.15 - レベルジオメトリの外側に UTEffect アクタが配置されました。

8. この例には、UTEffectsGenerator をトリガする物理ボリュームが必要です。戸口と同じ場所を覆うビルダブラシの配置から始めましょう。設定を調整して戸口に上手く合わせます。


図 12.16 - Red Builder ブラシの配置

9. ビルダブラシを目的の位置に配置したので、左側のメニューにある [Add Volume] ボタンを右クリックし、[PhysicsVolume] を選択してマップに追加します。


図 12.17 - [Add Volume] メニューから PhysicsVolume が選択されました。

10. マップに物理ボリュームを配置しました。クリックして選択してください。3D ビューポートで選択しにくい場合は、2D ビューポートに切り替えてから選択します。


図 12.18 - PhysicsVolume が選択されました。

11. Unreal エディタのメインツールバーにある [Kismet] ボタンをクリックし、Kismet を開きます。Kismet エディタが表示されます。


図 12.19 - Kismet エディタ

12. 右クリックしてコンテキストメニューを開きます。物理ボリュームを使って Touch イベントを作成しましょう (これを実行する前に、忘れずに物理ボリュームを選択してください)。これにより、物理ボリュームに接触するとトリガする Kismet イベントノードが作成され、2 つのエンジン固有のイベント (touched と untouched) に反応します。Touched は、アクタの衝突がオーナーの衝突範囲内で接触するとトリガされ、Untouched はその逆です。Touched と Untouched は一度だけ呼び出されます。つまり、別のアクタに続けて接触することはありません。


図 12.20 - Touch イベントが作成されました。

13. Touched イベントノードの調整が必要です。デフォルトでは、これは MaxTriggerCount を 1 に設定し、つまり一度だけトリガした後無効になりますが、この特別なケースでは永久にトリガ可能にしたいのです。左下には、選択された各ノードのプロパティ セクションがあり、そのセクションを下にスクロールすると [MaxTriggerCount] という値があります。これを 0 に設定します。


図 12.21 - MaxTriggerCount を 0 に設定します。

14. 次に、物理ボリュームに接触したときの反応を実装します。何かを使うアクションノードを作成しましょう。実は、これが前に作成した SEQACT_USE ノードです。もう一度コンテキストメニューを参照して、[Effect Generator] カテゴリーから [Use] アクションノードを追加します。


図 12.22 - Use アクションが追加されました。

15. 次に、いずれかのレベルビューポートで UTEffectsGenerator を選択します。


図 12.23 - UTEffectGenerator が選択されました。

16. Kismet エディタ ウィンドウに戻り、ワークスペース内で右クリックして [New Object Var Using UTEffectGenerator_0] (新しいオブジェクト変数 (UTEffectGenerator_0 を使用)) を選択して、選択した UTEffectsGenerator の参照を Kismet 内に追加します。

注意: 実際のマップ内のアクタの名前が異なる可能性があります。


図 12.24 - UTEffectGenerator オブジェクト変数が作成されました。

17. 次にすべてをつなぎましょう。PhysicsVolume_0 Touch ノードの Touched の右側にあるブラックボックスをクリックしてドラッグし、Use ノードの In の左側にあるブラックボックスにつなぎます。次に、Use ノードの Target の下にある紫のボックスをクリックしてドラッグし、これを UTEffectsGenerator_1 につなぎます。この接続により、物理ボリュームに接触すると Kismet が Use ノード をトリガし、Use ノードは UTEffectsGenerator_1 を使用します。


図 12.25 - 関連付けを適切に行いました。

18. よく出来ました。必要な基本アクションはこれで処理されます。しかし、UTEffectsGenerator にエフェクトをつなぐプロセスがまだ処理されていません。3 通りのエフェクトがあるので、それらをランダムに UTEffectsGenerator につなぎましょう。エフェクトの選択にはランダムスイッチを使います。右クリックしてコンテキストメニューを開き、[Action]->[Switch] ->[Random] を選択して Random スイッチを追加します。


図 12.26 - Random スイッチが追加されました。

19. Random スイッチノードのプロパティを変更する必要があります。画面の左下でスクロールして、[LinkCount] の値を 1 から 3 に変更します。


図 12.27 - LinkCount が 3 に設定されました。

20. 右クリックしてコンテキストメニューを開き、[Effect Generator] カテゴリーの [Add Action] の下にある 3 つの Set Effect Kismet ノードを追加します。これは、実際には前に作成した SeqAct_SetEffect です。3 つの Set Effect Kismet ノードを追加した後で、それらを前述の手順と同様に Random スイッチ Kismet ノードにつなぎます。


図 12.28 - Set Effect アクションを Random スイッチにつなぎました。

21. 他もすべて同様につなぎます。Set Effect Kismet ノードはすべて UTEffectsGenerator につないで、エフェクトを設定する UTEffectsGenerator の存在を認識できるようにする必要があります。また、Use Kismet ノードの Out を、Random Kismet ノードの In につなぎ、Use Kismet ノードがトリガされると Random Kismet ノードをトリガし、そのノードが今度は 3つの Set Effect Kismet ノードのいずれかをトリガするようにします。


図 12.29 - 残りの接続が完成しました。

22. レベル ビューポートに戻り、レベル内に配置した UTEffects のいずれかを選択し、上記の UTEffectsGenerator で行ったのと同じ手順で、これをオブジェクトとして Kismet に追加します。右クリックしてコンテキストメニューを開くと、選択した UTEffect がコンテキストメニュー内に現われます。


図 12.30 - UTEffect オブジェクト変数が作成されました。

23. 他の 2 つの UTEffects についても上記の手順を繰り返します。3 つののエフェクトをすべて Kismet エディタ ウィンドウ内に配置したら、次は Set Effect Kismet ノードにそれらをつなぎます。


図 12.31 - 3 つの UTEffect 変数はすべて Set Effect アクションにつながれています。

24. 良く出来ました。これでロジックはほぼ整いました。しかし、最初にレベルをロードしたときに UTEffectsGenerator にまだ UTEffect が割り当てられていないので、これを修正しましょう。右クリックしてコンテキストメニューを開き、Level Loaded and Visible イベントノードを作成します。


図 12.32 - Level Loaded and Visble イベントを追加しました。

25. この Level Loaded And Visible Kistmet ノードを、Random Kismet ノードにつなぐ必要があります。その理由は、レベルのロード時にはエフェクトを UTEffectsGenerator に設定するだけでよく、他には何も必要ないためです。


図 12.33 - 新しいイベントを Random スイッチにつなぎます。

26. Kismet に関する作業はこれだけです。次に必要なのは、全体をテストすることです。メインツールバーの [Build All] ボタンを押して、レベル全体をビルドします。

27. レベルがビルドされたら [PIE] ボタンをクリックします。レベルをテストするための小さなウィンドウが開きます。

28. レベルに配置したボリュームを走り抜け、エフェクトの 1 つが現われたら成功です。


図 12.34 - エフェクトの 1 つがアクティブになりました。

29. UTEffect_Gibbage と UTEffect_GrenadeRing 内には編集可能な変数を作成したので、それらはいずれも Unreal エディタ内に項目として表示されます。レベルデザイナーはこれを利用して、エフェクトの振る舞いを変更することができます。同様に他の変数も公開することで、作成したエフェクトをプログラマーによって非常に順応性の高いものにすることができます。このチュートリアルをさかのぼってこれらの変数を調整し、多彩なエフェクトを作成してみてください。


図 12.35 - Gibbage と Grenade エフェクトの編集可能プロパティ

このチュートリアルでは、デリゲートを Kismet と併用する方法を検討しました。Kismet はそのアプローチがよりオブジェクト指向であるため、この例ではデリゲートをインスタンス内でを関数にバインドするだけで、本来の目的どおりに機能させることができました。このデリゲートの使い方は非常に大事なポイントで、これによりエフェクトジェネレータにより生成されるエフェクトをレベルデザイナーが変更できるだけでなく、使いやすくするためにそれらのエフェクトに手を加えることもできるようにしています。同じことを達成するための手段は他にもありますが、デリゲートを用いると、このタスクを一層簡単に、かつ非常に柔軟な方法で行うことができます。

12.10 - まとめ

この章ではデリゲートを深く掘り下げて紹介しましたが、この章から何か有意義なものを学んでいただけたと思います。デリゲートは通常、コード実行がさまざまに変化する場合や、特に順応性のあるコードを記述しなければならない場合に使われます。UnrealScript においてデリゲートは便利なツールなので、特定タイプのタスクの実行可能な手段の 1 つとして常に検討されることを強くお勧めします。Unreal Tournament 3 では、サードパーティに特定イベントの動作を変更できる機会を与えるという単純な理由で、主に GUI 内で使用されていましたが、この章の各チュートリアルに示したように、実はデリゲートはほぼどこでも利用できます。

残念ながら、デリゲートを使用するかしないかの決定は主に経験に頼るところです。デリゲートのマッピング先の関数を Unreal Engine が決定しなければならないので、他のいくつかの方法よりずっと簡潔ですが、処理が比較的遅くなる可能性があります。

SUPPLEMENTAL FILES