UDN
Search public documentation:

UnrealScriptFoundationsJP
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

UE3 ホーム > [UnrealScriptHomeJP][UnrealScript ホーム]] > UnrealScript の基本コンセプト

UnrealScript の基本コンセプト


概要


UnrealScript 言語は、「Unreal Engine」でのみ使用されるようにカスタム設計されたスクリプト言語です。UnrealScript を使用することによって、エンジン全体を再コンパイルすることなく、「Unreal Engine」で作成されたゲームに対して、新たなゲームプレイアイテムを追加することができます。UnrealScript は、ゲームプレイプログラマーに対して、特にゲームで使用するために設計された機能を提供するとともに、イベントベースのゲームプレイの作成を格段に容易にし、実装も一層効率的にしています。

本ドキュメントで論じられるトピックスには、言語の機能 (クラスや変数、関数など) について触れている場合がありますが、機能そのものについて解説するわけではありません。本ページで扱っている内容は、言語の固有な機能に必ずしも関係するとは限らないコンセプトですが、特に初めての場合には理解しておくことが極めて重要となるコンセプトです。言語の全機能に関する詳細については、 UnrealScript のリファレンス のページを参照してください。

UnrealScript とは何か?


UnrealScript とは、(言語名であるとともに) UnrealScript の各ファイルのことでもあります。このシンプルなテキストファイルには、 .uc というファイル拡張子がつき、単一の UnrealScript クラスが定義されています。クラスとは何か、クラスはエンジンによってどのように使用されるのか、新たなクラスはどのように定義するか、といった問題に関する詳細については、 UnrealScript のクラス のページを参照してください。

UnrealScript は、ほとんどどのテキストエディタを使用しても作成することができます。ただし、UnrealScript に特化したハイライト表示機能やその他特別な機能を提供するテキストエディタもあり、これらを使用すると作業が格段と容易になります。また、nFringe や WOTGreal といった IDE によって、UnrealScript を使用する「Unreal」プロジェクトの開発に向けた、完全に統合されたソリューションが提供されています。

スクリプトの命名と位置


UnrealScript ファイルの名前は、必ず、ファイルが定義しているクラス名と完全に一致していなければなりません。ファイル名とクラス名に食い違いがあると、コンパイル中にエラーが発生します。

UnrealScript は、 Development\Src ディレクトリ内部のさまざまなフォルダに置かれます。このディレクトリには、デフォルトで多数のフォルダが含まれています。たとえば、Core 、Engine 、UDKBase 、UnrealEd などです。これらはそれぞれ異なる UnrealScript のプロジェクト (あるいは パッケージ) を表しています。これらのパッケージフォルダのそれぞれには、 Classes フォルダがあり、このフォルダにはそのパッケージに属するすべての UnrealScript が含まれています。

ゲームのために、カスタムの UnrealScript を追加してエンジンに使用させるには、1 個以上の新たなパッケージフォルダとそれに続く Classes フォルダを Development\Src ディレクトリ内に作成します。この中に、UnrealScript を配置することになります。

エンジンに使用されるカスタムの UnrealScript パッケージをセットアップする際に役立つ情報については、 カスタムの UnrealScript プロジェクト のページを参照してください。

UnrealScript の構成


MyActor.uc

/*********************
 * Class Declaration *
 *********************/


class MyActor extends Actor;


/************************************
 * Instance Variables/Structs/Enums *
 ************************************/


enum MyEnum
{
	ME_None.
	ME_Some,
	ME_All
}

struct MyStruct
{
	var int IntVal;
	var float FloatVal;
}

var int IntVar;

var float FloatVar;

var bool BoolVar;

var Actor ActorVar;

var MyEnum EnumVar;

var MyStruct StructVar;


/**********************
 * Functions & States *
 **********************/


function MyFunction()
{
	local int TempInt;

	if(ActorVar != none && BoolVar)
	{
		TempInt = IntVar;
	}
}

state NewState
{
	function MyFunction()
	{
		local float TempFloat;

		if(ActorVar != none && BoolVar)
		{
			TempFloat = FloatVar;
		}
	}
}


/**********************
 * Default Properties *
 **********************/


defaultproperties
{
	IntVar=5
	FloatVar=10.0
	BoolVar=true
}

クラスの宣言
あらゆる UnrealScript は、クラスの宣言から始まります。クラスの宣言では、当該スクリプトに関連づけられているクラスの名前、および、当該クラスが継承している他のクラスが示されるとともに、クラス指定子によって他の要素が制御されます (クラス指定子は、宣言の最後にオプションとして追加されます)。このクラス宣言は、スクリプトの先頭に置かれなければなりません。(ただし、クラスを説明するコメントや著作権を示すコメントなどは冒頭に付け加えることができます)。
インスタンス変数 / 構造体 / 列挙型変数
インスタンス変数の宣言がクラスの宣言に続きます。 これによって、クラスに含まれるプロパティが示されます。
関数 および ステート
関数とステートの宣言が、クラスの大部分を占めます。これらによって、クラスが実行できるアクションが指定されます。
Default Properties
defaultproperties (デフォルトのプロパティ) ブロックは、必ず、スクリプトの最後に置かれます。このブロックによって、クラス内で宣言されているインスタンス変数であればどれであっても、そのためのデフォルトの値を指定することができます。

スクリプトとクラス vs オブジェクトとアクタ


本質的にクラスとは、ゲーム内でエンジンによって使用されるアイテムのための設計図です。クラスは、 変数関数 を使用して、アイテムがもつことができるプロパティと動作を定義します。これらの設計図は、個別でユニークな、アイテムのインスタンスを作成します。一般的なオブジェクト指向プログラミングでは、クラスのユニークなインスタンスのことを、オブジェクトと呼びます。このことは UnrealScript においてはっきりとしています。それは、Object が階層における基本クラスとなっているからです。したがって、あらゆるクラスが Object をある時点で継承していることになり、あらゆるクラスのインスタンスは、専門的に言えば、オブジェクトであるということになります。オブジェクトは、編集およびアクセス可能な独自のプロパティをもつとともに、ワールド内の他のインスタンスに影響を与えることなく実行することができる動作およびアクションをもちます。

さて、このような次第で、クラスとは設計図であり、オブジェクトはインスタンスなのですが、果たして、実際にはどのようなことなのでしょうか? 「Unreal Tournament」でよく使用される武器である Flak Cannon (フラックキャノン) を例にとってみましょう。フラックキャノンは、弾薬をもつとともに、メインとして小さな発射物を発砲し、サブとして砲弾を発射します。これが、フラックキャノン (すなわち Flak Cannon クラス) の設計図です。(極端に単純化しましたが)。ところで、プレイヤーがワールド内でフラックキャノン上を走ると、フラックキャノンのインスタンスを受け取ります。これは実際にはどのようなことなのでしょうか? Flak Cannon クラスが設計図として使用され、新たなフラックキャノンが作られたということです。このユニークなフラックキャノンは、独自のプロパティ値をもつため、他のフラックキャノンから独立した動作を実行することができます。したがって、プレイヤーが発砲すると、その特定のフラックキャノンの弾薬数が減り、発射物が他ならぬそのフラックキャノンから発射されるのです。

以上が、オブジェクト指向プログラミングの基本的なコンセプトであり、UnrealScript のプログラミングに入って行く前にぜひとも理解しておかなければならないことです。さて、これ以上この問題は掘り下げないことにします。このテーマについて詳細に解説したものは、すでに多数そろっています。

スクリプト間のコミュニケーション


さて、UnrealScript でクラスを作成しましたが、ワールドに置かれるのはインスタンスであって、クラス自体ではありません。それならば、あるスクリプトはどのようにして他のスクリプトとコミュニケーションするのでしょうか? クラスは、それが書かれた時点で、ワールド内にどのようなインスタンスが置かれるか分かっていません。例として、上記でも取り上げたフラックキャノンをもつプレイヤーのことを考えてみましょう。どのようにして、そのプレイヤーは、他のフラックキャノンではなく、特定のフラックキャノンに発砲を命じることができるのでしょうか? 答えは、 参照 を利用するのです。UnrealScript における変数型の 1 つとして、Object 参照データ型があります。Object 参照データ型とは、Object (Actor) 型変数の気取った別名です。Object 参照変数がもつ値は、それと同じ型であるオブジェクトのインスタンスへの参照です。たとえば、Controller クラスは、現在コントロールしている Pawn を参照するために使用する変数をもっています。この変数の型は、 Pawn です。(そして、たまたまその変数名も Pawn となっています)。この変数の宣言は、次のようになります。

var Pawn Pawn;

上記のフラックキャノンの例では、現在プレイヤーが使用している武器への参照を保持することができる変数を、プレイヤーのためのクラス (この場合は Pawn) がもっているはずです。この変数が指し示すものは、プレイヤーが武器のピックアップ上を走った時に渡されたフラックキャノンのインスタンスです。この変数によって、フラックキャノンのインスタンスに直接コミュニケートできるようになるのです。

このことは非常に重要なコンセプトであって、UnrealScript で行うほぼ全てのことにおいて、中心的な考え方となります。あるオブジェクトが他のオブジェクトの値にアクセスしなければならない場合や、他のオブジェクトに対してある動作を命じなければならない場合は、必ず、そのオブジェクトは他のオブジェクトへの参照を必要とします。ここで、当然のことながら次のような疑問が生じます。すなわち、コミュニケーションの対象となる、ワールド内に置かれたインスタンスへの参照を、どのようにしたら取得できるのかという疑問です。その参照のための Object 参照変数を単に宣言するだけでは不十分です。Object 参照変数は、おおよそ、参照を保持する入れ物に過ぎません。その変数に何かを代入しなければ、その値は none のままです。つまり、その変数に値 (すなわち、参照したいインスタンス) を実際に入れなければなりません。これに関しては、残念なことに、従うべき定まったルールというものがありません。ワールド内にあるオブジェクトのインスタンスへの参照を得る方法は、多数あります。どのように参照を得るかという問題は、もっぱら個々の状況や、2 つのオブジェクト間の関係に依存します。次に、その例をいくつかあげてみます。

  • ヘルパー関数 - 特定のクラスではヘルパー関数が用意されていることがあります。これは、よく使用されるオブジェクトへの参照を返す関数です。たとえば、 GFxMoviePlayer には、 GetPC() 関数があります。これは、ムービーを所有する PlayerController への参照を返します。
  • イベント - あるアクタは、他のアクタ内でイベントをアクティベートします (例 : Touch イベント)。多くの場合、イベントをトリガーするアクタは、エンジンによって自動的に参照がイベントに渡されます。このことによって、その参照をイベント内で使用することができるようになります。あるいは、変数に参照を保存することによって、後で使用することもできます。
  • スポーン - Spawn() 関数は、生成したアクタへの参照を返します。あるアクタが他のアクタをスポーンし、そのスポーンしたアクタとすぐに (または後ほど) コミュニケーションしなければならない場合は、Spawn() 関数によって返された参照を使うことができます (ないしは、参照を変数に保存することができます)。
  • 第三者 - 参照が必要となるオブジェクトは、参照をもっている他のクラスの中ですでに参照されている場合が頻繁にあります。つまり、その参照を利用することによって当初の目的を果たすことができることになります。よく必要となり利用される例としては、現在のゲームタイプ (すなわち GameInfo クラスのインスタンス) があげられます。ゲームタイプへの直接的な参照をもっている唯一のクラスは、WorldInfo クラスです (その場合、 Game 変数が利用されます)。しかし、他の場所からゲームタイプにアクセスしなければならない場合がしばしばあります。幸いにして、あらゆる Actor は、現在の WorldInfo インスタンスへの参照をもっています (そのためには WorldInfo 変数を使います)。すなわち、WorldInfo への参照を利用することによって、GameInfo インスタンスへの参照にアクセスすることができるということです。この方法は、特に UnrealScript を始めたばかりの方には、とかく見過ごされがちなものです。すでに参照をもっているオブジェクトについてチェックを怠らなければ、探している参照が見つかるかもしれません。
  • イタレーション - イテレータ関数を利用するということは、検索を実行するようなものです。イテレータ関数は、ある基準を指定することができ、オブジェクトへの参照という形態で結果のリストを返します。これらの参照は、イテレータ内で直接利用することができます。あるいは、変数に保存して後で利用することもできます。
  • エディタ - 場合によっては、レベルデザイナーに任せて、エディタ内でアクタへの参照をセットしてもらうことができます。これに必要となるのは、参照を保持する編集可能な変数を作成することだけです。

オブジェクトの参照が得られたら、そのオブジェクトに所属する変数と関数にアクセスすることができます (ただし、protected/private に指定されていない場合)。アクセスするには、 ドット表記 という特別な構文を使用します。この構文は、オブジェクトの参照の後にドット (ピリオド) を付けて、さらにその後にアクセスが必要な変数の名前、または、実行すべき関数の名前を付けるだけです。

上記で触れたように、Controller において Pawn の参照を利用します。Controller の Pawn にある Health 変数にアクセスする場合は、次のようになります。

Pawn.Health

また、Pawn に対してメインの武器を発砲するように命じるには、 StartFire() 関数を利用して次のようにします。

Pawn.StartFire(0);

Object 参照変数は、オブジェクトへの参照を保持しますが、オブジェクトを参照するために、常に Object 参照変数が必要となるわけではありません。ヘルパー関数の結果を Object 参照変数に代入して、それを参照として使用することができる一方で、ヘルパー関数の結果を直接利用することもできます。

その実例として、上で触れた GFxMoviePlayer クラスがもつ GetPC() 関数を利用してみます。この関数は、Scaleform ムービーを所有する PlayerController を返します。たとえば、現在のプレイヤーがもつ Health にアクセスすることを考えてみます。Health は、PlayerController によってコントロールされる Pawn の中に保持されています。先に見たように、Controller にある Pawn 変数を使用することができますが、今回は、その前に Controller を参照する必要があります。 なぜなら GFxMoviePlayer クラスにいるからです。ただし、この参照を変数に保存する必要はありません。 GetPC() 関数の呼び出しとともにドット表記を利用するだけで済みます。

GetPC().Pawn.Health

次に、UI 上にボタンがあり、これを使ってプレイヤーの武器を発砲させる場合について考えてみます。これも先にやったとおりに実現することができます。

GetPC().Pawn.StartFire(0);

これが機能する理由は、 GetPC() が本質的に PlayerController インスタンスへの参照として考えることができるからです。このことは、 GFxMoviePlayer クラス内にある GetPC() の宣言から分かります。 GetPC() の返り値は、 PlayerController に当たります。

/**
 * Helper function to get the owning player controller for this movie
 *
 * @return The PlayerController corresponding to the LocalPlayerOwnerIndex that owns this movie
 */
event PlayerController GetPC()
{
	local LocalPlayer LocalPlayerOwner;

	LocalPlayerOwner = GetLP();
	if (LocalPlayerOwner == none)
	{
		return none;
	}
	return LocalPlayerOwner.Actor;
}

既存のスクリプトをどのように利用するか?


「Unreal Engine」には、大量の UnrealScript クラスが含まれています。これらのクラスは、 Development\Src ディレクトリに置かれているさまざまなパッケージにおいて、すでに実装されています。これらのクラスがどのようなもので、作成するゲームの中でどのように使用できるのか、ぜひ理解しておくべきです。既存のクラスは、主に、基本的で汎用的な機能を提供しています。これら既存のクラスは、本質的には、エンジン自体の一部となっており、「ゲーム」の一部にはなっていません。(もっとも、この区別は曖昧な場合があります)。メインシステムを構成する多くのものと、メインシステムの一部となっているクラスについては、UDN 上でさまざまな「テクニカルガイド」として解説されています。利用可能なクラスすべてを完全に理解するには、実際のスクリプト自体を検討し、コードを分析し、コメントを読むなどしながら、研究しなければならない場合もあります。その場合は、 UnCodeX が非常に役立つツールとなります。このツールは、スクリプトから、操作が簡単な Javadoc スタイルのドキュメンテーションを生成することができます。

最初に理解しておかなければならないことは、通常の状況において、既存のスクリプトを改変してはならないということです。エンジンを再コンパイルすることなしに、ネイティブクラスやネイティブパッケージ内のクラスを改変してしまうと、深刻な事態に陥る可能性があります。(通常は、ゲーム実行時にエンジンがクラッシュします)。

UnrealScript は、オブジェクト指向の言語です。したがって、各クラスは、他のクラスを継承 (UnrealScript の用語では extends (拡張)) することによって、親‐子関係を作ります。子クラスは、親クラスに存在するすべての変数、関数、ステートなどを継承します。したがって、カスタムのクラスを作成する場合は、既存のクラスを改変するのではなく、既存のクラスを拡張することによって、土台として利用するべきです。このやり方の素晴らしい点は、親クラスから子クラスに受け継がれるものから制約を受けないということです。なぜなら、必要な変数と関数を新たに追加することが可能だからです。また、親クラスから受け継がれる関数についても、親クラスの実装の仕方によって制約を受けることはありません。UnrealScript では、継承された関数をオーバーライドすることによって、望みどおりのアクションを実行させることが可能です。

場合によっては、クラスを変更せずに拡張するだけでは、限界があるように思えることもあります。子クラスすべてが継承することができるようにするために、既存の基本クラスに変数を追加するという発想は普通のことだからです。しかし、通常は、既存のクラスを改変することなく、システムを設計する方法が他にあるはずです。

結論 : 既存のクラスを拡張し、関数をオーバーライドすること。決して、既存のクラスを改変しないこと。

どのクラスを拡張すべきか


クラスを設計するには、通常、既存クラスを拡張した新たなクラス (例 : Minotaur モンスター) を作成します。既存クラス (例 : すべてのモンスターの基本クラスである Pawn クラス) には、必要となる機能のほとんどがそろっています。このやり方を取れば、「車輪の再発明」をしなくても済みます。つまり、カスタマイズしたい新たな機能を追加しながらも、カスタマイズする必要がない既存の機能はすべてそのまま利用することができるのです。「Unreal」で AI を実装する場合は、この方法が特に役立ちます。なぜなら、ビルトインされている AI システムによって提供されている極めて多数の基本機能を利用して、カスタムのクリーチャー (creature) のためのブロックを作成することができるからです。

階層をどの程度さかのぼったクラスを拡張すべきかといった問題は、きわめて状況に依存しています。たいていの場合は、機能をいくつか再実装することによって、不必要な機能を多数オーバーライドするのを避けるか否かの二者択一となります。いずれの方法が適しているかは、ご自身の判断次第です。繰り返しになりますが、最善の指針は、これから実装するクラスと類似性が最も高いクラスを選択するか、あるいは、共有する機能が最も多いクラスを選択して、それを起点として新たなクラスを実装すべきであるということです。

オブジェクトの階層


UnrealScript の使用を開始する前に、「Unreal」内にあるオブジェクトの高レベルな関係につい理解することが重要です。「Unreal」の構造は、他のほとんどのゲームと大きく異なります。「Unreal」は、純粋なオブジェクト指向です (COM/ActiveX と非常によく似ています)。つまり、明確に定義されたオブジェクトモデルをもち、高レベルなオブジェクト指向のコンセプト (オブジェクトグラフやシリアル化、オブジェクトのライフタイム、ポリモーフィズムなど) をサポートしているのです。歴史的に見るならば、ほとんどのゲームは、モノリシックに設計され、大規模な機能がハードコーディングされ、オブジェクトレベルでの拡張性がありませんでした。ただし、多くのゲーム (例 : Doom や Quake など) は、コンテンツレベルでの拡張性が高いことが知られています。「Unreal」が取る形態のオブジェクト指向には、大きな長所があります。それは、大規模な新しい機能とオブジェクト型を、ランタイム時に「Unreal」に追加できること、ならびに、この拡張がサブクラス化という形態を取ることができるということです (既存のコードを大量に改変するなどの方法を取らずに)。この拡張形態は非常に強力です。なぜなら、「Unreal」コミュニティにとって、そのすべてが相互運用される「Unreal」拡張機能を作りやすくなるためです。

Object は、「Unreal」内にあるすべてのオブジェクトの親クラスです。Object クラスにある関数すべては、どこからでもアクセスすることが可能です。あらゆるものが Object から派生しているからです。Object は、実際的なことは何もしないため、抽象的な基本クラスです。機能はすべてサブクラスによって提供されます。このようなサブクラスの例としては、Texture (テクスチャマップ)、TextBuffer (テキストのチャンク)、Class (他のオブジェクトのクラスを表わす) などがあげられます。

Actor (Object を拡張したクラス) は、「Unreal」においてスタンドアローン型ゲームのオブジェクトが継承する親クラスです。アクタが動き回ったり、他のアクタとインタラクトしたり、環境に影響を与えたり、ゲームに関連する他の有用なことを行ったりするための機能は、Actor クラスにすべてそろっています。

Pawn (Actor を拡張したクラス) は、「Unreal」における全クリーチャーおよび全プレイヤーの親クラスです。このクラスは、高度な AI 操作とプレイヤー操作ができます。

Class (Object を拡張したクラス) は、オブジェクトのクラスを表わす特殊なオブジェクトです。最初のうちは分かりづらいかもしれません。クラスはオブジェクトであり、クラスがオブジェクトを表わすわけですから。しかし、このような考え方は正常であり、この Class オブジェクトを扱うケースは多数あります。たとえば、新たなアクタを UnrealScript でスポーンさせる場合、Class オブジェクトを使ってこの新たなアクタのクラスを指定することができます。

UnrealScript を使用すると、どのような Object クラスのコードでも書くことができますが、99% Actor から派生したクラスのためのコードを書くことになります。UnrealScript の便利な機能のほとんどは、ゲーム関連のものであり、アクタを扱います。

UnrealScript のプログラミング戦略


UnrealScript コードを効率的に書き、落とし穴を回避しつつ UnrealScript の長所を活かすための有効なヒントを、以下にいくつか紹介します。

  • UnrealScript は、C/C++ に比較すると遅い言語です。 C++ による通常のプログラムは、UnrealScript によるものよりも 20 倍高速です。この独自のスクリプトを書くためのプログラミング原則は、次のことにつきます。ほとんど休止状態に置かれるスクリプトを書く。言い換えれば、カスタマイズすべき「面白い」イベントを扱う場合に限って UnrealScript を使用するということになります。基本的な動作などの機械的なタスクについては、「Unreal」の物理コードが代わりに処理してくれるため、UnrealScript は使用しません。たとえば、発射物のスクリプトを書く場合は、通常、HitWall() や、Bounce() 、Touch() といった関数を書くことによって、キーとなるイベントが発生した場合の処理方法を指定します。したがって、95% の時間は、この発射物のスクリプトコードによって何も実行されないことになります。物理コードがイベントの発生を知らせてくるのをただ待っているだけです。これは、基本的に非常に効率が良いやり方です。通常のレベルにおいて、UnrealScript は C++ よりも格段に速度が遅いのですが、UnrealScript の実行時間は、平均するとCPU 時間の 5~10% に過ぎません。
  • 潜行関数 (例 : FinishAnim および Sleep) を可能な限り利用します。 スクリプトの実行フローの基礎を潜行関数に置くことによって、アニメーション主導または時間駆動のコードを作成することができるようになるため、UnrealScript の効率がかなり向上します。
  • スクリプトをテストする際に「Unreal」ログに注意します。 UnrealScript ランタイムは、致命的ではない問題が発生すると、有益な警告をしばしば生成してくれます。
  • 無限再帰を引き起こすコードに注意します。 たとえば、Move コマンドがアクタを動かし、アクタが何かにぶつかった時に Bump() を呼び出すとします。この場合、もし Move コマンドを Bump() 関数の内部で使用するならば、無限再帰呼び出しというリスクを背負うことになります。このことは注意すべきです。UnrealScriptは、無限再帰と無限ループという 2 つのエラー状態にはうまく対処することができません。
  • アクタをスポーンおよび破棄すると、サーバーサイドに高負荷がかかります。また、ネットワークゲームでは負荷が一層高くなります。このことは、スポーンおよび破棄によってネットワーク帯域幅が占有されるために起きます。 スポーンおよび破棄は、節度を持って扱うべきです。また、アクタは「重い」オブジェクトであると見なすべきです。たとえば、100 個のユニークなアクタをスポーンし、物理コードによってさまざまな軌道を放つようなパーティクルシステムは、作成すべきではありません。非常に遅くなります。
  • UnrealScript によるオブジェクト指向の機能を最大限に活用します。 新たな機能を作成するには、既存の関数およびステートをオーバーライドします。それによって、コードが見やすくなり修正が簡単にできるようになります。また、他の人が作ったコードと容易に統合することができるようになります。C でよく使用されるテクニック (例 : アクタのクラスやステートに基づく switch() 文など) は避けるべきです。このようなコードは、新たなクラスを追加もしくはクラスを改変した場合に、機能しなくなる可能性が高いためです。
  • UnrealScript の .u パッケージは、.ini ファイルの EditPackages リストによって指定されている順番のとおりにコンパイルされます。 したがって、各パッケージが参照できるオブジェクトは、当該パッケージの中にあるか、それよりも前にコンパイルされたパッケージの中にあるものに限られます。この後にコンパイルされるパッケージのオブジェクトはけっして参照することができません。循環的な参照を行う必要が生じた場合は、次の 2 つの解決法があります。
    1. 最初の .u パッケージでコンパイルすべき基本クラスの集合と、2 番目の .u パッケージでコンパイルすべき子クラスの集合に分けることによって、基本クラスが子クラスをけっして参照しないようにします。このようなやり方は、この問題に限らず、プログラミングを行う上で役立つやり方であり、普通はうまく機能します。
      なお、クラス C が、その後にコンパイルされるパッケージに含まれるクラス (またはオブジェクト) O を参照する場合は、次の 2 つのクラスに分けることができます。1 つは、最初のパッケージにおいて変数 MyO を定義する抽象的な基本クラス C です (ただし、default properties 内にある MyO のデフォルト値は含みません)。もう 1 つは、2 番目のパッケージにおいて MyO のデフォルト値を指定するサブクラス D です (この指定は 2 番目のパッケージ内部においてのみ可能です)。
    2. これら 2 つの .u パッケージが、参照によって不可分に結びついている場合は、これらを 1 つのパッケージに統合します。 本来パッケージは、コードモジュールの 1 単位としてあるため、統合したほうが合理的です。これらの不可分なクラスの集合を複数のパッケージに分割しても、利点 (例 : メモリの節約) は実際のところありません。