UDN
Search public documentation:

UnrealScriptReferenceJP
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

UnrealScript 言語リファレンス

ドキュメントの概要: UnrealScript の概要と簡潔なリファレンス。 原作者は Tim Sweeney (EpicGames)。

ドキュメントの変更ログ:長期間にわたり作成され、維持。

Contents

はじめに

クイック リンク

UnrealScript 虎の巻? と MasteringUnrealScriptTOC? チュートリアルに目を通すようにしてください。

本書の目的

本書は、UnrealScript 言語について説明する技術文書です。チュートリアルでも、UnrealScript コードの便利な例を提供するものでもありません。UnrealScript の例としては、エンジンのソースコードを紹介しています。ソースコードは、実際に使用されている何万行もの UnrealScript コードであり、AI、動き、インベントリ、トリガなどの多くの問題を解決しています。手始めとして、「アクタ」、「オブジェクト」、「コントローラ」、「ポーン」、「武器」のスクリプトに目を通すとよいでしょう。

本書は、C/C++ または Java の実用的知識を持ち、オブジェクト指向プログラミングに詳しく、Unreal のプレー経験と UnrealEd 編集環境を使用した経験を持つ読者を想定して書かれています。

オブジェクト指向プログラミング初心者であるプログラマーの方は、amazon.co.jp または書店にてJavaプログラミングの入門書を入手されることを強くお勧めします。Java は UnrealScript に非常に似ており、クリーンでわかりやすいアプローチを持っているため、学ぶのに適した言語です。

UnrealScript の設計目標

UnrealScript は、開発チームとサードパーティの Unreal 開発者に、ゲームプログラミングに必要な機能とニュアンスを自然に記述できる強力なビルトイン プログラミング言語を提供するために作られました。

UnrealScript の主要な設計目標は次の通りです。

  • 従来のプログラミング言語が対応しない、時間、ステート、プロパティ、ネットワーキングの主要概念をサポートすること。これにより、UnrealScript コードが大幅に簡素化されます。C/C++ ベースの AI とゲームロジック プログラミングが複雑なのは、ある一定のゲーム時間を必要とするイベントの処理と、オブジェクトのステートに依存するイベントの処理が原因です。この結果、C/C++ はコーディング・理解・保守するのも、デバッグするのも困難なスパゲティコードとなります。UnrealScript は、時間、ステート、そしてネットワーク レプリケーションをネイティブ サポートするため、ゲームプログラミングが大幅に簡素化されます。

  • Java スタイルの簡潔なプログラミング、オブジェクト指向、コンパイル時のエラーチェックを提供すること。Java はウェブ プログラマーにクリーンなプログラミングプラットフォームを提供しますが、UnrealScript も、同じくクリーン、シンプル、そして強力な 3D ゲーム用プログラミング言語を提供します。Java に由来する UnrealScript の主要なプログラミング概念は次の通りです。
    • 自動ガーベジコレクションを備えたポインタレス環境
    • シンプルな単一継承クラスグラフ
    • コンパイル時の強力な型チェック
    • 安全なクライアントサイド実行「サンドボックス」
    • なじみのある C/C++ や Java コードの外観と操作

  • ゲームオブジェクトと対話処理に関して、ビットとピクセルではなく、豊かなでハイレベルなプログラミングを可能にすること。UnrealScript では、開発の簡潔さとパワーを優先し、実行速度を犠牲にすることを選びました。つまり、Unreal において、パフォーマンスを重視する低レベルのコードは C/C++ で書かれています。この場合、コードが複雑になってもパフォーマンスを向上させる方を優先しています。UnrealScript は、ビットやピクセルレベルではなく、それより上のレベル、つまりオブジェクトおよび対話レベルで機能します。

UnrealScript の初期開発段階において、現在の実装に到達するまで、いくつかの主要なプログラミングパラダイムを検討し、破棄しました。最初に、Unreal のスクリプト言語の基礎として、Sun と Microsoft Java VM for Windows を使用することを検討しました。その結果、Unreal の観点から、C/C++ と比較して Java はあまりプログラミング上の利点がないうえ、必要な言語機能 (たとえば演算子オーバーローディングなど) を備えていないという、もどかしい制約があることがわかりました。また、大きなオブジェクトグラフを扱う場合、VMタスクスイッチのオーバーヘッドと Java ガーベジコレクタの非効率性の両方が原因で、非常にスピードが遅くなることもわかりました。次に、UnrealScript の初期の実装を Visual Basic の改良型に基づいて作成しました。これはうまく動作しましたが、C/C++ に慣れているプログラマーにとって、あまり使いやすいものではありませんでした。UnrealScript のベースを C++/Java の改良型にするという最終的な決定は、ゲーム特有の概念を言語定義自体に反映することと、スピードと親しみやすさを考慮して行われました。Unreal コードベースが多くの面ではるかに簡単になったため、これは良い決断でした。

UnrealEngine3 が始めての方

すでに UnrealScript に精通している方のために、UnrealEngine2 から UnrealScript が大きく変更された点を簡単に紹介します。

  • レプリケーション - UE3 では、replication 文が変更されました。:
    • レプリケーションブロックは、変数にのみ使用されるようになりました
    • 関数レプリケーションは、 function specifiers を使用して定義されるようになりました ( Server, Client, Reliable )
  • ステートのスタッキング - ステートをスタックにプッシュおよびポップできるようになりました
  • UnrealScript プリプロセッサ - マクロや条件付のコンパイルのサポート。
  • デバッギング機能 - 新しいデバッギング関連機能が追加されました
  • デフォルトプロパティ - defaultproperties ブロックの処理が変わり、多少改善されました
    • Struct デフォルト - struct もデフォルトプロパティを持つことがでるようになりました
    • config やローカライズ変数へのデフォルト値は設定できなくなりました。
    • 実行時の defaultproperties はリードオンリーとなり、 class'MyClass'.default.variable = 1 と設定できなくなりました。
  • 動的配列 - 動的配列には、構成要素のインデックスを検索できる新しい関数 find() が追加されました
  • Dynamic Array Iterators - foreach 演算子は動的配列にて機能しています。
  • Delegate 関数の引数 - UE3 では、関数の引数として Delegate を渡すことができます
  • インターフェイス -インターフェイスのサポートが追加されました
  • 他のクラスからの定数へのアクセス : class'SomeClass'.const.SOMECONST
  • 複数タイマーのサポート
  • 関数引数のデフォルト値 - これにより、関数のオプション引数にデフォルト値を定義できるようになりました。
  • ツールチップ サポート – UnrealScript 内で /** tooltip text */ の形のコメントが宣言の上にあるようなプロパティにマウスを置くと、エディタ プロパティ ウィンドウがツールチップを表示するようになりました。
  • メタデータ サポート – プロパティをさまざまなタイプのメタデータと関連付けることで、インゲームとインエディタの機能を拡張しました。

プログラム構造の例

ここでは、典型的で、シンプルな UnrealScript クラスの例を示すことで、UnrealScript の構文と機能をわかりやすく示します。本書はソースコードと一致させていないため、記載されているコードは、実際の Unreal ソースとは異なる場合があることにご注意ください。

  //=====================================================================
  // TriggerLight.
  // A lightsource which can be triggered on or off.
  //=====================================================================
  class TriggerLight extends Light;
  
  //---------------------------------------------------------------------
  // Variables.
  
  var() float ChangeTime; // Time light takes to change from on to off.
  var() bool bInitiallyOn; // Whether it's initially on.
  var() bool bDelayFullOn; // Delay then go full-on.
  
  var ELightType InitialType; // Initial type of light.
  var float InitialBrightness; // Initial brightness.
  var float Alpha, Direction;
  var actor Trigger;
  
  //---------------------------------------------------------------------
  // Engine functions.
  
  // Called at start of gameplay.
  function BeginPlay()
  {
     // Remember initial light type and set new one.
     Disable( 'Tick' );
     InitialType = LightType;
     InitialBrightness = LightBrightness;
     if( bInitiallyOn )
     {
        Alpha = 1.0;
        Direction = 1.0;
     }
     else
     {
        LightType = LT_None;
        Alpha = 0.0;
        Direction = -1.0;
     }
  }
  
  // Called whenever time passes.
  function Tick( float DeltaTime )
  {
     LightType = InitialType;
     Alpha += Direction * DeltaTime / ChangeTime;
     if( Alpha > 1.0 )
     {
        Alpha = 1.0;
        Disable( 'Tick' );
        if( Trigger != None )
           Trigger.ResetTrigger();
     }
     else if( Alpha < 0.0 )
     {
        Alpha = 0.0;
        Disable( 'Tick' );
        LightType = LT_None;
        if( Trigger != None )
           Trigger.ResetTrigger();
     }
     if( !bDelayFullOn )
        LightBrightness = Alpha * InitialBrightness;
     else if( (Direction>0 &amp;amp;amp;&amp;amp;amp; Alpha!=1) || Alpha==0 )
        LightBrightness = 0;
     else
        LightBrightness = InitialBrightness;
  }
  
  //---------------------------------------------------------------------
  // Public states.
  
  // Trigger turns the light on.
  state() TriggerTurnsOn
  {
     function Trigger( actor Other, pawn EventInstigator )
     {
        Trigger = None;
        Direction = 1.0;
        Enable( 'Tick' );
     }
  }
  
  // Trigger turns the light off.
  state() TriggerTurnsOff
  
  {
     function Trigger( actor Other, pawn EventInstigator )
     {
        Trigger = None;
        Direction = -1.0;
        Enable( 'Tick' );
     }
  }
  
  // Trigger toggles the light.
  state() TriggerToggle
  {
     function Trigger( actor Other, pawn EventInstigator )
     {
        log("Toggle");
        Trigger = Other;
        Direction *= -1;
        Enable( 'Tick' );
     }
  }
  
  // Trigger controls the light.
  state() TriggerControl
  {
     function Trigger( actor Other, pawn EventInstigator )
     {
        Trigger = Other;
        if( bInitiallyOn ) Direction = -1.0;
        else Direction = 1.0;
        Enable( 'Tick' );
     }
     function UnTrigger( actor Other, pawn EventInstigator )
     {
        Trigger = Other;
        if( bInitiallyOn ) Direction = 1.0;
        else Direction = -1.0;
        Enable( 'Tick' );
     }
  }
  

このスクリプトにおける重要な要素を以下に記します。

  • クラス宣言。各クラスは、1 つの親クラスを「拡張」(親クラスから派生) します。各クラスは一緒に配布されるオブジェクトを集めた「パッケージ」に属しています。すべての関数と変数はクラスに属しており、そのクラスに属しているアクタを通してのみアクセス可能です。システム全体のグローバル関数や変数は存在しません。詳細はこちら

  • 変数宣言。UnrealScript は、ほとんどの C/Java の基底型、オブジェクト参照、struct、そして配列を含む、非常に多様な一連の変数型をサポートしています。さらに、変数を編集可能なプロパティにすることができます。これによって、ゲームデザイナーはプログラミングを行うことなく UnrealEd 内から変数にアクセスできます。これらプロパティは、 var= ではなく、 var() 構文を使用して指定されます。詳細はこちら

  • 関数。関数はパラメータのリストを取り、任意で値を返します。関数はローカル変数を持つことができます。関数には、UnrealEngine 自体 (たとえば BeginPlay) から呼び出されるものや、他の場所にあるスクリプトコード (例: Trigger) のコードから呼び出されるものがあります。詳細はこちら

  • コード。 forwhilebreakswitchif 、その他の標準的なCおよび Java キーワードはすべてサポートされています。中括弧とセミコロンは、UnrealScript で C、C++、および Java の場合と同じように使用されます。

  • アクタとオブジェクト参照。ここでは、オブジェクト参照を使用して、関数が別のオブジェクトから呼び出される例をいくつか示します。詳細はこちら

  • 「state (ステート)」 キーワード。このスクリプトは、いくつかの「ステート」を定義します。「ステート」とは、アクタがあるステートにあるときにのみ実行される関数、変数、およびコードの集まりです。詳細はこちら

  • UnrealScript のすべてのキーワード、変数名、関数、オブジェクト名では、大文字小文字の区別をしないことを覚えておいてください。UnrealScript にとって、 DemondemON 、および demon は、同じです。

Unreal 仮想マシン

Unreal 仮想マシン は、いくつかのコンポーネントで構成されています。そのコンポーネントとは、サーバ、クライアント、レンダリング エンジン、エンジンサポートコードです。

Unreal サーバーは、プレーヤとアクタ間で行われるすべてのゲームプレーと対話を制御します。シングルプレイヤーゲームの場合、Unreal クライアントと Unreal サーバーの両方が、同じマシン上で実行されます。インターネットゲームでは、専用サーバーが 1 基のマシン上で実行されます。このマシンに接続されているすべてのプレーヤは、クライアントです。

すべてのゲームプレーは「レベル」内で発生します。レベルとは、ジオメトリとアクタを含む自己完結型の環境です。UnrealServer は同時に複数のレベルを実行することができますが、各レベルは独立して動作し、お互いから保護されています。つまり、アクタはレベル間を移動することはできず、あるレベルのアクタは別のレベルのアクタとコミュニケートできません。

マップ内の各アクタはプレーヤの制御下にあるか (ネットワークゲームでは数多くのプレーヤが存在する場合があります) 、スクリプトの制御下にあります。アクタがスクリプトの制御下にあるとき、そのスクリプトはアクタの動作方法と、他のアクタとの対話方法をすべて定義します。

ワールド内でアクタが走り回り、スクリプトが実行され、イベントが発生しているステートの中で、UnrealScript の実行の流れを理解する方法があれば、お知りになりたいでしょう。その答えは以下の通りです。

時間を管理するために、Unreal はゲームプレーの各秒を「Tick」に分割します。Tick は、レベル内のすべてのアクタが更新される時間の最小単位です。Tick は通常、1/100 秒から 1/10 秒の間にあります。Tick 時間の制限事項は、CPU パワーの大きさによってのみ制限されます。マシンが早ければ、Tick の単位時間が短くなります。

UnrealScript の一部のコマンドは、実行に 0 Tick を費やし (すなわち、まったくゲーム時間を使わずに、実行されます) 、その他は多くの Tick を費やします。ゲーム時間を使う必要がある関数は、「潜在関数」と呼ばれています。潜在関数の例の一部として、Sleep、FinishAnim、および MoveTo があります。UnrealScript 潜在関数を呼び出すことができるのは、あるステート内のコード (いわゆる「ステートコード」) のみで、(ステート内で定義する関数を含む) 関数内のコードからは潜在関数を呼び出すことはできません。

アクタが潜在関数を実行している間は、潜在関数が終了するまでそのアクタのステート実行は継続しません。しかし、その他のアクタ、または VM は、アクタ内の関数を呼び出すことが可能です。この結果、たとえ、潜在関数が中断している間でも、いつでもすべての UnrealScript 関数を呼び出すことができます。

従来のプログラミングの観点から見ると、UnrealScript は、レベル内のアクタがまるで独自の実行「スレッド」を持っているかのように機能します。Unreal が内部で Windows スレッドを使用しないのは、非効率だからです (Windows 95 と Windows NT は、同時に何千ものスレッドを効率的に処理できません)。そのかわり、UnrealScript はスレッドをシミュレートします。これは、UnrealScript コードではっきりと見ることはできないものの、UnrealScript と対話するC++コードを書いている時には、はっきりとわかります。

すべての UnrealScript は、相互に独立して実行されます。レベルの中で 100 匹のモンスターが歩き回っていても、モンスターのうち 100 匹のスクリプトが、各「Tick」毎に同時に独立して実行されています。

オブジェクト ヒエラルキー

UnrealScript で作業を開始する前に、Unreal 内のオブジェクト同士のハイレベルな関係を理解することが重要です。Unreal のアーキテクチャは、他のほとんどのゲームと比べて大きく進化しています。Unreal はオブジェクトグラフ、シリアライゼーション、オブジェクトライフタイム、ポリモーフィズムなど、ハイレベルなオブジェクト指向概念をサポートする明確なオブジェクトモデルを持つことから、純粋なオブジェクト指向 (非常に COM/ActiveX に似ています) を実現しています。これまで、Doom や Quake など多くのゲームは、コンテンツレベルでかなりの拡張性を持っていましたが、ほとんどのゲームはモノリシック (一枚岩) 的に設計されており、主要な機能はハードコードされ、オブジェクトレベルでの拡張は不可能でした。Unreal のオブジェクト指向形式には、大きなメリットがあります。実行時に主な新機能とオブジェクトタイプを Unreal に追加できることです。また、この拡張は、(たとえば) 数多くの既存コードを変更する方法ではなく、サブクラス作成という形式を取ることができます。この形式による拡張性はきわめて強力です。というのも、Unreal コミュニティにおいて、だれもが相互運用できる Unreal 強化機能の作成を促進できるためです。

Object は、Unreal におけるすべてのオブジェクトの親クラスです。Object から派生しているため、Object クラスのすべての関数には、どこからでもアクセスできます。オブジェクト自体は、何も有益なことをしないという点で、抽象基底クラスです。すべての機能は、Texture (テクスチャマップ) などのサブクラス、TextBuffer (テキストのかたまり) 、および Class (他のオブジェクトのクラスを記述) などのサブクラスによって提供されます。

Actor (オブジェクトを拡張) は、Unreal におけるすべての独立型ゲームオブジェクトの親クラスです。Actor クラスは、アクタが移動したり、他のアクタと対話したり、環境に影響を及ぼしたり、その他の有益なゲーム関連の行為を行ったりするために必要な、すべての機能を含んでいます。

Pawn (Actor を拡張) は、Unreal のすべてのクリーチャーとプレーヤの親クラスで、高レベルの AI とプレーヤ操作が可能です。

Class (オブジェクトを拡張) は、オブジェクトのクラスを記述する特殊な種類のオブジェクトです。これは、最初は紛らわしく見えるかもしれません。しかし、これは優れた概念であり、これから Class オブジェクトを扱うケースに多く出くわすことになるでしょう。たとえば、UnrealScript で新しいアクタをスポーンするとき、Class オブジェクトを使用して新しいアクタのクラスを指定できます。

UnrealScript では、どんな Object クラスのためにでもコードを書くことができますが、99% Actor から派生したクラスのコードを書くことになるでしょう。有用な UnrealScript 機能の大部分は、ゲームに関連しており、アクタを処理するものです。

クラス

1 スクリプトは 1 クラスに正確に対応しており、スクリプトはクラス、クラスの親、そしてクラスに関連したその他の情報を宣言することから始まります。もっとも単純な形式は次のとおりです:

  class MyClass extends MyParentClass;
  

ここでは、「MyClass」という名前をつけた新しいクラスを宣言します。このクラスは「MyParentClass」の機能を継承しています。さらに、このクラスは「MyPackage」という名前をつけたパッケージに属しています。

各クラスは、その親クラスから変数、関数、ステートのすべてを継承します。次に、新しい変数宣言を追加し、新しい関数を追加し (または既存の関数をオーバーライド)、新しいステートを追加できます (または既存のステートに機能を追加)。

UnrealScript での典型的なクラス設計手順は、必要な機能の大部分を持つ既存のクラス (例: すべてのモンスターの基底クラスである Pawn クラス) を拡張して、新しいクラス (たとえばミノタウロス モンスター) を作成します。このアプローチなら、最初から機能を作る必要はまったくありません。つまり、カスタマイズの必要がない既存機能をすべてキープしながら、カスタマイズしたい新機能だけを追加することができるのです。このアプローチは、Unreal で AI を実装する際に特に威力を発揮します。ビルトイン AI システムが、独自に作成するクリーチャーの基礎単位として使用できる、おびただしい量の基礎単位を提供することができるのです。

クラス宣言には、クラスに影響を及ぼす、いくつかのオプション指定子を取ることができます。その指定子には以下のものがあります。

  • Native(PackageName): 「このクラスは、舞台裏で C++ サポートを使用します」という意味です。Unreal では、ネイティブクラスがクラスのパッケージに対応する DLL 内に C++ 実装を含んでいることを期待します。 Only native クラスのみがnaitve 関数または native インターフェースの実装をすることができます。 Native クラスは常にnative クラスから派生しなくてはいけません。Native クラスは、スクリプト変数および指定した関数で作用するよう必要な グルー で自動生成 C++ ヘッダファイルを作成します。デフォルトで、 PackageName がスクリプトクラスがあるパッケージとなっています。 例えば、クラスが Engine packageにある場合、自動生成ヘッダは EngineClasses.h と呼ばれます。
  • NativeReplication: このクラスが、ネットワーク全体でステートをレプリケートするために、C++ コードを使用することを示します。 Only valid for native classes.
  • DependsOn(ClassName[,ClassName,...]): ClassName が最初にコンパイルされることに依存しているクラスであるため、このクラスが必ず ClassName の後にコンパイルされるようにします。同じパッケージ内で定義された各種クラスについてのみ機能します。1 つのクラス宣言内で複数の DependsOn(...) 指定子を使用できます。
  • Abstract: クラスを「抽象基底クラス」として宣言します。これは、クラス単独では意味がないため、ユーザーがこのクラスのアクタを UnrealEd のワールドに追加できないようにするためです。たとえば、「Pawn」基底クラスは抽象クラスですが、「Brute」基底クラスは抽象クラスではありません。ワールドに Brute を配置できますが、ワールドに Pawn を配置することはできません。Abstract(抽象型)指定子はサブクラスに継承されません。
  • Deprecated: このクラスに属しているすべてのオブジェクトはロードされますが、保存されません。非推奨アクタの配置済みインスタンスがあれば、エディタ内でマップがロードされるときに、レベル設計者への警告を生成します。
  • Transient: 「このクラスに属しているオブジェクトを決してディスクに保存してはなりません」という意味です。この指定子は、プレーヤまたはウィンドウなど、性質上非永続型である特定の種類のNative クラスと組み合わせてのみ有効です。
  • NonTransient: 基底クラスから承継された Transient キーワードを無効にします。
  • Config(IniName): このクラスが .ini にデータを格納できることを表します。クラスに構成できる変数 ("config" または "globalconfig" で宣言) がある場合、指定された構成ファイルにこれらの変数を格納するようにします。このフラグは、すべての子クラスにプロパゲートされ、ネゲートされませんが、子クラスは Config キーワードを再宣言し、異なる IniName を指定することにより、.ini ファイルを変更することができます。通常、IniName はデータを格納する .ini ファイルの名前を指定しますが、いくつかの名前には、以下のような特別な意味があります。
    • Config(Engine): エンジン構成ファイルを使用します。このファイルの名前は、ゲームの名前に "Engine.ini" がついています。例えば、ExampleGame のエンジン構成ファイルの名前は ExampleEngine.ini となります。
    • Config(Editor): エディタ構成ファイルを使用します。このファイルの名前は、ゲームの名前に "Editor.ini" がついています。例えば、ExampleGame のエディタ構成ファイルの名前は ExampleEditor.ini となります。
    • Config(Game): ゲーム構成ファイルを使用します。このファイルの名前は、ゲームの名前に "Game.ini" がついています。例えば、ExampleGame のゲーム構成ファイルの名前は ExampleGame.ini となります。
    • Config(Input): 入力構成ファイルを使用します。このファイルの名前は、ゲームの名前に "Input.ini" がついています。例えば、ExampleGame の入力構成ファイルの名前は ExampleInput.ini となります。

  • PerObjectConfig: このクラスの構成情報は、オブジェクト毎に保存されます。保存先の.iniファイルには、各オブジェクトに関係した名前を持つセクションがあります。
  • PerObjectLocalized: このクラスのローカライズされたデータは、オブジェクトごとに定義され、そこでは [ObjectName ClassName] の形でオブジェクトの名前を取ったローカライゼーション ファイルの中で各オブジェクトがセクションを持ちます。このキーワードは、子クラスにプロパゲートされます。
  • EditInlineNew: エディタ。 このオブジェクトのインスタンスは、UnrealEd 内のインライン入力により作成できます (デフォルトビヘイビアでは既存のオブジェクトのみを参照し、プロパティウィンドウを通じて割り当てられます)。 このフラグはすべの子クラスにプロパゲートされます。; 子クラスは NotEditInlineNew キーワードを使いこのフラグをオーバーライドします。
  • NotEditInlineNew: エディタ。基底クラスから承継された EditInlineNew キーワードを無効にします。どの親クラスも EditInlineNew を使用していない場合は効果がありません。
  • Placeable: エディタ。 このクラスは UnrealEd にて作成されレベル、UIシーンまたは Kismet ウィンドウ(クラスタイプによる)に配置されることを意味しています。このフラグはすべの子クラスにプロパゲートされます。; 子クラスは NotPlaceable キーワードを使いこのフラグをオーバーライドします。
  • NotPlaceable: エディタ。基本クラスから継承された Placeable キーワードをネゲートします。このクラスは、UnrealEd などのレベル内に配置されないことを意味しています。
  • HideDropDown: エディタ。UnrealEd プロパティウィンドウボックスにこのクラスが表示されるのを防ぐ。
  • HideCategories(Category[,Category,...]): エディタ。 このクラスのオブジェクト用の UnrealEd プロパティウィンドウにで非表示になるべきカテゴリを指定します。 カテゴリのない宣言された変数を非表示にするには、変数を宣言するクラス名を使います。
  • ShowCategories(Category[,Category,...]): エディタ。基本クラスから継承された HideCategories keyword キーワードをネゲートします。
  • AutoExpandCategories(Category[,Category,...]): エディタ。このクラスのオブジェクト用の UnrealEd プロパティウィンドウにて自動的に拡張されるカテゴリを指定します。カテゴリのない宣言された変数を自動拡張をするには、変数を宣言するクラス名を使います。
  • Collapsecategories: エディタ。すべてのカテゴリは単一カテゴリになるまで閉じられます (継承されます) このキーワードはすべの子クラスにプロパゲートされます。; 子クラスは DontCollapseCategories キーワードを使いこのフラグをオーバーライドします。
  • DontCollapseCategories: エディタ。基本クラスから継承された CollapseCatogories キーワードをネゲートします。
  • Within ClassName: 高度。このクラスのオブジェクトは ClassName のインスタンスなしでは存在し得ないことを表します。このクラスのオブジェクトを作成するには、ClassName のインスタンスを Outer オブジェクトと指定しなければいけません。このキーワードは、クラス宣言自身に一番最初に従わなければいけません。
  • Inherits(ClassName[,ClassName,...]): 上級です。複数の承継に使用され、付属の基底クラスを指定します。複数のベースは、コンマで区切られた 1 つの Inherits ラインを使用して、またはそれぞれの基底クラスに別々の Inherits ラインを使用して指定することができます。Native クラスにのみ有効です。2 つの UObject から派生したクラスからの複数の承継はサポートされていません。
  • Implements(ClassName[,ClassName,...]): 高度。このクラスが実装するような、より多くのインターフェース クラスの 1 つを指定します。複数のインターフェースは、コンマで区切られた 1 つの Implements ラインを使用して、またはそれぞれのインターフェース クラスに別々の Implements ラインを使用して指定することができます。Native クラスのみが Native インターフェースを実装することができます。

  • NoExport: 高度。このクラスの C++ 宣言は、スクリプトコンパイラにより自動生成された C++ ヘッダファイルに含まれるべきでないということを意味しています。 C++ クラス宣言は個別のヘッダファイルに手動で宣言されなければいけません。Native クラスのみ有効です。

変数

変数のタイプ

ビルトイン タイプ

以下に、UnrealScript のインスタンス変数宣言の例をいくつか紹介します。 :

  var int a;               // Declare an integer variable named "A".
  var byte Table[64];         // Declare a static array of 64 bytes named "Table".
  var string PlayerName;      // Declare a string variable named "PlayerName".
  var actor Other;         // Declare a variable which can be assigned a reference to an Actor instance.
  var() float MaxTargetDist;   // Declare a float variable named "MaxTargetDist" and allow its value to be modified from an UnrealEd property window.
  

変数は、UnrealScript 内において 2 種類の場所に現れることができます。オブジェクト全体に適用されるインスタンス変数は、クラス宣言の直後に現れます。ローカル変数は関数内に現れ、その関数が実行されている間のみ有効になります。インスタンス変数は、var キーワードで宣言します。ローカル変数は、 local キーワードで宣言します。次がその例です。 :

  function int Foo()
  {
     local int Count;
     Count = 1;
     return Count;
  }
  

UnrealScript でサポートされる基本的な変数の型は、次の通りです。 :

  • byte: 0 から 255 までのシングルバイト値。
  • int: 32 ビット整数値。
  • bool: ブール値 : true または =false=。
  • float: 32 ビット浮動小数点数。
  • string: 文字列。 ( Unreal Strings を参照)
  • constant: 修正できない変数。
  • enumeration: あらかじめ定義されたいくつかの name 値の 1 つを取ることができる変数。たとえば、Actor スクリプトで定義された列挙型 ELightType はダイナミックライトを記述し、 LT_NoneLT_PulseLT_Strobe などのような値を取ります。

集合データ タイプ

  • array<Type>: Type の可変長配列です。
  • struct: C の構造体に似ている UnrealScript struct では、サブ変数を含む新しい変数の型を作成することができます。たとえば、2 つの一般に使用されている struct の 1 つは vector です。vector は、X、Y、Z コンポーネントから構成されています。もう 1 つは、 rotator で、ピッチ、ヨー、ロール コンポーネントから構成されています( UnrealScriptの文字列 を参照)。

Unreal タイプ

  • Name: Unreal 内のアイテムの名前 (関数、ステート、クラスなどの名前) です。名前はインデックスとして、グローバル名前テーブルに格納されます。名前は 64 文字までの簡単な文字列に対応しています。一度作成されると不変であるという点では、名前は文字列と異なります (詳しい情報は UnrealScript (Unreal スクリプト) の文字列を参照してください)。
  • Object and Actor references: ワールド内の他のオブジェクトやアクタを参照する変数です。例えば、Pawn クラスは、ポーンがどのアクタを攻撃しようとすれば良いかということを指定するような "Enemy" アクタ参照を持っています。オブジェクトとアクタ参照は、非常に便利なツールです。というのは、他のアクタの変数や関数へのアクセスを可能にするからです。例えば、Pawn スクリプトに Enemy.Damage(123) と書くことにより、敵の Damage 関数を呼び出し、敵にダメージを与えることができます。オブジェクト参照も None と呼ばれる特別な値を含んでおり、これは C NULL ポインタに相当します。これは、"this variable doesn't refer to any object (この変数は、どのオブジェクトも参照しません)" となります。
  • Delegate: unrealscript 関数への参照を持ちます。

変数指定子

また変数には、変数をさらに記述する const のような追加の指定子を含めることができます。実際に、汎用プログラミング言語からは予想もできないような非常にたくさんの指定子があります。これは主に、多くのゲーム特定概念および環境特定概念を UnrealScriptでネイティブサポートしたいと考えた結果です。

  • config: この変数は、構成可能にできます。現在の値を ini ファイルに保存し、config 変数が作成された時にロードすることができます。
  • globalconfig: すべてのサブクラスに反映されることを除き、サブクラスの構成がこの値をオーバーライドしない限り、config と同じように機能します。

  • localized: この変数の値には、ローカライズされた値が定義されます。大部分は文字列に使用されます。この変数の詳細は ローカライゼーション リファレンス? および Unreal Strings を参照してください。
  • const: 変数の中身を定数として扱います。UnrealScript では、const 変数の値を読むことはできますが、書き込みを行うことはできません。「Const」はエンジンによって更新される変数と、アクタの Location (MoveActor 関数の呼び出しによってのみ設定できる) など、UnrealScript から安全にアップデートできないものにのみ使用されます。

  • private: 変数は非公開で、同クラスのスクリプトのみからアクセスできます。ほかのどのクラス(サブクラスを含む)もアクセスできません。
  • protected: この変数は同クラスとそのサブクラスのみからアクセスできます。ほかのどのクラスからもアクセスできません。

  • repnotify: レプリケーションを経由して、このプロパティのこの値を受信した場合、アクタに通知する必要があります。
  • deprecated: この変数は近い将来削除され、エディタ内からアクセスできなくなることを示します。Deprecated プロパティはロードされますが、保存されません。

  • instanced: オブジェクト プロパティ専用です。このクラスのインスタンスが作成されると、デフォルトでこの変数に割り当てられるオブジェクトの唯一のコピーが与えられます。クラス デフォルト プロパティで定義されるサブオブジェクトのインスタンシングに使用されます。
  • databinding: このプロパティは、データ格納システムにより操作されます。
  • editoronly: このプロパティの値は、UnrealEd またはコマンドレットを実行している場合のみ、読み込まれます。ゲーム中は、このプロパティの値は破棄されます。
  • notforconsole: このプロパティの値は、PC で実行している場合のみ、読み込まれます。コンソールでは、このプロパティの値は破棄されます。

  • editconst: エディタ。この変数は、UnrealEd 内に表示されますが、編集することはできません。editconst である変数は、暗黙の 「const」 ではありません。

  • editfixedsize: エディタ。動的配列にのみ有効です。これはユーザーがUnrealEd プロパティウィンドウで配列の長さを変更してしまうのを防ぎます。
  • editinline: エディタ。このオブジェクト参照は、UnrealEd のプロパティ インスペクタ (オブジェクト参照の配列を含むオブジェクト参照にのみ有効) 内でインライン編集できます。
  • editinlineuse: エディタ。エディタ内で、このオブジェクト参照の横に、editinline に関連づけされたビヘイビアに加えて 「Use」 (使用) ボタンが追加されます。
  • noclear: エディタ。エディタからこのオブジェクト参照を None に設定することができます。
  • interp: Editor. Matinee 内の浮動プロパティ トラックまたはベクトルプロパティ トラックによって、時間の経過とともに値が維持されることを示します。

  • input: 高度。 Unreal の入力システムで変数にアクセスできるようにします。これにより、入力 (たとえばボタン押下やジョイスティック動作) を変数に直接マップできます。「byte」と「float」型の変数にのみ関連しています。
  • transient: 高度。この変数は一時使用のための変数であり、オブジェクトの永続的なステートの一部ではないことを宣言します。Transient 変数は、ディスクに保存されません。アクタがロードされるとき、Transient 変数はゼロに初期化されます。
  • duplicatetransient: 高度。オブジェクトのバイナリ複製を (StaticDuplicateObject を介して) 作成する際に、変数の値がクラス デフォルト値にリセットされるべきであることを表します。
  • noimport: 高度。T3D テキストがインポートされると、この変数をスキップする様に指示します。つまり、オブジェクトをインポート、コピー、またはペーストする際に、この変数の値が新しいオブジェクトのインスタンスに転送されるということです。
  • native: 高度。変数が、UnrealScript ではなく、C++ コードによってロードされて保存されることを宣言します。
  • export: 高度。オブジェクトプロパティ (またはオブジェクトの配列) にのみ有効です。defaultproperties 内でこのプロパティに割り当てられたオブジェクトは、その所有クラス (エクスポートプロパティを含むクラス) からオブジェクトが作成されたときにインスタンス化される必要があることを示します。
  • noexport: 高度。native クラスにのみ有効です。この変数は、自動生成されたクラス宣言に含まれるべきではありません。

  • nontransactional: 高度。この変数値の変更は、エディタの元に戻す/やり直し履歴に含まれないことを示します。

  • pointer{type}: 高度。この変数は、type へのポインタです。(type はオプションです)。 Note the syntax is: pointer varname{type}.

  • init: 高度。このプロパティはヘッダ ファイルに、FStringNoInit や TArrayNoInit としてではなく、FString または TArray としてエクスポートされるべきです。文字列や Native クラスで宣言された動的配列にのみ適用可能です。オブジェクトが作成される際にデフォルト値は消去されるので、'Init' プロパティに、デフォルト値を与えるべきではありません。(UnrealScript (Unreal スクリプト) の文字列Native コード内の文字列を参照してください。)
  • *repretry *:高度。構造体プロパティのみ。完全に送信を失敗した場合は、このプロパティのレプリケーションをリトライしてください(例えば、ネットワーク上でシリアライズが可能でないオブジェクト参照など)。簡易参照には、これがデフォルトとなっていますが、構造体には、これは望ましくありません。理由としては、バンド幅のコストが高いためこのフラグが指定されていない限りは無効になっています。
  • out: この指定子は関数パラメータのみに有効です。詳細な情報は、 関数を参照してください。
  • coerce: この指定子は関数パラメータのみに有効です。詳細な情報は、 関数を参照してください。
  • optional: この指定子は関数パラメータのみに有効です。詳細な情報は、 関数を参照してください。

編集機能

UnrealScript では、インスタンス変数を「編集可能」にできます。これによって、ユーザーは UnrealEd で変数の値を編集することができます。このメカニズムは、UnrealEd 内の「アクタ プロパティ」ダイアログのすべてのコンテンツに関与しています。ダイアログに表示されるのは、すべて編集可能として宣言された UnrealScript 変数です。

編集可能な変数を宣言するための構文は以下の通りです。

  var() int MyInteger; // Declare an editable integer in the default
                       // category.
  
  var(MyCategory) bool MyBool; // Declare an editable integer in
                               // "MyCategory".
  

また、変数を editconst として宣言することもできます。これは、変数は UnrealEd に表示されますが編集不可能であることを意味します。これによって、エディタ内で変数が変更されることを防ぎますが、スクリプトからの変更は防げないことを覚えておいてください。真に const でありながら、エディタ内に表示される変数が必要な場合、 const editconst として宣言する必要があります。:

  // MyBool は表示されますが、UnrealEd で編集できません
  var(MyCategory) editconst bool MyBool;
  
  // MyBool は表示されますが、UnrealEd で編集できず、
  // スクリプト内で変更できません
  var(MyCategory) const editconst bool MyBool;
  
  // MyBool は表示され、UnrealEd で設定できますが、
  // スクリプト内で変更できません
  var(MyCategory) const bool MyBool;
  

配列

配列は、以下の構文を使用して宣言します。

  var int MyArray[20]; // 20 整数の配列を宣言します。
  

UnrealScript は 1 次元の配列のみサポートしますが、ユーザーは自分で行と列の計算を行うことで、多次元配列をシミュレートできます。動的配列については、以下の高度な言語機能セクションを参照してください。

構造体

UnrealScript struct は、新しい種類の struct と呼ばる超変数にたくさんの変数を詰め込む方法です。変数、配列、その他の構造体を含むことができる点で、UnrealScript struct は C 言語の構造体によく似ています。しかし、UnrealScript struct は関数を含むことができません。

struct の宣言方法は次の通りです。

  // 3D スペースのポイントまたは方向ベクトル。
  struct Vector
  {
     var float X;
     var float Y;
     var float Z;
  };
  

いったん構造体を宣言すると、その struct 型の特定の変数宣言を開始できます:

  // Vector 型の多くの変数を宣言します。
  var Vector Position;
  var Vector Destination;
  

struct のコンポーネントにアクセスするには、以下のようなコードを使用してください。

  function MyFunction()
  {
     Local Vector A, B, C;
  
  // いくつかベクトルを加えます。
     C = A + B;
  
  // ベクトルの x 成分のみを追加します。
     C.X = A.X + B.X;
  
  // ベクトル C を関数に渡します。
     SomeFunction( C );
  
  // 特定のベクトル成分を関数に渡します。
     OtherFunction( A.X, C.Z );
  }
  

他の変数でできることは何でも、struct 変数で行うことができます。つまり、struct に変数を代入し、関数に渡し、コンポーネントにアクセスできます。

Object クラスでは、Unreal 内を通して使用する、いくつかの struct が定義されています。これらの struct はスクリプトの基礎単位であるため、よく理解しておく必要があります:

  • Vector: X、Y、Z 成分を使用した、スペース内における一意の 3D 点またはベクトル
  • Plane: 3D スペースで一意の平面を定義します。平面は X、Y、Z 成分 (正規化されていることが前提) に加え、W 成分で定義されます。W 成分は、平面の法線に沿った、原点から平面への距離 (平面から原点への最短距離) を表します。
  • Rotation: 一意の直交座標系を定義する回転です。回転には、Pitch(ピッチ)、Yaw(ヨー)、Role(ロール) コンポーネントが含まれます。
  • Coords: 3D スペース内の任意の座標。
  • Color: RGB カラー値。
  • Region: レベル内で一意の凸形領域を定義します。

構造体指定子

構造体はまた、構造体のインスタンスすべてに影響する指定子を持ちます。

  • *atomic *:この構造体は常に、単体としてシリアライズされることを指しています。この構造体にあるプロパティがデフォルトと異なる場合は、構造体のすべての要素がシリアライズされます。
  • *atomicwhencooked *:クックしたパッケージデータで動作時のみに'atomic' フラグを適用します。
  • *immutable *:この構造体はバイナリシリアライゼーションを使用することを意味しています(ディスクスペースを減少し、シリアライズパフォーマンスを向上します)。パッケージバージョンをインクリメントせずこの構造体からメンバを追加・削除することは安全ではありません。
  • *immutablewhencooked *:クックしたパッケージデータで動作時のみに'immutable' フラグを適用します。
  • *strictconfig *:struct プロパティが'config/globalconfig'を持っていることを意味します。この構造体内で'config/globalconfig'とマークされたプロパティのみが.ini(このフラグなしで、構造体にあるすべてのプロパティは設定可能) から読み込まれます。

列挙

UnrealScript 内において、列挙は多くのキーワードの集まりから「1 つ」を含む変数を宣言する、便利な方法として提供されています。たとえば、アクタクラスには列挙 EPhysics が含まれています。これは、Unreal がアクタに適用するべき物理を記述しています。これを、 PHYS_NonePHYS_WalkingPHYS_Falling 、その他など、既定値の 1 つに設定することができます。

内部では、列挙はバイト変数として格納されます。UnrealScript の設計時は、列挙が必須として考えられていませんでした。しかし、アクタの物理モードが (たとえば) 3 ではなく PHYS_Swimming に設定されているほうが、コードを読むときにはるかに理解しやすくなります。

列挙を宣言するサンプルコードを次に示します。

  // 列挙 EColor を 3 つの値で宣言します。
  enum EColor
  {
     CO_Red,
     CO_Green,
     CO_Blue
  };
  
  // ここで、EColor 型の 2 つの変数を宣言します。
  var EColor ShirtColor, HatColor;
  
  // あるいは、次のように変数と
  // 列挙を一緒に宣言できます:
  var enum EFruit
  {
     FRUIT_Apple,
     FRUIT_Orange,
     FRUIT_Bannana
  } FirstFruit, SecondFruit;
  

Unreal ソースでは、列挙値を LT_SteadyPHYS_Falling 、などのように宣言しており、単に「Steady」または「Falling」といった宣言は使用していません。これは、プログラミングスタイルの問題であって、この言語の要件ではありません。

UnrealScript は、列挙が定義されたクラスとそのサブクラス内で、非限定型 enum タグ ( FRUIT_Apple など) のみを認識します。クラス階層内のどこか他で定義された列挙タグを参照する必要がある場合、「資格を与え」なければなりません。

  FRUIT_Apple         // Unreal が、この列挙型タグを見つけることができない場合...
  EFruit.FRUIT_Apple  // このように資格を与えてください。
  

列挙値は静的配列のサイズとして使用されます。また、静的配列は、戦前にある列挙の型名を使い、列挙にある項目の数に等しいサイズになるよう宣言されます。次がその例です:

  var int MyIntArray[EFruit];
  

定数

UnrealScript では、ほとんどすべてのデータ型について不変のリテラル値を指定できます。

  • 整数とバイト定数は、 123 のようなシンプルな数字で指定されます。16 進数で整数またはバイト定数を指定する必要がある場合、 0x123 のように指定してください。
  • Float 定数は、 456.789 のような十進数で指定されます。
  • String 定数は、2 重引用符で囲まなければなりません。たとえば "MyString" などです。
  • Name 定数は、一重引用符で囲まなければなりません。たとえば 'MyName' などです。
  • Vector 定数は、X、Y、Z 価値を含みます。例は =ect(1.0,2.0,4.0)=です。
  • Rotation 定数には、ピッチ、ヨー、ロール値が含まれます。その例は、 Rot(0x8000,0x4000,0) です。
  • None 定数は、「オブジェクトなし」 (または「アクタなし」と等しい) を示します。
  • Self 定数は、「このオブジェクト」 (または「このアクタ」と同等) を示します。すなわち、スクリプトが実行されているオブジェクトです。
  • 一般的なオブジェクト定数は、オブジェクト名に続いて一重引用符に囲まれたオブジェクト型によって指定されます。たとえば、 texture'Default' です。
  • EnumCount は、列挙内の要素数を示します。たとえば、 ELightType.EnumCount です。
  • ArrayCount は、静的配列内の要素数を示します。たとえば、 ArrayCount(Touching) です。

「const」キーワードを使用すると、今後名前で参照できる定数を宣言できます。例:

  const LargeNumber=123456;
  const PI=3.14159;
  const MyName="Tim";
  const Northeast=Vect(1.0,1.0,0.0);
  

定数は、クラスまたは構造体内で定義できます。

別のクラスで宣言された定数にアクセスするには、「classname.constname」構文を使用してください。例を以下に示します。:

  class'Pawn'.const.LargeNumber
  

オブジェクトとアクタ参照変数

アクタまたはオブジェクトを参照する変数を宣言することができます。その例は次の通りです。:

  var actor A; // アクタ参照。
  var pawn P; // Pawn クラスのアクタを参照。
  var texture T; // テクスチャオブジェクトの参照。
  

上記の変数「P」は、Pawn クラスのアクタへの参照です。このような変数は、Pawn のサブクラスに属するあらゆるアクタを参照できます。たとえば、P はブルート、スカージ、またはマンタを参照することができます。あらゆる種類の Pawn の参照が可能です。しかし、(Trigger は Pawn のサブクラスでないため) P は決して Trigger アクタを参照することができません。

アクタを参照する変数があれば便利な例には、Pawn が攻撃しようとしているアクタを参照する、Pawn クラス内の Enemy 変数があります。

アクタを参照する変数があるときに、そのアクタの変数にアクセスして、関数を呼び出すことができます。例:

  // ポーンを参照する2つの変数を宣言。
  var pawn P, Q;
  
  // P を使用する関数です。
  // P に関する一部の情報を表示します。
  function MyFunction()
  {
  // P の敵をQに設定します。
     P.Enemy = Q;
  
  // P に逃走アニメーションを再生するよう命じます。
     P.PlayRunning();
  }
  

アクタを参照する変数は、常に有効なアクタ (レベル内に実際に存在するあらゆるアクタ) を参照します。そうでない場合は、 None 値を持ちます。None は、C/C++ の NULL ポインタと同等です。しかし、UnrealScript では、 None 参照を使用して変数および呼び出し関数にアクセスするほうが安全です。結果は常にゼロとなります。

オブジェクトまたはアクタ参照は、別のアクタまたはオブジェクトを「指し示す」だけで、別のアクタまたはオブジェクトを「含む」わけではないことに注意してください。C 言語において、アクタ参照に相当するのは、AActor クラス内のオブジェクトへのポインタです (C 言語では、AActor* と言います)。たとえば、ワールド内にボブとフレッドという、敵同士の 2 匹のモンスターがあるとします。ボブの「Enemy」変数は、フレッドを「ポイント」し、フレッドの「Enemy」変数はボブを「ポイント」します。

C ポインタとは異なり、UnrealScript のオブジェクト参照は安全で絶対確実です。オブジェクト参照が、存在しないか、または無効なオブジェクトを参照することは不可能です(特別な場合、つまり None 値を除いて)。UnrealScript では、アクタが破棄されると、そのアクタへの参照はすべて自動的に None に設定されます。

クラス参照変数

Unreal では、アクタ、テクスチャ、そしてサウンドがオブジェクトであるのと同じく、クラスもオブジェクトです。クラスオブジェクトは「class」という名前のクラスに属しています。あるクラスに属しているアクタをスポーンできるよう、(コンパイル時にどのクラスであるかを知る必要なく) クラスオブジェクトへの参照を保存したい場合がよく出てくるでしょう。例:

  var() class C;
  var actor A;
  A = Spawn( C ); // ある任意のクラスCに属しているアクタをスポーンします。
  

ここでは、クラス C の役割と、クラスCに所属するオブジェクト O を混同しないようにしてください (クラス C の「インスタンス」と呼ばれます)。少し無理がある例えとしては、クラスはコショウ引き器で、クラスのインスタンスはコショウのようなものです。クランク (Spawn 関数を呼ぶこと) を回転させることによってコショウ (そのクラスのオブジェクト) を作成するために、コショウ引き器 (クラス) を使用することができます。しかし、コショウ引き器 (クラス) はコショウ (クラスに属しているオブジェクト) ではないのです。

クラスオブジェクトを参照する変数を宣言するとき、構文 class<metaclass> を使用して、変数によって参照可能なクラスを metaclass 型のクラス (と、その子クラス) にオプションで限定することができます。たとえば、次の宣言を参照してください。

  var class<actor> ActorClass;
  

ActorClass 変数は、「actor」クラスを拡張するクラスのみを参照できます。これは、コンパイル時の型検証の改善に役立ちます。たとえば、Spawn 関数はパラメータとしてクラスを取ります。ただし、そのクラスが Actor のサブクラスの時に、class<classlimitor> 構文を使用して、コンパイラがその要件を強制しなければ意味がありません。

動的なオブジェクトキャスティングを使用すれば、クラスを動的に型変換(キャスト)することができます。以下を参照してください。

  //SomeFunctionCall() の結果を Actor 型のクラス (または Actor のサブクラス) に型変換します
  class<actor>( SomeFunctionCall() )
  

割り当て

変数に値を代入するには、「=」を以下のように使用してください。:

i = 10; // 整数変数iに値を代入します。 s = "Hello!"; // 文字列変数sに値を代入します。 v = q; // v ベクトルの値qをvにコピーします }

UnrealScript では、関数またはその他の式に特定のデータ型 (たとえば「浮動小数点」) が必要な場合に、違うデータ型 (たとえば「int」) を指定すると、コンパイラはその値を適切な型のデータに自動変換しようと試みます。すべての数値データ型 (byte、int、float) は、何も操作をしなくても自動的に変換されます。

また、コード内で明示的に変換すると、UnrealScript は他の多くのビルトイン データ型を他の型に変換できます。この場合の構文は次のとおりです:

  function Test()
  {
     local int i;
     local string s;
     local vector v, q;
     local rotation r;
  
  s = string(i);     // 整数 i を文字列に変換し、次に s に代入します。
  s = string(v);     // ベクトル v を文字列に変換し、次にそれを s に代入します。
  v = q + vector(r); // 回転rをベクトルに変換し、qを追加します。
  }
  

以下は、UnrealScript で使用できる非自動変換の組み合わせの全リストです:

  • 文字列からバイト、整数、浮動小数点へ。 "123" などの文字列を 123 に変換しようとします。文字列が値を表さない場合、結果は 0 となります。
  • バイト、整数、浮動小数点、ベクトル、回転から文字列へ。数字をその文字表現に変換します。
  • 文字列からベクトル、回転へ。ベクトルまたは回転の文字表現を解析しようとします。
  • 文字列からブール値へ。大文字と小文字の区別をしない語 "True" または "False"TrueFalse に変換します。ゼロ以外の値を True に変換し、それ以外をすべて False にします。
  • ブール値から文字列へ。結果は "True" または "False" のいずれかとなります。
  • バイト、整数、浮動小数点、ベクトル、回転をブール値へ。ゼロ以外の値を True に、ゼロ値を False に変換します。
  • ブール値からバイト、整数、浮動小数点へ。= True= を 1 に、 False0 に変換します。
  • 名前から文字列へ。名前をテキスト表現に変換します。
  • 回転からベクトルへ。回転に従って、「前」に向かっているベクトルを返します。
  • ベクトルから回転へ。ベクトルの方向の回転ピッチとヨーを返します。ロールはゼロとなります。
  • オブジェクト (またはアクタ) を整数へ。そのオブジェクトについて一意であることが保証される整数を返します。
  • オブジェクト (またはアクタ) からブール値へ。オブジェクトが None の場合は False を、それ以外の場合は True を返します。
  • オブジェクト (またはアクタ) を文字列へ。オブジェクトのテキスト表現を返します。

クラス間でオブジェクト参照を変換する

UnrealScript では、単純なデータ型を相互に変換できる、上記の変換関数と同様、さまざまな種類のアクタおよびオブジェクト参照を相互に変換できます。たとえば、すべてのアクタには、別のアクタへの参照を意味する、「Target」という変数があります。Target が「Pawn」アクタクラスに属しているかどうかを調べる必要があるスクリプトを書いていて、その Target が Pawn アクタクラスでなければ意味がない特別な処理を行う必要があるとしましょう。たとえば、Pawn 関数の 1 つを呼び出す場合などが該当します。アクタキャスト演算子を使用することで、これが実現します。次がその例です:

  var actor Target;
  //...
  
  function TestActorConversions()
  {
     local Pawn P;
  
  // Target を Pawn に型変換し、結果をPに代入します。Target が Pawn (または Pawn のサブクラス) でない場合、P に代入される値は None になります。
     P = Pawn(Target);
     if( P != None )
     {
  // Target はポーンですので、その Enemy を Self に設定します。
        P.Enemy = Self;
     }
     else
     {
  // Target は、ポーンではありません。
     }
  }
  

アクタ変換を実行するには、クラス名に続いて、変換したいアクタ式を括弧に入れて記述します。このような変換は、変換が意味を成すかどうかによって成功または失敗します。上記の例で、Target がポーンではなく Trigger オブジェクトを参照しているとしたら、トリガはポーンに変換できないため、式 Pawn(Target) は「None」を返します。しかし、Target が Brute オブジェクトを参照していたら、変換後に正しく Brute が返されます。これは、Brute が Pawn のサブクラスであるためです。

このことから、アクタ変換には 2 つの目的があることがわかります。1 つ目は、あるアクタ参照があるクラスに属しているかどうかをチェックするために使用することです。2 つ目は、アクタ参照を、ある 1 つのクラスから、もっと限定されたクラスに変換するために使用することです。注意: この変換は、変換しているアクタにはまったく影響を及ぼしません。実際よりも詳細なアクタ参照の種類であるかのように、UnrealScript が参照を処理できるだけです。これにより、より継承元に近いクラス内で宣言されたプロパティやメソッドにアクセスできます。

もう 1 つの変換の例は、Inventory スクリプトです。各 Inventory アクタは、Owner 変数がどのアクタを参照していても、Pawn によって所有されています (Actor.Owner が Actor 型の変数であるため)。そのため、Inventory コードの共通テーマは、Owner を Pawn に型変換することです。次がその例です。

  // 破棄されるときにエンジンによって呼び出されます。
  function Destroyed()
  {
  // 所有者のインベントリから削除します。
      if( Pawn(Owner)!=None )
          Pawn(Owner).DeleteInventory( Self );
  }
  

関数

関数を宣言する

UnrealScript では、新しい関数を宣言すると共に、既存の関数 (関数をオーバーライド) の新しいバージョンを記述できます。関数は 1 つ以上のパラメータを取り (UnrealScript がサポートする変数の型)、オプションで値を返すことができます。ほとんどの関数は直接 UnrealScript で書かれていますが、C++ で記述して DLL に組み込んだ関数を UnrealScript から呼び出して宣言することもできます。Unreal テクノロジーは、関数呼び出しのあらゆる組み合わせをサポートします。C++ エンジンはスクリプト関数を呼び出すことができます。スクリプトは C++ 関数を呼び出すことができます。そして、スクリプトはスクリプトを呼び出すことができます。

以下に簡単な関数宣言を紹介します。この関数はパラメータとしてベクトルを取り、浮動小数点数を返します。

  // ベクトルのサイズを計算するための関数。
  function float VectorSize( vector V )
  {
     return sqrt( V.X * V.X + V.Y * V.Y + V.Z * V.Z );
  }
  

function という語が、常に関数宣言の前にあります。次に、オプションの戻り値の型 (この場合、 float ) 、関数名、そして括弧に入った関数パラメータが続きます。

関数が呼び出されると、中括弧内のコードが実行されます。関数内では、 ( local キーワードを使用して) ローカル変数を宣言し、あらゆる UnrealScript コードを実行することができます。オプションの return キーワードがあれば、関数はすぐに値を返します。

関数に、(配列を含む) あらゆる UnrealScript 型を渡すことができます。また、関数はどんな型も返すことができます。

デフォルトでは、関数で宣言するあらゆるローカル変数は、ゼロに初期化されます。

関数呼び出しは、再帰的である場合があります。たとえば次の関数は、数字の階乗を計算します。

  // 数字の階乗を計算する関数。
  function int Factorial( int Number )
  {
     if( Number <= 0 )
        return 1;
     else
        return Number * Factorial( Number - 1 );
  }
  

一部の UnrealScript 関数は、特定のイベントが発生すると必ずエンジンから呼び出されます。たとえば、あるアクタが別のアクタに触れられると、エンジンは触られたアクタの_Touch_ 関数を呼び出してだれが触っているかを伝えます。カスタム関数 Touch を記述することで、接触発生の結果として特別なアクションを行うことができます。

  // 何かがこのアクタに触れると呼び出されます。
  function Touch( actor Other )
  {
  Log( "触られた!" )
  Other.Message( "触ったな!" );
  }
  

上記の関数は、いくつかの注目すべき点を示しています。第 1 に、関数は Log コマンドを使用して、ログ ファイルに、メッセージを書き込みます (フォーマット規則を除けば、Basic 言語の「Print」コマンドとC言語の「printf」に相当)。第 2 に、Other アクタに所属する「Message」関数を呼び出しています。UnrealScript およびその他のオブジェクト指向言語において、別のアクタの関数呼び出しはよく行われるアクションです。これによって、アクタが相互にコミュニケートする単純な方法を提供します。

関数のオーバーライド

「関数のオーバーライド」とは、サブクラスに関数の新しいバージョンを書くことを意味します。たとえば、Demon という1種類の新しいモンスター用にスクリプトを書いているとしましょう。作成したばかりの Demon クラスは、Pawn クラスを拡張します。ここで、ポーンが初めてプレーヤを見ると、ポーンの「SeePlayer」関数が呼び出されます。それによって、ポーンがプレーヤを攻撃し始めることができます。これは素晴らしい概念です。しかし、たとえば、新しい Demon クラスで、「SeePlayer」を違った方法で処理したいとしましょう。さて、どのようにこれを行えば良いでしょうか?その答えは、関数のオーバーライドです。

関数をオーバーライドするには、親クラスから新しいクラスに関数定義をカットしてペーストするだけです。たとえば、SeePlayer を Demon クラスに追加できます。

  // Touch 関数の新しい Demon クラスバージョン。
  function SeePlayer( actor SeenPlayer )
  {
     log( "The demon saw a player" );
  // ここに新しいカスタム機能を加えます...
  }
  

関数のオーバーライドは、新しい UnrealScript を効率的に作成するための鍵です。既存のクラスを拡張する新しいクラスを作成できます。次に、違う方法で処理させたい関数をオーバーライドするだけで完了です。この処理によって、膨大な量のコードを書くことなく、新しい種類のオブジェクトを作成できます。

UnrealScript のいくつかの関数は、 final として宣言されています。 final キーワード ( function という語の直前に表示) は、「この関数は、子クラスによってオーバーライドできない」ことを宣言するものです。これは、だれもオーバーライドしようと試みないことが確実な関数で使用しなければなりません。なぜなら、それによってスクリプトコードがスピードアップするためです。たとえば、ベクタのサイズを計算する VectorSize 関数が指定されているとします。だれかがそれをオーバーライドするという理由はまったくないので、それを final として宣言してください。一方、Touch のような関数は非常に文脈依存型であるため、 final として宣言するべきではありません。

高度な関数指定子

  • Static: 静的関数はクラスのオブジェクト参照がなくても呼び出すことができる点で、C 言語のグローバル関数のように機能します。静的関数は、他の静的関数を呼び出すことができ、また、変数のデフォルト値にアクセスできます。静的関数は非静的関数を呼び出すことができず、インスタンス変数にアクセスできません (これらの関数はオブジェクトのインスタンスに対しては実行されないためです)。C++ のような言語とは異なり、静的関数は仮想で、子クラスでオーバーライドすることが可能です。これは、静的関数を可変クラス (コンパイル時には未知であるが、変数または表現式によって参照されるクラス) 内で呼び出したい場合に有効です。

  • Singular: function 宣言の直前に表示される singular キーワードは、関数が再帰的に自分を呼び出すことを防ぎます。そのルールは、次のとおりです。特定のアクタが singular 関数の途中にすでに存在する場合、以降その singular 関数へのあらゆる呼び出しはスキップされます。これは、一部の無限再帰バグを避けるために有効です。たとえば、_Bump_ 関数内のアクタを移動しようとすると、移動中にアクタが別のアクタにぶつかる可能性が大いにあります。ぶつかると、それが原因で、もう一度 Bump 関数が呼び出され、それが繰り返されます。そのような行動を避けるよう十分気をつける必要があります。しかし、そのような再帰的状況を絶対に起こさないコードを書く自信がない場合は、 singular キーワードを使用してください。

  • Native: UnrealScript 関数を native として宣言できます。native は、その関数は UnrealScript から呼び出し可能であるが、実際には (どこかほかの場所で) C++ で実装されていることを意味します。たとえば、アクタクラスには、多くのネイティブ関数定義が含まれます。次がその例です。
:

  native(266) final function bool Move( vector Delta );
  

native キーワードに続く括弧内の数字は、( AUTOREGISTER_NATIVE マクロを使用して) C++ により関数が宣言されたときの数字に対応し、演算子関数のためだけに必要です。ネイティブ関数は、UnrealScript 定義を含んでいるクラスのパッケージとまったく同じ名前がついた DLL に入っていることが予期されます。

  • NoExport: Native 関数にのみ使用されます。このネイティブの関数の C++ 関数宣言がエクスポートされてはならないことを宣言します。glue バージョンの関数の宣言のみがエクスポートされます。

  • Exec: コンソールに関数名を入力することによって、この関数を実行できることを示します。特定のクラスにのみ有効です。

  • Latent: Nativeの関数が、潜在関数であることを宣言します。つまり、ステートコードによってのみ呼び出されることができ、一定のゲーム時間が経過したあとに値を返すことができると意味です。

  • Iterator: Native関数が、 foreach コマンドでアクタリスト内をループするために使用できる反復子であることを宣言します。

  • Simulated: アクタがシミュレートされたプロキシまたは自律プロキシのいずれかである場合に、関数はクライアント側で実行できることを宣言します。Nativeと final の両方である関数は、すべて自動的にシミュレートされます。

  • Server: 関数は、ローカルクライアントで実行するのではなく、サーバで実行すために送信しなければならないことを宣言します。

  • Client: 関数をサーバーで実行するのではなく、所有者であるクライアントで実行するためにクライアントに送信しなければならないことを宣言します。また、このフラグは、その関数に simulated フラグを暗黙に設定します。

  • Reliable: レプリケートされた関数 (server または client という印がついている) は確実に送信されなければならず、接続が現在飽和ステートで送信できない場合は、関数を実行することができないことを宣言します。

  • Unreliable: 複製された関数 (server または client でマークされている) が不確かに送られるべきと宣言する。この意味は、向こう側に特別な順番で届く、または届くこと自体保証されていないということであり、利用可能なバンド幅が不十分な場合は、完全にスキップされることもあるということです。

  • Private, Protected: これらのキーワードは、それぞれの変数キーワードと同じ意味です。

  • Operator, PreOperator, PostOperator: 演算子と呼ばれる特別な種類の関数を宣言するためのキーワードです (C++ 言語の operator に等しい)。これは、UnrealScript が「+」、「-」、「==」、および「||」のようなビルトイン演算子をすべて知るための方法です。本書では演算子の仕組みを詳細に説明しませんが、演算子の概念は C++ に類似しています。そして、UnrealScript 関数またはネイティブ関数として、新しい演算子関数とキーワードを宣言できます。

  • Event: event キーワードは、UnrealScript にとって function と同じ意味です。しかし、 unreal -make -h を使用して C++ ヘッダファイルをエクスポートすると、Unreal は各「イベント」について C++ -> UnrealScript 呼び出しスタブを自動生成します。これによって、C++ コードは UnrealScript 関数に自動的に同期し、UnrealScript 関数に無効なパラメータを渡す可能性が排除されます。たとえば、次の UnrealScript コードを参照してください。
:

  event Touch( Actor Other )
  { ... }
  

EngineClasses.h 内に以下のコードに類似したものを生成します。

  void eventTouch(class AActor* Other)
  {
      FName N("Touch",FNAME_Intrinsic);
      struct {class AActor* Other; } Parms;
      Parms.Other=Other;
      ProcessEvent(N, &Parms);
  }
  

このため、C++ から UnrealScript 関数を以下のように呼び出すことができます。

  AActor *SomeActor, *OtherActor;
  SomeActor->eventTouch(OtherActor);
  

  • Const: ネイティブ宣言されている関数でのみ有効な指定子で、必ず関数宣言の_後で_追記します。この指定子は、生成されたヘッダーに関数を「const型」でエクスポートするかどうかを定義します。以下はその使用例です。:
  native function int doSomething(string myData) const;
  

制御構造

UnrealScript は、C/C++/Java のすべてのフロー制御文をサポートします。 :

繰り返し構造

For ループ

「For」ループを使用すると、一定の条件が満たされている限り、ループを続けます。例:

  // Example of "for" loop.
  function ForExample()
  {
     local int i;
     log( "Demonstrating the for loop" );
     for( i=0; i<4; i++ )
     {
        log( "The value of i is " $ i );
     }
     log( "Completed with i=" $ i);
  }
  

このループの出力は次のとおりです:

  Demonstrating the for loop
  The value of i is 0
  The value of i is 1
  The value of i is 2
  The value of i is 3
  Completed with i=4
  

for ループでは、セミコロンで区切った3つの式を指定する必要があります。最初の式は、変数を開始値に初期化するためのものです。第 2 の式は、各ループの反復が行われる前にチェックする条件です。この条件が true なら、ループが実行されます。false の場合、ループは終了します。第 3 の条件は、ループカウンタをインクリメントする式を与えます。

ほとんどの「for」ループ式は、カウンタを更新するだけですが、適切な初期化、終了、そしてインクリメント式を使用することで、リンクリスト内の移動など、さらに高度な「for」ループを使用できます。

すべてのフロー制御文では、中括弧なしで単一の文を実行することができます。その例は次のとおりです。

  for( i=0; i<4; i++ )
     log( "The value of i is " $ i );
  

または、中括弧に入れることで、複数の制御文を実行できます。その例は次のとおりです。

  for( i=0; i<4; i++ )
  {
     log( "The value of i is" );
     log( i );
  }
  

Do ループ

"Do" ループは式の終わりがtrueの時にループを続けます。Unreal は do-until 構文を使っており、これはC/Java ( do-while を使用) とは異なりますので注意してください。

  // Example of "do" loop.
  function DoExample()
  {
     local int i;
     log( "Demonstrating the do loop" );
     do
     {
        log( "The value of i is " $ i );
        i = i + 1;
     } until( i == 4 );
     log( "Completed with i=" $ i);
  }
  

このループの出力は次のとおりです:

  Demonstrating the do loop
  The value of i is 0
  The value of i is 1
  The value of i is 2
  The value of i is 3
  Completed with i=4
  
  Demonstrating the do loop
  The value of i is 0
  The value of i is 1
  The value of i is 2
  The value of i is 3
  Completed with i=4
  

While ループ

「While」ループを使用すると、一定の開始条件が True である間は、ループを継続します。

// Example of "while" loop. function WhileExample() { local int i; log( "Demonstrating the while loop" ); while( i < 4 ) {

log( "The value of i is " $ i ); i = i + 1; } log( "Completed with i=" $ i); }

このループの出力は次のとおりです:

  Demonstrating the do loop
  The value of i is 0
  The value of i is 1
  The value of i is 2
  The value of i is 3
  Completed with i=4
  

Continue

「continue」コマンドは、ループの開始点にジャンプします。そのため、continue コマンドの後にあるものは何も実行されません。そのため、特定の場合にループコードをスキップするために使用できます。

  function ContinueExample()
  {
     local int i;
     log( "Demonstrating continue" );
     for( i=0; i<4; i++ )
     {
        if( i == 2 )
           continue;
        log( "The value of i is " $ i );
     }
     log( "Completed with i=" $ i );
  }
  

このループの出力は次のとおりです:

  Demonstrating break
  The value of i is 0
  The value of i is 1
  The value of i is 3
  Completed with i=4
  

Break

「break」コマンドは、最も近くにあるループ (「For」、「Do」、「While」) を終了します。

  function BreakExample()
  {
     local int i;
     log( "Demonstrating break" );
     for( i=0; i<10; i++ )
     {
        if( i == 3 )
           break;
        log( "The value of i is " $ i );
     }
     log( "Completed with i=" $ i );
  }
  

このループの出力は次のとおりです:

  Demonstrating break
  The value of i is 0
  The value of i is 1
  The value of i is 2
  Completed with i=3
  

"break" コマンドは、条件文 ("switch") の残りをスキップするのにも使用できます。

選択構造

If-Then-Else 文

「If」、「Else If」、および「Else」を使用すると、特定の条件が満たされた場合にコードを実行します。

  // Example of simple "if".
  if( LightBrightness < 20 )
     log( "My light is dim" );
  
  // Example of "if-else".
  if( LightBrightness < 20 )
     log( "My light is dim" );
  else
     log( "My light is bright" );
  
  // Example if "if-else if-else".
  if( LightBrightness < 20 )
     log( "My light is dim" );
  else if( LightBrightness < 40 )
     log( "My light is medium" );
  else if( LightBrightness < 60 )
     log( "My light is kinda bright" );
  else
     log( "My light is very bright" );
  
  // Example if "if" with brackets.
  if( LightType == LT_Steady )
  {
     log( "Light is steady" );
  }
  else
  {
     log( "Light is not steady" );
  }
  

Case 文

「Switch」、「Case」、「Default」と「Break」を使用すると、簡単に条件リストを処理できます。

// Example of switch-case. function TestSwitch() { // Executed one of the case statements below, based on // the value in LightType. switch( LightType ) { case LT_None: log( "There is no lighting" ); break; case LT_Steady: log( "There is steady lighting" ); break; case LT_Backdrop: log( "There is backdrop lighting" ); break; default: log( "There is dynamic" ); break; } }

「switch」文は 1 つ以上の「case」文と、オプションの「default」文で構成されています。switch 文の後、一致する「case」文があればそれが実行されます。それ以外の場合、「default」文が実行されます。そうでない場合、「select」文が終わった後も実行が継続されます。

「case」ラベルに続くコードを書いた後、「break」文を使用しなければなりません。これは、「switch」文が終わった後もコードが実行されるようにするためです。「break」を使用しなければ、実行は失敗し、次の「case」ハンドラに行きます。

  // Example of switch-case.
  function TestSwitch2()
  {
     switch( LightType )
     {
        case LT_None:
           log( "There is no lighting" );
           break;
        case LT_Steady:   // will "fall though" to the LT_Backdrop case
        case LT_Backdrop:
           log( "There is lighting" );
           break;
        default:
           log( "Something else" );
           break;
     }
  }
  

Goto

「Goto」コマンドにより、現在の関数またはステート内のラベルへ行きます。

  // Example of "goto".
  function GotoExample()
  {
     log( "Starting GotoExample" );
     goto Hither;
  Yon:
     log( "At Yon" );
     goto Elsewhere;
  Hither:
     log( "At Hither" );
     goto Yon;
  Elsewhere:
     log( "At Elsewhere" );
  }
  

出力は次のようになります。

  Starting GotoExample
  At Hither
  At Yon
  At Elsewhere
  

言語機能

ビルトイン演算子と優先順位

UnrealScript は数字の合計、値の比較、変数のインクリメントなどの操作用に、多種多様な C/C++/Java-スタイルの演算子を提供します。演算子の完全なリストは、Object.u で定義されていますが、概要は次の通りです。以下は、標準演算子を優先順位で並べたものです。C 言語スタイル演算子は、すべて C 言語と同じ優先順位を持つことにご注意ください。

演算子 適用される型 意味
@ string 文字列連結。2 つの文字列間に1スペースを置きます。"string1"@"string2" = "string1 string2"
@= string 文字列連結。2 つの文字列間に1スペースを置きます。連結して代入(v3323 以降)。
$ string 文字列連結
$= string 文字列の連結、連結して代入する(v3323 以降)。
*= byte、int、float、vector、rotation 乗じて代入
/= byte、int、float、vector、rotation 割って代入
+= byte、int、float、vector 足して代入
-= byte、int、float、vector 引いて代入
|| bool 論理和
|| bool 論理積
^^ bool 排他的論理和
& int ビット単位論理積
| int ビット単位論理和
^ int ビット単位排他的論理和 (XOR)
= All 不等比較
== すべて 同等比較
< byte、int、float、string 左辺は右辺より小さい
> byte、int、float、string 左辺は右辺より大きい
<= byte、int、float、string 左辺は右辺より小さいか等しい
>= byte、int、float、string 左辺は右辺より大きいか等しい
~= float、string およその同等 (誤差 0.0001)、大文字小文字を区別して比較。
<< int、vector 左へビットシフト (int)、フォワードベクトル変換 (vector)
>> int、vector 右へビットシフト (int)、リバースベクトル変換 (vector)
>>   >>と同じ
+ byte、int、float、vector 加算
- byte、int、float、vector 減算
% float、int、byte 剰余 (割った後の余り)
* byte、int、float、vector、rotation 乗算
/ byte、int、float、vector、rotation 除算
Dot vector ベクトルの内積
Cross vector ベクトルの外積
float 指数

ClockwiseFrom
int (回転子要素) 1 つめの要素が 2 つめの要素よりも時計回り側にある場合は True を返します

上記の表は、優先順位の順に演算子を記載しています (同じ優先順位の演算子は一緒にまとめられています) 。「1*2+3*4」のような複合表現式を入力すると、UnrealScript は演算子を優先順位に従って自動的にまとめます。乗算は加算よりも高い優先順位を持つため、この表現式は「(1*2)+(3*4)」として評価されます。

"&&" (論理積) と "||" (論理和) 演算子は省かれることが可能です。式の結果が最初の式だけで確定する場合 (たとえば、&& のうち左辺が false の場合) 、2 つめの式は評価されません。

フォワードとリバース ベクトル変換に関して、リバース >> はローカル空間からワールド空間へ変換し、フォワード << は変換して元に戻します。

例えば、プレイヤーの 64 ユニット先にベクトルが面している場合は、vect(64,0,0) はローカル プレイヤー空間にあります。これをワールド空間に置きたい場合は、プレイヤーの回転を使用してワールド空間にこれを変換しなければいけないので、以下を使用して演算します。

  myWorldSpaceVect = my64Vect >> playerPawn.rotation;
  

ワールド空間ベクトルがあり、ローカル プレイヤー空間に置きたい場合は、フォワード回転を使用すると良いでしょう。例として、車のアクタのワールド空間速度をローカル空間に変換すると、HUD にプリントするために X (フォワード速度) を引き出すことができます。

さらに、UnrealScript は次の単項演算子をサポートします:

  • ! (bool) 論理否定。
  • - (int、float) 否定。
  • ~ (int) ビット単位否定。
  • ++, -- デクリメント(変数の前または後ろのいずれか)。

時々、新しい演算子がエンジンに追加されます。完全な演算子の一覧 (特に Object クラス) については、最新の UnrealScript ソースをチェックしてください。

汎用関数

オブジェクトの作成

UnrealScript で新しいオブジェクト インスタンスを作成するには、オブジェクトがアクタであるかどうかにより、2 つのうちの 1 つの関数を用います。アクタの場合、Actor.uc で宣言された Spawn 関数を使用しなければいけません。アクタ派生でないクラスに関しては、 new 演算子を使用しなければいけません。新しい演算子の構文は、他のいかなる関数とも異なります。任意のパラメータ リストに加えて、新しいオブジェクトのクラスと任意のテンプレート オブジェクトを指定しなければいけません。新しい演算子には UnrealScript 宣言がありませんが、関数シグネチャは以下の通りになります。

( object InOuter, name InName, int InFlags, class InClass, object InTemplate );

InOuter
(任意) 新しく作成されたオブジェクトに Outer としてアサインするオブジェクトです。これが指定されていないと、オブジェクトの Outer はゲームが実行されている間のみに存在する、一時パッケージと呼ばれる特別なパッケージに設定されます。
InName
(任意) 新しいオブジェクトに与える名前です。これが指定されていないと、ClassName_## (## はこのクラスのインスタンスが作成される度に増加していきます) という形でオブジェクトに固有の名前が与えられます。
InFlags
(任意であり、オブジェクト フラグが 64 ビットなので現在は壊れています。) オブジェクトを作成する際に使用するオブジェクト フラグです。有効な値は以下の通りです。
:
    • 0x0000000100000000: エディタの元に戻す/やり直しをサポートします。(RF_Transactional)
    • 0x0000000400000000: 外部ファイルにより、参照されます。(RF_Public)
    • 0x0000400000000000: ディスクに保存できません。(RF_Transient)
    • 0x0010000000000000: ゲーム クライアント上のオブジェクトを読み込みません。(RF_NotForClient)
    • 0x0020000000000000: ゲーム サーバー上のオブジェクトを読み込みません。(RF_NotForServer)
    • 0x0040000000000000: エディタ内のオブジェクトを読み込みません。(RF_NotForEdit)
    • 0x0008000000000000: 非参照でもエディットのためにオブジェクトを維持します。(RF_Standalone)
InClass
インスタンスを作成するためのクラスです。
InTemplate
新しいオブジェクトのプロパティ値を初期化するのに使用するオブジェクトです。

新しい演算子の実際の構文は以下のようになります。:

  ObjectVar = new[(InOuter, InName, InFlags)] <class'InClass'>[(InTemplate)];
  

クラス LightFunction のオブジェクトを作成してください。:

  function CreateALight()
  {
     local LightFunction NewObj;
  
     NewObj = new class'Engine.LightFunction';
  }
  

このオブジェクトをその Outer として割り当て、"NewLight" という名前の、新しい LightFunction オブジェクトを作成してください

  function CreateALight()
  {
     local LightFunction NewObj;
  
     NewObj = new(Self,'NewLight') class'Engine.LightFunction';
  }
  

新しいオブジェクトのプロパティを初期化する LightFunctionTemplate 変数の値として割り当てされたオブジェクトを使用して、一時パッケージに "NewLight" と呼ばれる、新しい LightFunction オブジェクトを作成してください。:

  var LightFunction LightFunctionTemplate;
  
  function CreateALight()
  {
     local LightFunction NewObj;
  
     NewObj = new(None,'NewLight') class'Engine.LightFunction' (LightFunctionTemplate);
  }
  
  defaultproperties
  {
     Begin Object Class=LightFunction Name=MyLightFunctionArchetype
     End Object
     LightFunctionTemplate=MyLightFunctionArchetype
  }
  

整数関数

  • int Rand( int Max ); 0 から (最大値-1) までの乱数を返します。
  • int Min( int A, int B ); 2 つの数字のうち最小値を返します。
  • int Max( int A, int B ); 2 つの数字のうち最大値を返します。
  • int Clamp( int V, int A, int B ); 最初の数字を A から B の範囲内に制限して返します。

警告:C や C++ と同等の言語と違い、Min と Max は整数で正常に動作します。浮動小数に使用される場合は警告なしで、切り捨てされます。浮動小数には、FMin や FMax を使用する必要があります。

浮動小数点関数

  • float Abs( float A ); 数字の絶対値を返します。
  • float Sin( float A ); 指定されたラジアンの正弦 (サイン) を返します。
  • float Cos( float A ); 指定されたラジアンの余弦 (コサイン) を返します。
  • float Tan( float A ); 指定されたラジアンの正接 (タンジェント) を返します。
  • float ASin( float A ); 指定されたラジアンの逆正弦を返します。
  • float ACos( float A ); 指定されたラジアンの逆余弦を返します。
  • float Atan( float A ); 指定されたラジアンの逆正接を返します。
  • float Exp( float A ); 定数「e」を A で累乗した値を返します。
  • float Loge( float A ); A の対数 (底は「e」) を返します。
  • float Sqrt( float A ); A の平方根を返します。
  • float Square( float A ); A の二乗 = A*A を返します。
  • float FRand(); 0.0 から 1.0 までの乱数を返します。
  • float FMin( float A, float B ); 2 つの数字のうち小さい方の値を返します。
  • float FMax( float A, float B ); 2 つの数字のうち大きい方の値を返します。
  • float FClamp( float A, float B ); 最初の数字を A から B の範囲内に
  • 制限して返します。
  • float Lerp( float A, float B, float Alpha ); A と B の間の線形補間を返します。
  • float Smerp( float Alpha, float A, float B ); A と B の間のアルファ平滑化された非線形補間を返します。
  • float Ceil ( float A ); 切り上げます

  • float Round ( float A ); 通常通り、四捨五入します。

文字列関数

  • int Len( coerce string S ); 文字列の長さを返します。
  • int InStr( coerce string S, coerce string t); 第 1 文字列の中から第2文字列を探します。存在すればオフセット値を返し、しなければ -1 を返します。
  • string Mid ( coerce string S, int i, optional int j ); i から始まり、文字数がjである文字列 S を返します (j が指定されない場合はすべてを返す)。
  • string Left ( coerce string S, int i ); 文字列の左端からi文字目の文字 s を返します。
  • string Right ( coerce string] S, int i ); 文字列の右端から i 文字目の文字sを返します。
  • string Caps ( coerce string S ); S を大文字に変換して返します。
  • string Locs ( coerce string S ); S を小文字に変換して返します (v3323 以降)
  • string Chr ( int i ); ASCII 表から文字を 1 つ返します
  • int Asc ( string S ); 文字の ASCII コードを返します (文字列の最初の文字のみが使用されます)
  • string Repl ( coerce string Src, coerce string Match, coerce string With, optional bool bCaseSensitive ); Match をソース内の With と置換します。(v3323 以降)
  • string Split(coerce string Text, coerce string SplitStr, optional bool bOmitSplitStr); SplitStr の最初の発生で Text を分割し、 Text の残りの部分を返します。=bOmitSplitStr= が true の場合、 SplitStr は返された文字列から省略されます。
  • array SplitString( string Source, optional string Delimiter=",", optional bool bCullEmpty ); 文字列配列内へテキストの文字列を分割するラッパー関数は単独式を使用しています。
  • JoinArray(array StringArray, out string out_Result, optional string delim = ",", optional bool bIgnoreBlanks = true); 単独の文字列を作成します。
  • ParseStringIntoArray(string BaseString, out array Pieces, string Delim, bool bCullEmpty); 区切られた文字列を文字列配列の要素に分割します。

  • A == B; 両方の文字列が同じ場合に True を返す比較 (大文字と小文字を区別) 。
  • A ~= B; 両方の文字列が同じ場合に True を返す比較 (大文字と小文字の区別をしません) 。
  • A != B; 両方の文字列が違う場合に True を返す比較 (大文字と小文字を区別) 。

詳細な情報に関しては、 UnrealScript (Unreal スクリプト) の文字列 を参照してください。

ベクトル関数

  • float VSize( vector A ); ベクトルのユークリッド値を返します (成分の二乗和の平方根)。
  • float Normal( vector A ); 指定されたベクトルの方向を向いた、サイズ 1.0 のベクトルを返します。
  • Invert ( out vector X, out vector Y, out vector Z ); 3 つの軸ベクトルによって指定された座標系を 反転します。
  • vector VRand ( ); 一様に分散されたランダムベクトルを返します。
  • vector MirrorVectorByNormal( vector Vect, vector Normal ); 指定された法線ベクトルに対してベクトルをミラーリングします。

タイマー機能

タイマー機能はアクタのサブクラスでのみ使用可能です。

複数のタイマーを作成して、それぞれ個別のタイマー時間を設定できます。各タイマーは固有のターゲット関数を持ちます。(デフォルトは Timer())

  • function SetTimer(float inRate, optional bool inbLoop, optional Name inTimerFunc);
この関数は、inRate 秒後にトリガされるタイマーをスタートします。inbLoop の値が「true」の場合、タイマーはループします。inTimerFunc は呼出す関数を定義します。呼出し関数のデフォルトは Timer() で、この値は複数タイマーの識別にも利用されます。
  • ClearTimer(optional Name inTimerFunc); 作動中のタイマーを停止します。
  • bool IsTimerActive(optional Name inTimerFunc); 任意のタイマーが作動中の場合「true」を返します。
  • float GetTimerCount(optional Name inTimerFunc); 例えば最後にタイマーが実行されてからの秒数といった、タイマーのカウンター値を返します。タイマーがアクティブでなかった場合は「-1」を返します。
  • float GetTimerRate(optional name TimerFuncName = 'Timer'); タイマーの設定時間を返します。 GetTimerRate('SomeTimer') - GetTimerCount('SomeTimer') はタイマーに残された時間を返します。

デバッグ関数

以下の関数は、コードのデバッグを行う際に役立ちます

  • LogEx( ELoggingSeverity Severity, name Category, coerce string Msg ); 所与の重大度と分類と共にメッセージを記録します。この関数を使用すると、標準 log() 関数よりも自由に管理を行うことができます。ランタイムで、ログメッセージを重大度と分類に基づいてフィルターすることができます。
  • LogFatal( name Category, coerce string Msg ); LogEx(LOG_FATAL, Category, Msg) 呼び出しの短縮形。
  • LogError( name Category, coerce string Msg );
  • function LogWarn( name Category, coerce string Msg );
  • LogInfo( name Category, coerce string Msg );
  • LogDebug( name Category, coerce string Msg );
  • LogTrace( name Category, coerce string Msg );

  • string Split(coerce string Text, coerce string SplitStr, optional bool bOmitSplitStr); SplitStr の最初の発生で Text を分割し、 Text の残りの部分を返します。=bOmitSplitStr= が true の場合、 SplitStr は返された文字列から省略されます。
  • array SplitString( string Source, optional string Delimiter=",", optional bool bCullEmpty ); 文字列配列内へテキストの文字列を分割するラッパー関数は単独式を使用しています。
  • JoinArray(array StringArray, out string out_Result, optional string delim = ",", optional bool bIgnoreBlanks = true); 単独の文字列を作成します。
  • ParseStringIntoArray(string BaseString, out array Pieces, string Delim, bool bCullEmpty); 区切られた文字列を文字列配列の要素に分割します。

  • ScriptTrace(); 現在のスクリプト呼び出しスタックをログファイルにダンプします
  • Name GetFuncName(); 現在の呼び出している関数の名前を返します
  • DumpStateStack(); 現在のステートスタックを記録します

UnrealScript preprocessor

詳細については UnrealScript 前処理プログラム ページをご覧ください。

UnrealScript ツールとユーティリティ

スクリプト プロファイラ

UnrealScript プロファイラを使用すると、どの部分のスクリプト実行にもっとも時間がかかっているかを理解するのに役に立ちます。

スクリプト デバッガ

詳細は Unreal Engine 3 デバッギング ツール ページを参照してください。

高度な言語機能

タイマー

タイマーは、イベントを起こす、または長期にわたり再発させるように、イベントをスケジュールするための仕組みとして使用されます。このように、特定の時間が過ぎた後、一度、ないしは何度も呼ばれるような Timer() 関数を持つために、自分自身をゲーム エンジンに登録するのに、アクタはタイマーを設定できます。

UnrealScript タイマーは、各アクタ内に構造体の配列として実装されます (アクタは複数の未決のタイマーを持つことができます)。構造体には、タイマーが切れるまでの時間や切れると呼び出される関数などが含まれています。

通常、ゲーム ループは 1 フレームに 1 回、各アクタをティックし、各アクタの Tick() 関数の一部は UpdateTimers() への呼び出しを含みます。この UpdateTimers() は期限切れのタイマーをチェックし、適切な UnrealScript 関数を呼び出します。

グラニュラリティはフレーム デルタ時間に限定されますが、ハードウェアやOS リソースは必要とされません。これらすべては C++ で実装されるので、何の心配もせずに何百という UnrealScript タイマーを安全にアップデートすることができます。タイマーが動作する際に (遅い) スクリプト コードを実行するので、もちろん、すべて同時やフレームごとに期限が切れるのは避けた方がいいでしょう。

ステート

ステートの概要

歴史的に見ると、ゲームが「Pong (ピンポンゲーム)」の時期を過ぎて発達して以来、ゲームプログラマーは「ステート」の概念を使用してきました。ステート (「ステートマシンプログラミング」とも呼ばれています) は、複合的なオブジェクトのビヘイビアを管理できる自然な方法です。しかし、UnrealScript 以前は、ステートは言語レベルでサポートされていませんでした。つまり、開発者がオブジェクトのステートに基づく C/C++ 「switch」文を作成する必要があったのです。そのようなコードを書いて、更新するのは骨の折れる作業でした。

UnrealScript は言語レベルでステートをサポートしています。

UnrealScript で、ワールド内の各アクタは、常に唯一のステートにあります。そのステートは、アクタが実行したいアクションを反映します。たとえば、移動ブラシには、「StandOpenTimed」や「BumpOpenTimed」のようないくつかのステートがあります。ポーンには、「Dying」 (死んでいる)、「Attacking」 (攻撃中)、「Wandering」 (歩き回る) などのいくつかのステートがあります。

UnrealScript では、特定のステートで存在する関数とコードを書くことができます。これらの関数は、アクタがその特定のステートにあるときにのみ呼び出されます。たとえば、モンスタースクリプトを書いているときに、「SeePlayer」関数を処理する方法を検討しているとしましょう。移動中にプレーヤに出会うと攻撃するように設定したいと考えているとします。すでにプレーヤを攻撃している場合は、中断することなく攻撃を続けさせたいと考えているとします。

これを行う最も容易な方法は、いくつかのステートを定義して (歩き回りと攻撃中)、各ステートについて違うバージョンの「Touch」を記述することです。UnrealScript は、この方法をサポートしています。

さらにステートについてより深く学ぶ前に、ステートにおける主な利点 2 つと、厄介な問題 1 つを理解しておく必要があります。

  • 利点: ステートは、ステートに特定の関数を記述するためのシンプルな方法を提供します。そのため、アクタの行っている行為次第で、同じ関数の処理方法をさまざまに変化させることができます。

  • 利点: 通常の UnrealScript コマンドに加え、「潜在関数」として知られている、いくつかの特殊関数を使用することで、特別な「ステートコード」を書くことができます。潜在関数は、「ゆっくり」 (すなわちブロックなしで) 実行を行う関数で、一定のゲーム時間が経過したあとに値を返します。これによって、時間ベースのプログラミングを行うことが可能になります。これは、C、C++、Java では提供されない大きな利点です。すなわち、概念化するのと同じ方法でコードを書くことができるのです。たとえば、「このドアを開ける。2 秒待つ。この音響効果を再生する。モンスターを放してプレーヤを攻撃させる」と等しいスクリプトを、単純な、リニアコードで書くことができます。時間ベースのコード実行管理における詳細部分は、UnrealEngine が処理します。

  • 問題点: 複数のステート、そして子クラスにおいて、(Touch のような) 関数をオーバーライド可能になるため、どの「Touch」関数がどの具体的状況で呼び出されるかを、正確に理解しておく必要が生じます。UnrealScript は、このプロセスを明確に定めた規則を提供しますが、この問題は、クラスとステートの複雑な階層を作成する場合に、認識しておくべきものです。

以下に、TriggerLight スクリプトから、ステートの例を引用します。

  // Trigger turns the light on.
  state() TriggerTurnsOn
  {
     function Trigger( actor Other, pawn EventInstigator )
     {
        Trigger = None;
        Direction = 1.0;
        Enable( 'Tick' );
     }
  }
  
  // Trigger turns the light off.
  state() TriggerTurnsOff
  {
     function Trigger( actor Other, pawn EventInstigator )
     {
        Trigger = None;
        Direction = -1.0;
        Enable( 'Tick' );
     }
  }
  

ここでは、2 つの異なるステート (TriggerTurnsOn と TriggerTurnsOff) を宣言しており、各ステートにおいて Trigger 関数のいろいろなバージョンを記述しています。ステートなしでこの実装をやり遂げることもできますが、ステートを使用すことで、さらにモジュール性と拡張性が高いコードができます。UnrealScript では、既存のクラスを簡単にサブクラス化し、新しいステートを加え、新しい関数を加えることができます。ステートなしでこれを実行しようとすると、結果のコードを後で拡張することがより難しくなるでしょう。

ステートは編集可能として宣言できます。つまり、ユーザーはアクタのステートを UnrealEd で設定することも、しないこともできます。編集可のステートを宣言するためには、以下を行ってください。

  state() MyState
  {
     //...
  }
  

編集不可のステートを宣言するためには、以下を行ってください。

  state MyState
  {
     //...
  }
  

また、「auto」キーワードを使用することにより、アクタの自動ステート、つまり初期ステートを指定できます。これによって、新しいアクタが最初にアクティブになると、必ずこのステートになります。

  auto state MyState
  {
     //...
  }
  

ステートラベルと潜在関数

ステートは、関数の他に、UnrealScript コードの前に 1 つ以上のラベルを持つことができます。例:

  auto state MyState
  {
  Begin:
     Log( "MyState has just begun!" );
     Sleep( 2.0 );
     Log( "MyState has finished sleeping" );
     goto('Begin');
  }
  

上記のステートコードは "MyState has just begun!" というメッセージを出力します。そして、2 秒後、 "MyState has finished sleeping" というメッセージを出力します。この例において興味深いのは、潜在関数「Sleep」の呼び出しです。この関数呼び出しはすぐに値を返さず、一定のゲーム時間が経過した後に値を返します。潜在関数は、ステートコード内からのみ呼び出すことができ、関数内からは呼び出すことはできません。潜在関数を使用すると、時間の経過を含む、複雑なイベントの連鎖をうまく管理できます。

すべてのステートコードは、ラベル定義から開始されます。上記の例において、ラベルは「Begin」と名付けられています。ラベルは、便利なエントリポイントをステートコードに提供するものです。ステートコードではあらゆるラベル名を使用できますが、「Begin」ラベルは特別です。このラベルは、そのステートのコードにおける、デフォルトの開始点です。

すべてのアクタで、次の 3 つの主要な潜在関数が利用できます。

  • Sleep( float 秒 ) はステート実行を一定時間休止した後で継続します。
  • FinishAnim() は、現在再生中のアニメーションシーケンスが終了するまで待ち、それから継続します。この関数を使用することで、アニメーション ドリブン スクリプト、つまり実行がメッシュ アニメーションによって制御されているスクリプトの記述が容易になります。たとえば、なめらかなアニメーションが AI システムの主要目標であるため、大部分の AI スクリプトはアニメーション ドリブン型 (タイムドリブンとは対照的) です。
  • FinishInterpolation() は、現在の InterpolationPoint 動作が終了するまで待ち、それから継続します。

Pawn クラスは、ワールド内の移動や短時間の動作などのアクションにとって重要な潜在関数をいくつか定義します。使用方法の説明は、別途 AI 文書を参照してください。

次の3つのネイティブ UnrealScript 関数は、ステートコードを書くときに特に役立ちます。

  • ステートコード内の「Goto('LabelName')」関数 (C/C++/Basic の goto に類似) は、指定されたラベルにてステートコードの実行を継続させます。
  • ステート内の特別 Goto('') コマンドは、ステートコードの実行を停止させます。新しいステートに行くか、現在のステート内の新しいラベルに行くまで、ステートコード実行は継続されません。
  • 「GotoState」関数によって、アクタは新しいステートへ行き、オプションとして、指定されたラベルで継続します (ラベルを指示しなければ、デフォルトは「Begin」ラベルとなります)。ステートコード内から GotoState を呼び出し、すぐに目的のステートへ行くことができます。また、アクタ内のどの関数内からも GotoState を呼び出すことができますが、すぐに効力は生じません。つまり、実行がステートコードを返すまで効果が発生しません。

以下に、今まで説明したステートコンセプトの例を示します。

  // This is the automatic state to execute.
  auto state Idle
  {
     // When touched by another actor...
     function Touch( actor Other )
     {
        log( "I was touched, so I'm going to Attacking" );
        GotoState( 'Attacking' );
        Log( "I have gone to the Attacking state" );
     }
  Begin:
     log( "I am idle..." );
     sleep( 10 );
     goto 'Begin';
  }
  
  // Attacking state.
  state Attacking
  {
  Begin:
     Log( "I am executing the attacking state code" );
     //...
  }
  

このプログラムを実行して、アクタに触りに行くと、次の出力が表示されます。

  I am idle...
  I am idle...
  I am idle...
  I was touched, so I'm going to Attacking
  I have gone to the Attacking state
  I am executing the attacking state code
  

次の GotoState の重要な側面を理解しておいてください。関数内から GotoState を呼び出すと、直ちに目的の場所へ行くわけではありません。実行がステートコードに戻ったときに目的の場所へ行きます。

ステート継承とスコープ規則

UnrealScript では、既存のクラスをサブクラス化するとき、新しいクラスは、すべての変数、関数、そしてステートをその親クラスから継承します。これは良く知られている概念です。

しかし、UnrealScript プログラミングモデルへステート抽象概念が追加されたことで、継承とスコープ規則に新たなひねりが追加されました。完全な継承規則は、以下の通りです。

  • 新しいクラスは、その親クラスからすべての変数を継承します。
  • 新しいクラスは、その親クラスの非ステート関数をすべて継承します。継承されたどの非ステート関数もオーバーライドできます。また、まったく新しい非ステート関数を追加できます。
  • 新しいクラスは、ステート内の関数とラベルを含む、親クラスのステートをすべて継承します。継承されたどのステート関数もオーバーライドできます。また、継承されたどのステートラベルでもオーバーライドすることができ、新しいステート関数を追加することも、新しいステートラベルを追加することもできます。

以下に、すべてのオーバーライド規則の例を示します。

  // Here is an example parent class.(次が親クラスの一例です。)
  class MyParentClass extends Actor;
  
  // A non-state function.
  function MyInstanceFunction()
  {
     log( "Executing MyInstanceFunction" );
  }
  
  // A state.
  state MyState
  {
     // A state function.
     function MyStateFunction()
     {
        Log( "Executing MyStateFunction" );
     }
  // The "Begin" label.
  Begin:
     Log("Beginning MyState");
  }
  
  // Here is an example child class.
  class MyChildClass extends MyParentClass;
  
  // Here I'm overriding a non-state function.
  function MyInstanceFunction()
  {
     Log( "Executing MyInstanceFunction in child class" );
  }
  
  // Here I'm redeclaring MyState so that I can override MyStateFunction.
  state MyState
  {
     // Here I'm overriding MyStateFunction.
     function MyStateFunction()
     {
        Log( "Executing MyStateFunction" );
     }
  // Here I'm overriding the "Begin" label.
  Begin:
     Log( "Beginning MyState in MyChildClass" );
  }
  

1 つ以上のステートで、そして、1 つ以上の親クラスでグローバルに実装されている関数がある場合、どの関数バージョンがどの内容で呼び出されるかを知っておく必要があります。これらの複雑な状況を解決するスコープ規則は次の通りです。

  • オブジェクトがあるステートにあり、そのステートのどこか (アクタのクラスか、ある親クラスのいずれか) に関数の実装が存在する場合、関数の最も継承元に近いステートバージョンが呼び出されます。
  • そうでない場合、関数の継承元に最も近い非ステートバージョンが呼び出されます。

高度なステートプログラミング

ステートが、親クラス内にある同じ名前のステートをオーバーライドしない場合、「extends」キーワードを任意で使用して、そのステートを現クラス内の既存ステートに拡張させることができます。これは、たとえば、共通した多くの機能を持つ、よく似たステートグループ (MeleeAttacking や RangeAttacking など) がある状況において便利です。この場合、基底の Attacking ステートを次の通り宣言することができます。

  // Base Attacking state.
  state Attacking
  {
     // Stick base functions here...
  }
  
  // Attacking up-close.
  state MeleeAttacking extends Attacking
  {
     // Stick specialized functions here...
  }
  
  // Attacking from a distance.
  state RangeAttacking extends Attacking
  {
     // Stick specialized functions here...
  }
  

ステート内で ignores 指定子を任意で使用して、そのステートにある間は、関数を無視することができます。この場合の構文は次のとおりです:

  // Declare a state.
  state Retreating
  {
     // Ignore the following messages...
     ignores Touch, UnTouch, MyFunction;
  
     // Stick functions here...
  }
  

その「state」変数 (「名前」型の変数) から、アクタが入っている特定のステートを知ることができます。

GotoState('') を使用することで、アクタが「ステートなし」になることも可能です。アクタが「ステートなし」のとき、そのグローバル (非ステート) 関数だけが呼び出されます。

アクタのステートを設定するために、GotoState コマンドを使用するときは常に、エンジンは EndState() と BeginState() という、2 つの特別な通知関数を呼び出すことができます。これは、それらの関数が既に定義されている場合です。EndState は、現在のステートにおいて新しいステートが開始される直前に、呼び出されます。BeginState は、新しいステートが始まった直後に呼び出されます。これらの関数は、ステートが必要とする可能性がある、ステートに特定の初期化やクリーンアップを行うために便利な場所を提供します。

ステートスタッキング

通常のステート変更においては、あるステートから別のステートへ入ると、既に終了した前回のステートに戻ることができません。ステートスタッキングではこれが可能です。PushState 関数を呼び出すことによって、ステートは新しいステートに変わり、その新しいステートがスタックの最上部に置かれます。現在のステートは、凍結されます。PopState を呼び出すと、前のステートがリストアされ、PushState が呼び出された時点からの実行が継続されます。

ステートは一度だけスタックに置くことができ、同じステートを2回目にスタックに置こうとすると、失敗します。PushState は、GotoState と同様に機能し、ステートのエントリポイントのためにステート名とオプションのラベルを取ります。新しいステートは PushedState イベントを受けとり、現在のステートは PausedState イベントを受けとります。PopState 呼び出しの後、現在のステートは PoppedState イベントを受けとります。そして、新しいステート (スタック内で次にあるステート) は ContinuedState を受けとります。

  state FirstState
  {
     function Myfunction()
     {
        doSomething();
        PushState('SecondState');
        // this will be executed immediately since we're inside of a function (no latent functionality)
        JustPushedSecondState();
     }
  
  Begin:
     doSomething();
     PushState('SecondState');
     // this will be executed once SecondState is popped since we're inside of a state code block (latent functionality)
     JustPoppedSecondState();
  }
  
  state SecondState
  {
     event PushState()
     {
        // we got pushed, push back
        PopState();
     }
  }
  

関数 IsInState を使用すると、スタック上に、ある特定のステートがあるかどうかを調べることができます。この関数は、ステートの名前をチェックします。そのため、親ステートのチェックには使用できません。たとえば:

  state BaseState
  {
     ...
  }
  
  state ExtendedState extends BaseState
  {
     ...
  }
  

アクティブとなっているステートが ExtendedState であれば、IsInState('BaseState') は false を返します。もちろん、IsInState('BaseState', true) を呼び出したときに、BaseState がスタック上にあれば True リターンを返します。

複製

UnrealScript, 内での複製に関する詳細な情報は、 Unreal ネットワーク アーキテクチャ ページを参照してください。

Iteration (ForEach)

UnrealScript の foreach コマンドによって、レベル内の全アクタ、または別のアクタの一定距離内にあるすべてのアクタ等、大きなグループのアクタの処理がしやすくなります。「foreach」は、「反復子」関数と呼ばれる特別な種類の関数を使用することで機能します。「反復子」の目的は、アクタリストを反復して適用することです。

以下は foreach の簡単な例です。

  // Display a list of all lights in the level.
  function Something()
  {
     local actor A;
  
     // Go through all actors in the level.
     log( "Lights:" );
     foreach AllActors( class 'Actor', A )
     {
        if( A.LightType != LT_None )
           log( A );
     }
  }
  

foreach コマンドの最初のパラメータは定数クラスです。このクラスは、どんな種類のアクタを検索するべきかを指定します。たとえば、検索対象をすべての Pawn に制限するために使用できます。

foreach コマンドの2つめのパラメータにおける最初の変数です。この変数には、 foreach ループの反復毎に1つのアクタが代入されます。

以下が、「foreach」で使用できるすべての反復子関数です。

  • AllActors ( class<actor> BaseClass, out actor Actor, optional name MatchTag )
    レベル内の全アクタを反復処理します。オプションの MatchTag を指定すると、指定されたタグに一致する「Tag」変数を持つアクタのみが含まれます。

  • DynamicActors( class<actor> BaseClass, out actor Actor )
    レベルが開始して以来スポーンされたすべてのアクタを反復処理し、レベル内に配置されたアクタを無視します。

  • ChildActors( class<actor> BaseClass, out actor Actor )
    このアクタによって所有されるすべてのアクタを反復処理します。

  • BasedActors( class<actor> BaseClass, out actor Actor )
    このアクタを基底として使用するすべてのアクタを反復処理します。

  • TouchingActors( class<actor> BaseClass, out actor Actor )
    このアクタに接触している (相互に突き抜けている) すべてのアクタを反復処理します。

  • TraceActors( class<actor> BaseClass, out actor Actor, out vector HitLoc, out vector HitNorm, vector End, optional vector Start, optional vector Extent )
    衝突エクステント Extent のボックスを使用して、Start ポイントから End ポイントまでをトレースした線に触れるすべてのアクタを反復処理します。各反復処理で、HitLoc は衝突場所に設定されます。そして、HitNorm は外方向の衝突法線に設定されます。

  • OverlappingActors( class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc, optional bool bIgnoreHidden )
    指定された場所 (指定されていない場合、このアクタの場所) からの指定半径内にあるすべてのアクタを反復処理します。

  • VisibleActors( class<actor> BaseClass, out actor Actor, optional float Radius, optional vector Loc )
    指定された場所 (指定されていない場合、このアクタの場所) から見えるすべてのアクタを反復処理します。

  • VisibleCollidingActors ( class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc, optional bool bIgnoreHidden );
    特定の半径内において衝突する (bCollideActors==true) アクタをすべて返します。この半径は、Loc (呼び出し元の場所がデフォルト) からそのアクタの位置までのトレースがワールドに衝突しない半径です。衝突ハッシュを使用するため、AllActors() よりもはるかに高速です。

  • CollidingActors ( class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc );
    一定の半径内にある衝突するアクタ (bCollideActors==true) を返します。衝突ハッシュを使用するため、合理的に小さい半径では AllActors() よりもはるかに高速です。

  • ZoneActors( class BaseClass, out actor Actor )
    反復子を呼び出している対象のオブジェクトと同じゾーン内にあるすべてのアクタのリストを反復処理します。

: 反復子関数はすべて、特定のクラス (ZoneActors() の場合は ZoneInfo、その他すべての場合は Actor) に属しています。非アクタの関数内で反復子を使用したい場合は、アクタ変数において次の構文を使用する必要があります。

  • foreach ActorVar.DynamicActors(class'Pawn', P)
  • foreach ActorVar.Region.Zone.ZoneActors(class'Pawn', P)

そのため、Interaction クラスからは、次が可能です。

  • foreach ViewportOwner.Actor.DynamicActors(class'Pawn', P)

Function Calling Specifiers

関数呼び出し指定子

複雑なプログラミング状況では、現在のスコープにはない特定の関数バージョンをしばしば呼び出す必要があります。これらのケースに対応するため、UnrealScript は次のキーワードを提供します。

  • Global: 関数の最派生 (非ステート) バージョンを呼び出します。
  • Super: 親クラス内の対応する関数バージョンを呼び出します。呼び出される関数は、内容によって、ステートまたは非ステート関数のいずれかになります。
  • Super(classname): 指定されたクラス内 (またはその上位) に所属する対応する関数バージョンを呼び出します。呼び出される関数は、内容によって、ステートまたは非ステート関数のいずれかになります。

複数の呼び出し指定子 (すなわち Super(Actor).Global.Touch) を組み合わせることは、有効ではありません。

以下が、呼び出し指定子の一例です。

  class MyClass extends Pawn;
  
  function MyExample( actor Other )
  {
     Super(Pawn).Touch( Other );
     Global.Touch( Other );
     Super.Touch( Other );
  }
  

もう 1 つの例では、アクタがゲームプレイに入ろうとしているときに BeginPlay() 関数が呼び出されます。BeginPlay() 関数はアクタクラスで実装され、実行される必要がある重要な機能の一部を含みます。新しい機能を加えるために、新しいクラスの MyClass 内で BeginPlay() をオーバーライドすることを想定してください。これを問題なく行うためには、親クラス内の BeginPlay() のバージョンを呼び出す必要があります:

  class MyClass extends Pawn;
  
  function BeginPlay()
  {
     // Call the version of BeginPlay in the parent class (important).
     Super.BeginPlay();
  
     // Now do custom BeginPlay stuff.
     //...
  }
  

変数クラスの静的関数にアクセスする

次の構文を使用することで、可変クラスの静的関数を呼び出すことができます。

  var class C;
  var class<Pawn> PC;
  
  class'SkaarjTrooper'.static.SomeFunction(); // Call a static function
                                              // in a specific class.
  
  PC.static.SomeFunction(); // Call a static function in a variable class.
  
  class<Pawn>(C).static.SomeFunction(); // Call a static function in a
                                        //casted class expression.
  

変数のデフォルト値

変数のデフォルト値にアクセスする

UnrealEd を使用すると、レベル設計者がオブジェクトのクラスの「デフォルト」変数を編集することができます。新しいアクタがクラスからスポーンするとき、その変数はそれぞれのデフォルトにすべて初期化されます。変数を、手動でデフォルト値にリセットするとよい場合が時々あります。たとえば、プレーヤがインベントリ項目を落とすと、インベントリコードはアクタの値の一部をデフォルトにリセットする必要があります。UnrealScript では、「Default.」キーワードでクラスのデフォルト変数にアクセスできます。例:

  var() float Health, Stamina;
  //...
  
  // Reset some variables to their defaults.
  function ResetToDefaults()
  {
     // Reset health, and stamina.
     Health = Default.Health;
     Stamina = Default.Stamina;
  }
  

クラス参照を通して変数のデフォルト値にアクセスする

クラス参照( class または class<classlimitor> タイプの変数)があれば、そのクラスのオブジェクトがなくても、参照先クラスのデフォルトプロパティにアクセスすることができます。この構文は、クラス型に評価するあらゆる式で機能します。

  var class C;
  var class<Pawn> PC;
  
  Health = class'Spotlight'.default.LightBrightness; // Access the default value of
                                                     // LightBrightness in the Spotlight class.
  
  Health = PC.default.Health; // Access the default value of Health in
                              // a variable class identified by PC.
  
  Health = class<Pawn>(C).default.Health; // Access the default value
                                          // of Health in a casted class
                                          // expression.
  

defaultproperties ブロックを使用してデフォルト値を指定する

UnrealEd のプロパティウィンドウを使用してアクタのプロパティのデフォルト値を設定する方法に加えて、もうひとつメンバー変数のデフォルト値を割り当てる方法があります。クラスの defaultproperties ブロック内に特別代入式を置く方法です。

  • 動的配列演算を除いて、宣言文を defaultproperties ブロックに入れることはできません
  • セミコロンを各行の終わりに配置できますが、必須ではありません
  • デフォルト値は、子クラスによって継承されます。子クラスの defaultproperties で指定された値は親クラスで指定された値をオーバーライドします。

構文
defaultproperties ブロックの構文は、標準 UnrealScript 構文とは若干異なります。

  • 単純型(整数、浮動小数点、Bool、バイト):
    • VarName=Value

  • 静的配列:
    • ArrayProp(0)=Value1
      ArrayProp(1)=Value2
      または
    • ArrayProp[0]=Value1
      ArrayProp[1]=Value2

  • 動的配列:
    • ArrayProp=(Value1,Value2,Value3)
      または
    • ArrayProp(0)=Value1
      ArrayProp(1)=Value2
      ArrayProp(2)=Value3

  • 名前
    • NameProp='Value'
      または
    • NameProp=Value

  • オブジェクト
    • ObjectProp=ObjectClass'ObjectName'

  • サブオブジェクト
    • Begin Object Class=ObjectClass Name=ObjectName
          VarName=Value
          ...
      End Object
      ObjectProperty=ObjectName

  • 構造体:
    • StructProperty=(InnerStructPropertyA=Value1,InnerStructPropertyB=Value2)
      または
    • StructProperty={(     
                      InnerStructPropertyA=Value1,     
                      InnerStructPropertyB=Value2     
                      )}
注記: struct のデフォルト値内で使用される場合、一部の型においてはこれと違う構文が必要です。
  • インライン静的配列は、適切な方法で宣言しなければなりません (括弧 () ではなく中括弧[ ]が、配列区切りに使用されていることに注目してください):
         StructProperty=(StaticArray[0]=Value,StaticArrayProp[1]=Value)
  • インライン動的配列は、以下の 1 行構文を使用して宣言する必要があります。
         StructProperty=(DynamicArray=(Value,Value))
  • インライン名前変数は、引用符に入っていなければなりません。
         StructProperty=(NameProperty="Value")

  • 動的配列演算。動的配列の内容を変更するために使用できます。動的配列は親から継承することができます。
    • Array.Empty - 配列全体をクリアします
    • Array.Add(element) - 配列の最後に要素を追加します
    • Array.Remove(element) - 配列から要素を削除します。これにより、この要素の全ての発生が削除されます。
    • Array.RemoveIndex(index) - 指定されたインデックスの要素を削除します
    • Array.Replace(elm1, elm2) - elm1 を elm2 に置換しますすべての発生が置換されます。elm1 が見つからなければ警告が作成されます。

次の例を参照してください (Actor.uc に基づいています)。

  defaultproperties
  {
     // objects
     MessageClass=class'LocalMessage'
  
      // declare an inline subobject of class SpriteComponent named "Sprite"
     Begin Object Class=SpriteComponent Name=Sprite
         // values specified here override SpriteComponent's own defaultproperties
        Sprite=Texture2D'EngineResources.S_Actor'
        HiddenGame=true
     End Object
     //todo
     Components.Add(Sprite)
  
      // declare an inline subobject of class CylinderComponent named "CollisionCylinder"
     Begin Object Class=CylinderComponent Name=CollisionCylinder
         // values specified here override CylinderComponent's own defaultproperties
        CollisionRadius=10
        CollisionHeight=10
        AlwaysLoadOnClient=True
        AlwaysLoadOnServer=True
     End Object
     //todo
     Components.Add(CollisionCylinder)
  
     CollisionComponent=CollisionCylinder
  
      // floats (leading '+' and trailing 'f' characters are ignored)
     DrawScale=00001.000000
     Mass=+00100.000000
     NetPriority=00001.f
  
      // ints
     NetUpdateFrequency=100
  
      // enumerations
     Role=ROLE_Authority
     RemoteRole=ROLE_None
  
      // structs
     DrawScale3D=(X=1,Y=1,Z=1)
  
      // bools
     bJustTeleported=true
     bMovable=true
     bHiddenEdGroup=false
     bReplicateMovement=true
  
     // names
     InitialState=None
  
     // dynamic array (in this case, a dynamic class array)
     SupportedEvents(0)=class'SeqEvent_Touch'
     SupportedEvents(1)=class'SeqEvent_UnTouch'
     SupportedEvents(2)=class'SeqEvent_Destroyed'
     SupportedEvents(3)=class'SeqEvent_TakeDamage'
  }
  

動的配列

上記で、静的配列を取り上げました。静的配列では、コンパイル時にサイズ (配列内の要素数) が設定され、変更できません。動的配列と静的配列は、以下の共通の特徴を持っています。

  • 一定のシーク時間 - 配列内にいくつ要素があっても、コードが配列の特定要素にアクセスするために費やす時間は同じです。
  • 無制限の要素型 - 整数、ベクトル、アクタ、その他など、どんなタイプの配列でも持つことができます。(例外として、ブール値は動的配列のみに有効です)
  • アクセスビヘイビア - インデックスがある要素ならどれでもアクセスできます。逆に、配列の境界外のインデックスで要素にアクセスしようとすると、「accessed none」が返されます。

動的配列は、変化するニーズに対応するために、静的配列の機能に加えて、実行時に要素数を変更できる手段を提供します。動的配列を使用するためには、2、3 のことを知っている必要があります。

1 つ目は、変数宣言です。動的配列を宣言する方法は、その他の UnrealScript 変数の宣言方法と非常に似ています (すなわち、var/local type varname)。動的配列では、array キーワードで型が指定され、次にアングルブラケットに入った配列型が指定されます。配列型にも山括弧 (class< Actor> のような) が含まれている場合、配列型の終わり山括弧と配列ラッパーの間にスペースを入れる必要があります。そうしなければ、コンパイラが 2 つの終わり山括弧を、 >> 演算子として解決してしまいます。例:
IntList という名前の整数の動的配列を宣言します: var array<int> IntList;
Players という名前の class 型の動的配列を宣言します: var array<class<PlayerController> > Players;

スクリプトが開始されると、IntList は 0 要素で開始されます。動的配列がサポートするメソッドによって、配列に要素を追加し、要素を取り出し、配列の長さを任意に増減させることができます。これらのメソッドを呼び出すための構文は (上記の IntList 例を使用して): IntList.MethodName() 以下の動的配列メソッドが利用できます。

  • Add(int Count): FArray::AddZeroed() と同一の Count により、配列の長さを拡張します。
  • Insert(int Index, int Count): Index は要素に挿入するための配列インデックスで、Count は挿入する要素の数です。挿入場所にある既存の要素は次へ送られ、新しい要素が作成されて、指定した場所に挿入されます。インデックス 3 に 5 つの要素を挿入すると、配列内のインデックス 3 以降にあるすべての要素が (インデックス値で) 次へ 5 つ分送られます。かつてインデックス 3 に位置していた要素はインデックス 8 に位置することになり、要素 4 は要素 9 になります。新しく追加された要素はすべてデフォルト値に初期化されます(struct 以外の、structdefaultproperties を含むすべての型はゼロ/null)。
  • Remove(int Index, int Count): Index は要素の削除を開始する配列インデックスで、Count は削除する要素の数です。これによって、配列内において有効なインデックス以降の、配列要素を削除することができます。削除される範囲よりも高い数値のインデックスがあれば、それらのインデックス値は変更されます。動的配列にインデックス値を保存する場合は、このことを覚えておいてください。
  • AddItem(Item): 配列の末尾に Item を追加し、配列の長さを 1 つ拡張します。
  • RemoveItem(Item): 順次探索を使用して、すべての Item を削除します。
  • InsertItem(int Index, Item): IndexItem を配列に挿入し、配列の長さを 1 つ拡張します。

  • Find(...) - 配列内で要素のインデックスを検索します。Find には 2 つのバージョンがあります。一致する要素値全体を検索する標準検索と、struct の単一プロパティの値に基づいて、一致する struct を検索する特別バージョンです。
    • Find(Value): Value は検索する値です。配列内で最初に見つかった、指定された値にマッチする、または配列内にその値が見つからない場合はそれを -1 した値にマッチする要素のインデックスを返します。_Value_ は、有効な表現式を使用して表すことができます。
  • Find(PropertyName, Value): PropertyName は、検索対象となる struct 内のプロパティ名です (「Name」型でなければなりません)。Value は検索する値です。PropertyName プロパティで指定された値に一致する struct を検索し、最初に見つかった struct のインデックス返します。値が見つからなかった場合は、-1 を返します。Value は、あらゆる有効な式で表すことができます。
  • Sort(SortDelegate) – インプレース配列のコンテンツをソートするため SortDelegate を使用します。_SortDelegate_ は次のシグニチャが一致しなければなりません。
    • delegate int ExampleSort(ArrayType A, ArrayType B) { return A < B ?-1 : 0; } // a negative return value indicates the items should be swapped(負の返し値は項目がスワップされることを意味する)

Length 変数

動的配列には、 Length という変数もあります。これは、動的配列の現在長(要素数)です。上の例の配列を用いて Length にアクセスするには、 IntList.Length と記述します。Length 変数は読み取るだけでなく、直接設定することができます。これによって、配列内の要素数を変更できます。直接 Length 変数を変更すると、配列長における変更はすべて配列の最後で起こります。たとえば IntList.Length = 5 を設定し、次に IntList.Length = 10 を設定すると、余分な5つの要素は配列の最後に追加されて、元の 5 つの要素とその値はそのままになります。長さを減少させると、要素も最後から同様に削除されます。Insert() または Length を増加させることによって要素を配列に加えると、要素はその変数型のデフォルト値に初期化される点にご注意ください (整数は 0、クラス参照は None など)。また、配列の現在長の値よりも大きい要素インデックスを設定することによって動的配列の長さを増加できることを知っておくとよいでしょう。これは、Length をより大きな値に設定した場合と同じように配列を拡張します。 OldLength = Array.length
Array.Length = OldLength + 1
Array[OldLength] = NewValue

Array[Array.Length] = NewValue

Array.AddItem(NewValue)

これらすべては同じ演算の等価形式です。

しかし、両方の配列の長さを増加したり、同時にメンバにアクセスすることができないことに留意してください。

Array[Array.length].myStructVariable = newVal

上記のようにはできません。 注意: 動的配列の Length メンバーを、決して「++」、「--」、「+=」、または「-=」でインクリメント/デクリメントしないでください。また、Length を out パラメータ (関数が値を変えることができる) として関数に渡さないでください。これらを行うと、Length が正確でなくなることからメモリリークとクラッシュが起きてしまいます。「=」演算子を通して長さを設定することによってのみ (そして、Length より大きいインデックスの要素を設定することで)、動的配列の実際の長さを適切に変更できます。

備考: array<bool> はサポートされているタイプではありません!

最後になりますが、動的配列は、レプリケートされません。動的配列へのインデックスと、動的配列に保存する値の 2 つの引数を持つ、レプリケート関数を使用することによって、これを回避することができます。しかし、その結果、要素がクライアントとサーバ上において同じtickスペースでなくなるという結果を考慮する必要があるでしょう。

イテレートする動的配列

動的配列は、単純なイテレーションを可能にするため、'foreach' 演算子をサポートするようになりました。基本的な構文は 'foreach ArrayVariable(out ArrayItem,optional out ItemIndex) {}' であり、そこでは各イテレーションはインデックスをインクリメントし、プロパティが提供されるとインデックスとアイテムを書き出します。

  function IterateThroughArray(array<string> SomeArray)
  {
      local string ArrayItem;
      local int Index;
      foreach SomeArray(ArrayItem)
      {
         `log("Array iterator test #1:"@ArrayItem);
      }
      foreach SomeArray(ArrayItem,Index)
      {
          `log("Array iterator test #2:"@ArrayItem@Index);
      }
  }
  

インターフェース クラス

インターフェースに関する詳細な情報は、 UnrealScript のインターフェース ページを参照してください。

関数デリゲート

デリゲートの使用法に関する詳細な情報は、 UnrealScript のデリゲート ページを参照してください。

Native クラス

Native クラスに関する詳細な情報は、 Native UnrealScript クラスのコンパイルNative UnrealScript クラスの作成 ページを参照してください。

メタデータ サポート

ゲーム内とエディタ内の機能は、プロパティ メタデータを介して拡張することが可能です。

メタデータ概観

任意のメタデータは、以下のようにして UnrealScript 内のプロパティにリンクすることができます。

変数:

  var float MyVar<TAG=VALUE>
  

列挙:

  enum EMyEnum
  {
     EME_ValA<TAG=VALUE>,
     EME_ValB<TAG=VALUE>,
  };
  

クラス:

以下の UnProg3 メーリング リスト スレッドを参照してください: https://udn.epicgames.com/lists/showpost.php?id=24834&list=unprog3

複数のメタデータ仕様を用いる

| 文字で分離することにより、複数のメタデータ仕様を同じプロパティに使うことができます。

例は以下の通りです。:

  var()   LinearColor    DrawColor<DisplayName=Draw Color|EditCondition=bOverrideDrawColor>;
  

利用可能なメタデータ仕様

以下が、現在サポートされているメタデータ タグとその内容です。

<ToolTip=TEXT_STRING>

エディタ プロパティ ウィンドウで、マウスが対応するプロパティの上に置かれると、 TEXT_STRING をツールチップとして表示します。

注意: /** VALUE */ コメントが、スクリプト コンパイラにより自動的に ToolTip メタデータに変換されるように、サポートは最近追加されました。

<DisplayName=TEXT_STRING>

エディタ プロパティ ウィンドウで、プロパティ名を実際の名前でなく TEXT_STRING として表示します。

例:

  Var() bool bEnableSpawning<DisplayName=Spawning Enabled>;
  

警告: エディタ コンボ ボックスで列挙をソートするのに UPropertyInputCombo を変更している場合は、列挙内での DisplayName の使用は問題を起こす原因となります。

以下の UnProg3 メーリング リスト スレッドを参照してください: https://udn.epicgames.com/lists/showpost.php?list=unprog3&id=24302

<EditCondition=ConditionalPropertyName>

これは、他の (ブーリアン) プロパティの値に基づいて、エディタ プロパティのエディットできるかどうかのステータスを有効化/無効化することができます。

例えば、MyPackage.MyClass UnrealScript クラスで以下をセットアップできます。

  /** スポーンを有効化/無効化。*/
  Var() bool bEnableSpawning;
  
  /** AI がスポーンする速度の設定。bEnableSpawning = TRUE でない限り効果がない。*/
  Var() float RespawnsPerSecond<EditCondition=bEnableSpawning>;
  

その後、bEnableSpawning が false の場合はいつでも、RespawnsPerSecond がエディタ内でグレー表示されるようになります。これで、デザイナーにとって少し分かりやすくなります。

重要: このメタデータ設定は、コントロールされる変数 (RespawnsPerSecond) がカスタム プロパティ アイテム バインディング (WxCustomPropertyItem_ConditionalItem) を使用する必要があります。

これを有効化するには、以下のようにして Editor.ini で接続する必要があります。

  [UnrealEd.CustomPropertyItemBindings]
  CustomPropertyClasses=(PropertyPathName=" MyPackage.MyClass: RespawnsPerSecond ",PropertyItemClassName="WxCustomPropertyItem_ConditionalItem")
  

これに関する詳細な情報は、https://udn.epicgames.com/lists/showpost.php?list=unprog3&id=30824 を参照してください。

<FriendlyName=TEXT_STRING>

これは内部のみでの使用で UNREALSCRIPT への使用を目的としているかどうか分かりません。EPIC がはっきりさせてくれるでしょう。

<AllowAbstract>

クラス プロパティに存在する場合、そのプロパティをエディットするためのエディタ ドロップダウン ボックスが抽象クラスを含みます。存在しない場合は、具象クラスのみを含みます。このメタデータ仕様内では、True や False などの値を指定する必要はありません。

<AutoComment=BOOLEAN_VALUE>

Kismet Sequence Action (Kismet シーケンス アクション) のプロパティに追加されると、プロパティとその時点での値が、そのアクションの上にコメントとして自動的に表示されます。これを見るには、スクリプトに新しい "Gate" シーケンス アクションを設置してください。このクラスでは、bOpen と AutoCloseCount の両方が、このメタデータ オプションを使用します。

高度な技術的問題

UnrealScript 実装

UnrealScript (Unreal スクリプト) のコンパイル プロセス から UnrealScript (Unreal スクリプト) の実行UnrealScript バイトコード 表記まで、UnrealScript がどのような仕組みになっているかに関する詳細な情報は UnrealScript 実装 ページを参照してください。

UnrealScript バイナリ互換性の問題

UnrealScript は、バイナリ互換性を壊すことなく、パッケージファイルのクラスが時間経過と共に進化できるように設計されています。ここで、バイナリ互換性とは、「従属バイナリファイルが、エラーなくロードされ、リンクされる」ことを意味します。変更されたコードが、設計された通りに機能するどうかはまた別の問題です。特に、安全に行うことができる変更の種類は次の通りです。

  • パッケージ内の .uc スクリプトファイルは、バイナリ互換性を壊すことなく再コンパイル可能です。
  • 新しいクラスをパッケージに加える。
  • 新しい関数をクラスに加える。
  • 新しいステートをクラスに加える。
  • 新しい変数をクラスに加える。
  • システムからプライベート変数を削除する。

一般に安全ではないその他の変換には、以下が含まれますが、それに制限されません。

  • 新しいメンバーを struct に加える。
  • パッケージからクラスを削除する。
  • 変数、関数パラメータ、または戻り値の型を変更する。
  • 関数内でパラメータの数字を変更する。

テクニカルノート

ガーベジコレクション Unreal のすべてのオブジェクトおよびアクタは、 Java VM と同様、ガーベジコレクタを使用して、ツリーに従ってガーベジを収集します。 Unreal ガーベジコレクタは、UObject クラスのシリアライゼーション機能を使用して、どのオブジェクトが、それぞれのアクティブオブジェクトに参照されているかを再帰的に判定します。その結果、 オブジェクトを明示的に削除する必要がなくなります。オブジェクトが参照されなくなるとガーベジコレクタが、それを見つけるからです。このアプローチには、参照されていないオブジェクトの削除が遅れるという副作用があります。しかし、削除がそれほど頻繁ではない場合、参照カウントを行うよりもはるかに効率がよい方法です。

UnrealScript はバイトコードに基づいています。 UnrealScript コードは、p コードや Java バイトコードに似た、一連のバイトコードにコンパイルされます。これにより、UnrealScript はプラットホーム中立となっています。結果として、他のプラットホーム、つまりマッキントッシュや Unix へのクライアントやサーバーの移植が簡単です。また、すべてのバージョンが、 同じスクリプトを実行することで、相互運用が容易に可能になります。

仮想マシンとしての Unreal。 Java 言語とビルトインの Java のクラス階層が、Web ページ スクリプティング用にバーチャル マシンを定義するのと同じように、Unreal Engine を 3D ゲーム用の仮想マシンと考えることができます。Unreal 仮想マシンは (プラットホーム従属のすべてのコードを別々のモジュールに分割するため) 実質上ポータブルであり、(拡張可能なクラス階層であるため) 拡張可能です。しかし、現在のところ、独立していながら互換性のある実装を、第三者が作成できる程度まで、Unreal VM を文書化する計画はありません。

UnrealScript コンパイラは 3 パスです。 C++ とは違い、UnrealScript は 3 つの異なるパスでコンパイルされます。最初のパスでは、変数、構造体、列挙、定数、ステート、関数宣言が解析され記憶されます。つまり各クラスの概要が構築されます。2 つ目のパスでは、スクリプト コードがバイトコードにコンパイルされます。これにより、循環する依存関係をもつ複雑なスクリプト階層であっても、別々のリンク フェーズを使うことなく 2 パスで完全にコンパイルおよびリンクすることができます。3 つ目のパスは、.uc ファイルの defaultproperties ブロックで指定された値を使用して、クラスのデフォルト プロパティを解析して、インポートします。

持続性アクタステート。 Unreal ではユーザーが随時ゲームを保存できるため、スクリプト実行ステートを含むすべてのアクタのステートは、すべてのアクタが可能な範囲で最も低い UnrealScript スタック レベルにある時にのみ、保存できることを覚えておいてください。この持続性要件は、潜在関数をステートコード以外から呼び出せないという制限事項の理由です。ステートコードは、できる限り低位のスタック レベルで実行されるため、簡単にシリアライズすることができます。関数コードは、あらゆるスタック レベルに存在することができます。例えば、すぐ下のスタックにC++ Native 関数を持つ可能性があります。これは明らかに、関数コードをディスクに保存して、後でリストアできる状況ではありません。

Unrealfile は、Unreal の Native バイナリファイル形式です。 Unrealfile には、インデックス、特定の Unreal パッケージ内のオブジェクトのシリアライズされたダンプが含まれます。Unrealfile は、他の Unrealfile に保存したその他のオブジェクトへの参照を含むことができるという点で、DLL に類似しています。このアプローチにより、既定の「パッケージ」の形で Unreal コンテンツをインターネット上で配布することができ、(特定のパッケージを再度ダウンロードしないため) ダウンロード時間を減らすことができます。

UnrealScript が静的変数をサポートしない理由。 C++ は静的変数 (クラス-プロセス毎) をサポートしていますが、これは C++ 言語が元々低レベル言語であることに理由があります。Java も静的変数をサポートしていますが、これはあまり考え抜かれた結果ではありません。このような変数は、シリアライゼーション、導出、そしてマルチレベルに関する範囲が曖昧であることから、UnrealScript では用いられていません。静的変数は、すべてのアクティブな Unreal レベルにおいて同じ値であることを意味する、「グローバル」なセマンティクスを持つべきでしょうか?パッケージ別であるべきでしょうか?レベル別であるべきでしょうか?そうだとすれば、どのようにシリアライズされるのでしょうか?.u ファイル内のクラスででしょうか、それとも .unr ファイル内のレベルででしょうか?基底クラス別に独自の静的変数があるのでしょうか?また、クラスの派生バージョンにはそれ自体の静的変数の値があるのでしょうか?UnrealScript では、言語機能として静的変数を定義せず、実際のオブジェクトに静的およびグローバル変数に似た変数を入れて公開するためのクラスを作成することで、これらの変数の管理をプログラマーに任せ、問題を回避します。レベル毎にアクセス可能な変数を希望する場合、それらの値を含む新しいクラスを作成して、レベル内で確実にシリアライズされるようにしてください。これにより、曖昧さがなくなります。このような目的に使用できるクラスの例については、LevelInfo と GameInfo を参照してください。

UnrealScript プログラミング戦略

ここでは、落とし穴を避けながら、UnrealScript コードを効率よく書き、UnrealScript の強みを活かす方法について、いくつかのトピックを取り上げます。

UnrealScript は C/C++ と比較して、遅い言語です。 典型的な C++ プログラムは UnrealScript よりも約 20 倍速く実行されます。すべての UnrealScript の背後にあるプログラミング哲学は、「ほとんど常に待機ステートにあるスクリプトを書くこと」です。言い換えれば、カスタマイズしたい「興味深い」イベントのみを処理するために UnrealScript を使用し、Unreal の物理コードが処理できるような機械的なタスク (たとえば基本的な運動など) には Script を使用しません。例えば、発射体のスクリプトを書く場合は通常、主要なイベントが起こると何をしたらよいのかを記述する HitWall()、Bounce()、および Touch() 関数を書きます。従って、その発射体のスクリプトの時間の 95% は、何のコードも実行せず、物理コードがイベントを通知してくれるのをただ待っています。これは、本質的に非常に効率的です。典型的な Unreal レベルでは、たとえ UnrealScript が C++ より大幅に遅いことを考えても、UnrealScript の実行時間は CPU 時間の平均 5-10% です。

FinishAnim、Sleep のような潜在関数をできるだけ利用してください。 スクリプトの実行の流れの基礎を潜在関数にすることで、UnrealScript で効率のよい、アニメーション ドリブンないしタイム ドリブンのスクリプトができます。

スクリプトをテストしている間、Unreal のログに注意を払ってください。 UnrealScript ランタイムは、致命的ではない問題を通知するような、役に立つ警告をログに生成してくれます。

無限再帰を引き起こす可能性のあるコードに気をつけてください。 たとえば、「Move」コマンドは何かをヒットすると、アクタを動かし Bump() 関数を呼び出します。そのため、Move コマンドを Bump 関数内で使用すると、永遠に再帰を行うことになる危険性があります。ご注意ください。無限再帰と無限ループの 2 つは、UnrealScript が上手く処理できないエラーステートです。

アクタをスポーンおよび破壊することは、サーバ側にとって非常に負荷の高い操作で、ネットワークゲームにおいてはさらに高い負荷となります。これは、スポーンと破壊がネットワーク帯域幅を消費するためです。 アクタは「重い」オブジェクトであるため、これを合理的に使用する必要があります。例えば、100 個のアクタをスポーンし、物理コードを使用してそれらを異なった軌道に送ることによって、パーティクル システムを作成しようとしないでください。これは非常に遅くなります。

UnrealScript のオブジェクト指向機能をできるだけ利用してください。 既存の関数およびステートをオーバーライドして新しい機能を作成することによって、変更しやすく、他の人のコードとも統合しやすいコードができます。従来の C 技法、例えばアクタ、またはステートのクラスに基づいて switch() 文を使用するなどを避けてください。このようなコードは新規クラスを追加して、いろいろ変更を加えると、結果的に壊れる傾向があります。

UnrealScript .u パッケージは .ini ファイルの EditPackages で指定された順番通りにコンパイルされます。 そのため、各パッケージは自己パッケージ内のオブジェクトと、既にコンパイルされたパッケージ内のオブジェクトのみを参照できますが、その後にコンパイルされたパッケージ内のオブジェクトは参照できません。パッケージ間で循環参照を行う必要が生じた場合、次の 2 つの解決方法があります。

    1. クラスを、最初の .u パッケージでコンパイルされる基底クラスの組と、2 つ目の .u パッケージ内でコンパイルされる子クラスの組に分解します。このとき、基底クラスが子クラスを参照していないことを確認にします。この手法は、良いプログラミング習慣であると共に、通常はうまく機能します。
      指定されたクラスCが、後でコンパイルされたクラスまたはオブジェクト O を参照する必要がある場合、クラスを次の 2 つに分けることができます。1 つ目のパッケージで変数 MyO を定義する抽象基底クラス C (ただし、そのデフォルト プロパティ内に MyO のデフォルト値を含まない) と、2 つ目のパッケージにある、MyO の適切なデフォルト値を指定する、サブクラスDです。この場合のデフォルト値は第 2 パッケージ内でのみ指定できます。
    2. 参照のせいで 2 つの .u パッケージが解決不可能に絡み合っている場合、1 つのパッケージに結合したほうが合理的です。パッケージは、コード モジュール方式の 1 単位として使用することが目的であるため、分離不可能なクラスを複数のパッケージに分けることに実質的な利益 (メモリの節約など) はありません。