Unity
Unity と C# を使用した初めてのゲーム開発
私はソフトウェア アーキテクトとして、さまざまなシステムを作成し、ネイティブ コードのマルウェアのリバース エンジニアリングを手掛けてきました。また、いつもコード側から物事を考えるようにもしてきました。しかし、ゲーム製作に関しては、どこから着手すればよいか皆目分かりません。Windows が出始めの頃、ネイティブ コード グラフィックスをプログラミングした経験が少しありますが、楽しいとは感じませんでした。その後、DirectX の開発を始めました。DirectX の開発は非常に強力ですが、行う処理に対してコード量が多すぎるように思えます。
ある日、私は Unity を試してみたところ、すばらしい処理が可能になることがわかりました。そこで、Unity の基本とアーキテクチャを全 4 回のシリーズでお届けしようと思います。今回がその第 1 回です。次回以降は、2D ゲームと 3D ゲームの作成方法を取り上げ、最終回では Windows プラットフォーム向けのビルド方法について説明する予定です。
Unity の概要
Unity とは、2D、2.5D、および 3D のゲームやアプリ シーンを設計するためのシステムを開発者に提供する 2D/3D エンジン兼フレームワークです。Unity で開発したプログラムには、ゲームだけでなく、2D/3D 空間を操作する必要のあるトレーニング シミュレーター、応急手当てアプリなどのビジネス向けのアプリも存在するため、「ゲームやアプリ」と表現しています。Unity により、開発者はコードだけでなく、ビジュアル コンポーネントを通じて 2D/3D 空間を操作することができます。また、主要モバイル プラットフォームすべてにゲームやアプリをエクスポートするなど、多くの処理を無料で行うことができます (高機能の有料 Pro バージョンもあります。ただし、無料バージョンでも非常に多くの処理が可能です)。Unity は主要 3D アプリすべてと多数のオーディオ形式をサポートします。さらに、Photoshop の .psd 形式を認識できるため、.psd ファイルを Unity プロジェクトにドロップできます。Unity により、開発者は、アセットのインポートと組み立て、オブジェクトを操作するコードの記述、高度なアニメーション システムと一緒に使用するアニメーションの作成やインポートなどが可能ですす。
図 1 からわかるように、Unity にはクロスプラットフォーム サポートがあるため、文字どおり 1 回クリックするだけでプラットフォームを変更できます。ただし、公正を期すために補足しておくと、一般的には、アプリ内購入ができるように各ストアに統合するなど、最低限の作業は必要です。
図 1 Unity でサポートされるプラットフォーム
Unity の最も強力な部分は、おそらく Unity Asset Store でしょう。これが、ゲーム市場における最高のアセット マーケットプレースであることは間違いありません。アートワーク、3D モデル、3D モデル用アニメーション ファイル (Mixamo の動作アニメーション コンテンツがストアで 10,000 以上公開されています)、効果音や音楽、プラグイン (複数プラットフォーム サポートに役立つ MultiPlatform Toolkit など)、PlayMaker や Behave などのビジュアル スクリプティング システム、高度なシェーダー、テクスチャ、パーティクル効果など、ゲーム コンポーネントとして求められるすべてのものが揃っています。Unity のインターフェイスはスクリプトに完全に対応しており、サード パーティ製のプラグインを Unity GUI に適切に統合できます。全員とは言わないまでも、大半のプロのゲーム開発者が Asset Store から多数のパッケージを利用しています。提供に適したアセットがあれば、Asset Store に公開することも可能です。
Unity に対する誤解
敵対するユーザーは常にいるので、Unity に対する誤解を説明するのはためらわれますが、Unity は既定で 2D アセットや 3D モデル (地形を除く) をデザインするためのシステムではありません。ゾンビの群れをシーンに表示して操作することはできますが、Unity の既定のツールでゾンビを作成することはできません。その意味で、Unity は、Autodesk Maya、Autodesk 3DSMax、Blender、Adobe Photoshop などとは異なり、アセット作成ツールではありません。ただし、Unity 内で 3D コンポーネントのモデルを作成できる 1 つ以上のモデル作成プラグイン (ProBuilder) がサード パーティから提供されています。2D タイル化環境の作成用には、2D Terrain Editor などの 2D 世界構築プラグインが提供されています。また、木、草原、山などのある美しい風景を作成する Terrain (地形) ツールを使用して Unity 内で地形をデザインできます。そのため、やはり Unity で可能なことは限られていると言うのはためらわれます。
マイクロソフトはどのような役割を担っているのでしょう。マイクロソフトと Unity は緊密に連携して、マイクロソフト製品にまたがる優れたプラットフォーム サポートを実現しています。Unity は Windows のスタンドアロンの実行可能ファイル、Windows Phone、Windows ストア アプリ、Xbox 360、および Xbox One をサポートします。
はじめに
最新バージョンの Unity をダウンロードし、クリック可能なスクロール ホイールを備えた 2 ボタン マウスを用意します。ダウンロードは 1 つで、無料バージョンまたは Pro バージョンのライセンスが付与されます。バージョン間の違いに関しては、japan.unity3d.com/unity/licenses を参照してください。Unity の主要インターフェイスであるエディターは、Windows (Surface Pro を含む)、Linux、および OS X で動作します。
Unity を使った実際のゲーム開発は次回取り上げることにし、まずは、Unity のインターフェイス、プロジェクト構造、およびアーキテクチャについて説明します。
アーキテクチャとコンパイル
Unity は、ネイティブ C++ ベースのゲーム エンジンです。C#、JavaScript (UnityScript)、および (頻度は少ないですが) Boo でコードを記述します。(Unity エンジン コードではない) コードは、Mono または Microsoft .NET Framework 上で JIT (Just-in-Time) コンパイルされ実行されます (JIT コードを使用できない iOS は例外で、Mono で Ahead-of-Time (AOT) コンパイルを使用してネイティブ コードにコンパイルされます)。
Unity では、エクスポートやビルドを行うことなく、IDE 内でゲームをテストできます。Unity でコードを実行する場合は、Mono バージョン 3.5 を使用します。この Mono には、.NET Framework 3.5/CLR 2.0 とほぼ同等の API 互換性があります。
Unity でコードを編集するには、プロジェクト ビューでコード ファイルをダブルクリックします。既定ではクロスプラットフォーム エディターである MonoDevelop が開きますが、好みによって、エディターとして Visual Studio を使用するように構成できます。
デバッグには、MonoDevelop か、Visual Studio 用サード パーティ製プラグインの UnityVS を使用します。ゲームをデバッグする際は、発行済みのコマンドであり、操作を実行するソフト デバッガーを使用して、Unity.exe をデバッグするのではなく、Unity 内の仮想環境をデバッグすることになるため、UnityVS がなければ、Visual Studio をデバッガーとして使用することはできません。
デバッグを行うには、Unity から MonoDevelop を起動します。MonoDevelop には、MonoDevelop 内で [Debug] (デバッグ) をクリックし、[Attach to Process] (プロセスにアタッチ) でプロセスを選択した後、Unity のデバッガーへの接続を開き、コマンドを発行するプラグインがあります。UnityVS を使用する場合、代わりに Visual Studio デバッガーを Unity に接続します。
Unity を初めて開くと、図 2 に示すプロジェクト ダイアログが表示されます。
図 2 Unity Project Wizard
プロジェクト ダイアログでは、プロジェクトの名前と場所を指定します (1)。ここではどのチェック ボックスもオンにする必要はありませんが、プロジェクトに任意のパッケージをインポートすることができます (2)。パッケージの一覧は単に利便性のために提供されています。パッケージは後からインポートすることもできます。パッケージとは .unitypackage ファイルのことです。パッケージには Unity でパッケージ化できるモデル、コード、シーン、プラグインなど、あらゆるリソースが含まれます。パッケージは簡単に再使用や配布が可能です。ただし、パッケージが何かわからない場合は、ここでは何も選択しません。その場合、プロジェクトのサイズは、(場合により非常に) 大きくなります。最後に、2D または 3D のいずれかを選択します (3)。最近まで高度な 2D ゲーム ツールがなかった Unity にとって、このドロップダウンは比較的新しいものです。3D に設定すると、既定の設定が 3D プロジェクトになります。これは長年変わらない Unity の動作なので、特筆することはありません。2D を選択した場合、Unity では見た目上細かな (しかし重要な) 点が変わります。これについては、このシリーズの今後の 2D に関するコラムで説明します。
この一覧には、システムの特定の場所にある .unitypackage ファイルの内容が設定されます。Unity ではインストール時にいくつか用意されています。Unity Asset Store からダウンロードするアセットも、.unitypackage ファイルの形式です。ダウンロードしたアセットは、ローカル システムの C:\Users\<ユーザー名>\AppData\Roaming\Unity\Asset Store にキャッシュされます。そのため、システム上に存在すればこの一覧に表示されます。任意の .unitypackage ファイルをダブルクリックするだけで、パッケージをプロジェクトにインポートできます。
Unity のインターフェイスの説明を続けましょう。図 2 のダイアログで [Create] (作成) をクリックすると、新しいプロジェクトが作成されます。Unity の既定のウィンドウ レイアウトは図 3 に示すとおりです。
図 3 既定の Unity ウィンドウ
次のものが表示されています。
- Project (プロジェクト): プロジェクトで使用するすべてのファイルです。ファイルをエクスプローラーから Unity にドラッグ アンド ドロップして、プロジェクトに追加できます。
- Scene (シーン): 現在開いているシーンです。
- Hierarchy (階層): シーンにあるすべてのゲーム オブジェクトです。"GameObject" という用語の使用と [GameObject] メニューに注意してください。
- Inspector (検査): シーンで選択したオブジェクトのコンポーネント (プロパティ) です。
- ツールバー: 左側には、パン、移動、回転、拡大/縮小のボタンがあり、中央には、再生、一時停止、コマ送りのボタンがあります。再生ボタンをクリックすると、個別にビルドを実行することなく、ほぼ即時にゲームが実行されます。一時停止をクリックするとゲームが一時停止し、コマ送りを 1 回クリックすると 1 フレーム進みます。このように綿密にデバッグを制御できます。
- Console (コンソール): このウィンドウは非表示にできますが、このウィンドウにはコンパイルの出力、エラー、警告などが表示されるほか、コードのデバッグ メッセージも表示されます。たとえば、Debug.Log の出力がここに表示されます。
重要なのは、[Scene] (シーン) タブの隣の [Game] (ゲーム) タブです。このタブは、再生ボタンをクリックするとアクティブになり、このウィンドウでゲームが実行されます。これは、再生モードと呼ばれ、ゲームをテストするためのプレイグラウンドが提供されます。また、[Scene] (シーン) タブに戻ることで、リアルタイムでゲームに変更を加えることができます。しかし、ここで細心の注意が必要です。再生ボタンが強調表示されている間は再生モードになっています。再生モードを終えると、再生モード中に加えた変更は失われます。私や、私が話したことのある Unity 開発者は皆、このように作業内容を失ったことがあります。そのため、私は [Edit] (編集)、[Preferences] (基本設定)、[Colors] (色)、[Playmode tint] (再生モードの色) の順に移動して、再生モード時にはそのことが明確なようにエディターの色を変更しています。
シーンについて
ゲーム内で実行するものはすべてシーン内に存在します。プラットフォーム用にゲームをパッケージ化する場合、最終的なゲームは 1 つ以上のシーンの集合と、プラットフォームに依存するコードを追加したものになります。プロジェクトにはいくつでもシーンを含めることができます。シーンはゲームの 1 レベルと考えることができます。ただし、プレーヤー/カメラをシーン内の異なる点に移動するだけで、1 つのシーン ファイルに複数のレベルを含めることができます。サード パーティ製パッケージやサンプル ゲームを Asset Store からダウンロードした場合には、通常、プロジェクト内で開くシーン ファイルを探します。シーン ファイルは、プロジェクト内で現在のシーンとそのプロパティに使用するリソースに関するさまざまなメタデータを含む 1 つのファイルです。他のツールの場合と同じように、開発中は Ctrl キーを押しながら S キーを押して、頻繁にシーンを保存することが重要です。
Unity では、プロジェクトを開くと時折新しい空のシーンが作成され、プロジェクト エクスプローラーで目的のシーンを探すことになる場合もありますが、通常は、最後に作業していたシーンが開きます。不慣れなユーザーにとっては非常にややこしい動作かもしれませんが、直前のプロジェクトが開いて作業がどこに行ったのかわからなくなった場合には次のことを思い出してください。落ち着いて探せば、プロジェクトに保存したシーン ファイルに作業は見つかります。プロジェクト内のすべてのシーンは、図 4 に示すアイコンをクリックして、[Scene] (シーン) でフィルターをかけて検索できます。
図 4 プロジェクト内のシーンにフィルターをかける
シーンでは、カメラがないと何も表示することができず、GameObject に Audio Listener コンポーネントをアタッチしないと何も音を出すことができません。しかし、Unity で新しいシーンを作成すると、Audio Listener コンポーネントが既に設定されているカメラが必ず作成されます。
プロジェクト構造とアセットのインポート
Unity プロジェクトは Visual Studio プロジェクトとは異なります。プロジェクト ファイルやソリューション ファイルが存在しないので、それらのファイルを開くことはありません。Unity でフォルダー構造を指定すると、そのフォルダーがプロジェクトとして開きます。プロジェクトには、Assets フォルダー、Library フォルダー、ProjectSettings フォルダー、および Temp フォルダーがありますが、インターフェイス上に表示されるのは Assets フォルダーだけです (図 4 参照)。
Assets フォルダーにはすべてのアセット (アート、コード、オーディオ) が含まれます。プロジェクトに追加した個々のファイルがここに表示されます。Assets フォルダーは常に Unity エディターの最上位フォルダーです。ただし、Unity インターフェイスで変更しただけでは、ファイル システムに影響はありません。
Library フォルダーは、インポートしたアセットのローカル キャッシュで、アセットに関するすべてのメタデータが保持されます。[Edit] (編集)、[Project Settings] (プロジェクト設定) の順にアクセスすると、ProjectSettings フォルダーに構成した設定が格納されます。Temp フォルダーは、ビルド プロセス中に、Mono と Unity からの一時ファイル用に使用されます。
ファイル システムを直接変更するのではなく、Unity インターフェイスを通じて変更することが重要であることを強調しておきます。単純なコピー アンド ペーストも例外ではありません。Unity ではエディターを通じてオブジェクトのメタデータを追跡するので、エディターを使用して変更するようにしてください (少数の例外的な場合は別とします)。ファイル システムから Unity にドラッグ アンド ドロップしても機能します。
重要な GameObject
シーンに存在するものは、事実上すべて GameObject です。.NET Framework の System.Object と考えてください。ほぼすべての型がここから派生します。同じ考え方が GameObject に当てはまります。Unity シーンに存在するすべてのオブジェクトの基本クラスが GameObject です。図 5 に示すすべてのオブジェクト (および他の多くのオブジェクト) が GameObject から派生しています。
図 5 Unity の GameObject
GameObject は、[Inspector] (検査) ウィンドウで見ると、非常にシンプルです。シーンに空の GameObject を追加したものを図 6 に示します。[Inspector] (検査) ウィンドウには空の GameObject のプロパティが表示されています。GameObjects には、オブジェクトを強調表示したときに Unity によって表示されるウィジェットを除き、既定で表示プロパティが設定されていません。この時点では、単純に空のオブジェクトです。
図 6 シンプルな GameObject
GameObject には、Name、Tag (XAML で FrameworkElement.Tag を使用して設定するテキスト タグや Windows フォームのタグに似ています)、Layer、および Transform (おそらく最も重要なプロパティです) があります。
Transform プロパティは、GameObject の単なる位置、回転、およびスケールです。Unity では、左手座標系を使用します。これは、X (水平方向)、Y (垂直方向)、および Z (奥行き。画面の奥方向または手前方向) として、コンピューター画面の座標を考える方法です。
ゲーム開発では、ベクトルを使用するのが非常に一般的です。ベクトルについては今後のコラムでもう少し説明します。今のところは、Transform.Position と Transform.Scale がどちらも Vector3 オブジェクトであることを覚えておけば十分です。Vector3 は単なる 3 次元ベクトルです。言い換えれば、X、Y、および Z の 3 つの点にすぎません。この 3 つの単純な値を使用して、オブジェクトの場所を設定したり、ベクトルの方向にオブジェクトを移動したりできます。
コンポーネント
GameObjects にコンポーネントを追加することで、機能を追加します。追加するものはすべてコンポーネントです。コンポーネントはすべて [Inspector] (検査) ウィンドウに表示されます。MeshRender コンポーネント、SpriteRender コンポーネント、オーディオ機能コンポーネント、カメラ機能コンポーネント、物理関係コンポーネント (衝突と剛体)、パーティクル システム、経路探索システム、サード パーティ カスタム コンポーネントなどがあります。オブジェクトにコードを割り当てるには、スクリプト コンポーネントを使用します。コンポーネントは、GameObject に機能を追加して命を吹き込むものです。ソフトウェア開発における Decorator パターンに似ていますが、はるかに便利です。
新しい GameObject に何らかのコードを割り当てます。今回は、[GameObject]、[Create Other] (その他を作成)、[Cube] (キューブ) の順にアクセスして作成できる単純な立方体です。立方体の名前を「Enemy」に変更した後、もう 1 つ立方体を作成して立方体を 2 つにしました。図 7 に示すように、1 つの立方体をもう 1 つの立方体から約 -15 単位離します。この操作をするには、オブジェクトを強調表示してからツール バーの移動ツールか W キーを使用します。
>図 7 立方体を 2 つ設定した現在のプロジェクト
コードは、プレーヤーを見つけて所有者をそれに向かって移動させる単純なクラスです。一般的に、移動操作は次の 2 つの方法のいずれかで行います。1 つはオブジェクトの Transform.Position プロパティを変更してオブジェクトをフレームごとに新しい位置に移動させる方法で、もう 1 つはオブジェクトに物理力を適用して後の処理を Unity に任せる方法です。
フレームごとの処理は、「この位置に移動する」と言うのと同じと考えるので、若干の方法の違いがあります。今回の例では、フレームごとにオブジェクトを少しずつ移動させるので、オブジェクトの移動先を厳密に制御する必要があります。フレームごとに調整しない場合、1 つの関数を呼び出して移動を行うライブラリがあります (無料で使用できる iTween ライブラリなど)。
最初に行うのは、[Project] (プロジェクト) ウィンドウを右クリックして、「EnemyAI」という名前の新しい C# スクリプトを作成することです。このスクリプトをオブジェクトに割り当てるには、スクリプト ファイルをプロジェクト ビューから [Scene] (シーン) ビューまたは [Hierarchy] (階層) のオブジェクトにドラッグします。これで、コードがオブジェクトに割り当てられます。後は Unity によって処理されます。とても簡単です。
図 8 スクリプトを割り当てた Enemy
図 9 のコードのパブリック変数に注目してください。エディターを見ると、このパブリック変数が、実行時に既定値をオーバーライドするオプションと一緒に表示されています。これはすばらしいことです。既定値を GUI でプリミティブ型に変更することも、さまざまなオブジェクト型のパブリック変数 (プロパティではありません) を公開することもできます。このコードを他の GameObject にドラッグ アンド ドロップすると、そのコード コンポーネントの完全に個別のインスタンスが作成されます。これが基本的な例です。このオブジェクトに RigidBody コンポーネントを追加するとさらに効率的になりますが、ここではシンプルに保ちます。
図 9 EnemyAI スクリプト
public class EnemyAI : MonoBehavior
{
// These values will appear in the editor, full properties will not.
public float Speed = 50;
private Transform _playerTransform;
private Transform _ myTransform;
// Called on startup of the GameObject it's assigned to.
void Start()
{
// Find some gameobject that has the text tag "Player" assigned to it.
// This is startup code, shouldn't query the player object every
// frame. Store a ref to it.
var player = GameObject.FindGameObjectWithTag("Player");
if (!player)
{
Debug.LogError(
"Could not find the main player. Ensure it has the player tag set.");
}
else
{
// Grab a reference to its transform for use later (saves on managed
// code to native code calls).
_playerTransform = player.transform;
}
// Grab a reference to our transform for use later.
_myTransform = this.transform;
}
// Called every frame. The frame rate varies every second.
void Update()
{
// I am setting how fast I should move toward the "player"
// per second. In Unity, one unit is a meter.
// Time.deltaTime gives the amount of time since the last frame.
// If you're running 60 FPS (frames per second) this is 1/60 = 0.0167,
// so w/Speed=2 and frame rate of 60 FPS (frame rate always varies
// per second), I have a movement amount of 2*0.0167 = .033 units
// per frame. This is 2 units.
var moveAmount = Speed * Time.deltaTime;
// Update the position, move toward the player's position by moveAmount.
_myTransform.position = Vector3.MoveTowards(_myTransform.position,
_playerTransform.position, moveAmount);
}
}
コードでは、エディター内で公開されている任意のコンポーネントへの参照を受け取ることができます。また、GameObject に、独自の Start メソッドや Update メソッド (およびその他のメソッド) を設定したスクリプトを割り当てることもできます。このコードを含むスクリプト コンポーネントに EnemyAI クラス (コンポーネント) への参照が必要であれば、単純にそのコンポーネントを要求できます。
public class EnemyHealth : MonoBehavior
private EnemyAI _enemyAI;
// Use this for initialization.
void Start () {
// Get a ref to the EnemyAI script component on this game object.
var enemyAI = this.GetComponent<EnemyAI>();
}
// Update is called once per frame.
void Update () {
_enemyAI.MoveTowardsPlayer();
}
コードを MonoDevelop または任意のコード エディターで編集してから Unity に切り替えると、通常、若干の遅延が生じます。これは、Unity でコードのバックグラウンド コンパイルが行われるためです。コード エディター (デバッガーではない) は、[Edit] (編集)、[Preferences] (基本設定)、[External Tools] (外部ツール)、[External Script Editor] (外部スクリプト エディター) の順にアクセスして変更できます。コンパイルに問題がある場合は、Unity エディター画面の一番下のステータス バーに表示されるので、見逃さないようにしてください。コードにエラーがあるゲームを実行しようとしても、Unity では続行できません。
コードを記述する
先ほどのコード サンプルには、Start と Update という 2 つのメソッドと、MonoBehavior 基本クラスから継承した EnemyHealth クラスがあります。この基本クラスを使用すれば、継承したクラスを GameObject に割り当てるだけで済みます。この基本クラスには多くの機能と、通常、いくつかのメソッドとプロパティがあります。main メソッドがクラス内に存在する場合は、Unity によって呼び出されます。呼び出すことのできるメソッドは多数あります (docs-jp.unity3d.com/Documentation/Manual/ExecutionOrder.html 参照)。ASP.NET Web フォーム ページのライフサイクルと同様に、多くのメソッドがあるとしても、使用するのは一般的にごく少数です。クラスに実装する最も一般的なコード メソッドは次の通りです。これらは、MonoBehavior 派生クラスのイベントのシーケンスに関連します。
Awake: オブジェクトごとに 1 回、オブジェクトの初回の初期化時に呼び出されます。他のコンポーネントはまだ初期化されていない可能性があるため、このメソッドは通常、現在の GameObject を初期化するために使用されます。MonoBehavior 派生クラスの初期化には常にこのメソッドを使用し、コンストラクターは使用しません。また、シーン内の他のオブジェクトはまだ初期化されていない可能性があるため、他のオブジェクトに対してクエリを実行しないようにします。
Start: オブジェクトの有効期間中かつ Update メソッドが呼び出される前の最初のフレームで呼び出されます。Awake にとても似ていますが、Start の場合は、シーン内の他のオブジェクトが Awake によって初期化されていることがわかっています。そのため、次のように、コードで他のオブジェクトにクエリを簡単に実行できます。
// Returns the first EnemyAI script component instance it finds on any game object.
// This type is EnemyAI (a component), not a GameObject.
var enemyAI = GameObject.FindObjectOfType<EnemyAI>();
// I'll actually get a ref to its top-level GameObject.
var enemyGameObject = enemyAI.gameObject;
// Want the enemy’s position?
var position = enemyGameObject.transform.position;
Update: 各フレームで呼び出されます。その頻度は、場合によって異なり、完全に処理に依存します。さまざまなものをレンダリングしているため、システムの負荷は常に変化しており、このフレーム レートも毎秒変化するからです。再生モード時に [Game] (ゲーム) タブの [Stats] (統計) ボタンを押すと、現在のフレーム レートを確認できます (図 10 参照)。
図 10 統計を取得する
FixedUpdate: フレーム レートにかかわらず、1 秒間に決められた回数呼び出されます。Update は 1 秒間に呼び出される回数が一定ではなく、物理エンジンと同期しません。そのため、オブジェクトに力またはその他の物理関係関数を提供する場合には、一般的に FixedUpdate を使用するのが最適です。FixedUpdate は既定で 0.02 秒ごとに呼び出されます。つまり、Unity での物理計算も 0.02 秒ごとに行われます (この間隔は、Fixed Timestep と呼ばれ、開発者が調整できます)。これは、やはりフレーム レートに依存しません。
Unity が生成するコード プロジェクト
プロジェクトのコードを記述したら、Unity によってルート フォルダーに 1 つ以上のプロジェクト ファイルが作成されます (Unity インターフェイスには表示されません)。プロジェクト ファイルは Unity エンジン バイナリではなく、コードの編集とコンパイルを行った Visual Studio または MonoDevelop 用のプロジェクトです。Unity では、多数の個別のプロジェクトのように見えるものを作成します (図 11 参照)。ただし、それぞれには重要な目的があります。
図 11 Unity が作成したプロジェクト
単純な Unity プロジェクトの場合、これらのファイルがすべて存在するわけではありません。これらのファイルは、コードをさまざまな特殊フォルダーに配置した場合にのみ作成されます。図 11 に示すプロジェクトを分類すると、次の 3 種類のみになります。
- Assembly-CSharp.csproj
- Assembly-CSharp-Editor.csproj
- Assembly-CSharp-firstpass.csproj
各プロジェクトには、Assembly-CSharp-vs.csproj のように、末尾に -vs が追加された重複するプロジェクトがあります。これらのプロジェクトはコード エディターが Visual Studio の場合に使用されます。また、Visual Studio ソリューションでプラットフォーム固有のデバッグを行うために Unity からエクスポートしたプロジェクトに追加することができます。
他のプロジェクトは、同じ目的を果たしますが、CSharp が UnityScript に置き換えられます。これらは、単にプロジェクトの JavaScript (UnityScript) バージョンです。これらのプロジェクトが存在するのは、Unity ゲームで JavaScript を使用しており、さらに、作成するこれらのプロジェクトをトリガーするスクリプトがフォルダーにある場合のみです。
どのようなプロジェクトが作成されるのかを確認したので、これらのプロジェクトをトリガーするフォルダーについて説明し、その目的について示します。すべてのフォルダー パスは、プロジェクト ビューの /Assets ルート フォルダーの下にあることを想定しています。Assets は常にルート フォルダーであり、その下にすべてのアセット ファイルが含まれます。たとえば、Standard Assets は、実際には /Assets/Standard Assets です。アセンブリを生成するスクリプトのビルド プロセスは、4 フェーズで行われます。フェーズ 1 でコンパイルされるオブジェクトは、フェーズ 2 でコンパイルされるオブジェクトを認識できません (まだコンパイルされていないため)。同じプロジェクト内で UnityScript と C# を混在させる場合には注意してください。UnityScript から C# クラスを参照する場合は、それ以前のフェーズでそのクラスがコンパイルされている必要があります。
フェーズ 1 は、Standard Assets フォルダー、Pro Standard Assets フォルダー、および Plug-ins フォルダー内のランタイム スクリプトで構成されます。これらのフォルダーはすべて /Assets の下に配置されています。このフェーズで、Assembly-CSharp-firstpass.csproj プロジェクトが作成されます。
フェーズ 2 のスクリプトは、Standard Assets/Editor フォルダー、Pro Standard Assets/Editor フォルダー、Plug-ins/Editor フォルダーにあります。最後のフォルダーは、設計時機能を提供する Unity エディターの API を操作するスクリプトであることを意味します (Visual Studio プラグインと、そのプラグインによる GUI 強化方法を考えてください。これだけが Unity エディター内で実行されます)。このフェーズで、Assembly-CSharp-Editor-firstpass.csproj プロジェクトが作成されます。
フェーズ 3 は、Editor フォルダーに含まれないすべての他のスクリプトから構成されます。このフェーズで、Assembly-CSharp-Editor.csproj プロジェクトが作成されます。
フェーズ 4 は、残りすべてのスクリプト (/Assets/Editor や /Assets/Foo/Editor などの Editor という名前の他のフォルダー内のスクリプト) から構成されます。このフェーズで、Assembly-CSharp.csproj プロジェクトが作成されます。
他にも、Resources などの使用頻度が少ないフォルダーがいくつかありますが、ここでは説明しません。また、コンパイラで "何を" 使用するかという疑問にも答えていません。.NET か、Mono か、Windows Runtime (WinRT) 用 .NETか、Windows Phone Runtime 用 .NET かについては、図 12 にコンパイルに既定で使用されるものを一覧にしています。使用できる API はプラットフォームごとに異なるので、特に WinRT ベースのアプリの場合は、この一覧を理解することが重要です。
図 12 コンパイルのバリエーション
プラットフォーム | ゲーム アセンブリの生成元 | 最終コンパイル実行元 |
Windows Phone 8 | Mono | Visual Studio/.NET |
Windows ストア | .NET | Visual Studio/.NET (WinRT) |
Windows スタンドアロン (.exe) | Mono | Unity - .exe とライブラリを生成 |
Windows Phone 8.1 | .NET | Visual Studio/.NET (WinRT) |
Windows 向けにビルドを行う場合、Unity は、呼び出しを行って C#/UnityScript/Boo コード (DLL) からゲーム ライブラリを生成し、ネイティブ ランタイム ライブラリを含める役割を担います。Windows ストアと Windows Phone 8 の場合は、Visual Studio ソリューションがエクスポートされます。ただし、Windows スタンドアロンの場合は、.exe ファイルと必要な .dll ファイルが Unity によって生成されます。さまざまなビルドの種類については、プラットフォームに対応したビルドについて取り上げる、シリーズの最終回で説明します。低水準のグラフィックス レンダリングは Windows プラットフォーム上で DirectX によって行われます。
Unity におけるゲームの設計は、非常に単純なプロセスです。
- アセット ストアを利用する、自分で作成する、アーティストを雇うなどしてアセット (アートワーク、オーディオなど) を用意する。Unity はネイティブで Maya、Cheetah3d、Blender、および 3dsMax をサポートしています。場合によっては、それらのネイティブ 3D 形式を処理するためのソフトウェアがインストールされている必要があります。また、.obj や .fbx の一般的なファイル形式も処理できます。
- オブジェクトやシーンを制御するコードを C#、JavaScript/UnityScript、または Boo で記述し、ゲーム ロジックを実装する。
- Unity でテストする。プラットフォームにエクスポートする。
- そのプラットフォームでテストする。展開する。
さらに学習したい人のために
今回は、Unity のアーキテクチャとプロセスの概要を紹介しました。インターフェイス、コード割り当ての基本、GameObject、コンポーネント、Mono と .NET など、さまざまなものを取り上げました。これらは、2D ゲームのゲーム コンポーネントの構成について説明する次回のコラムを理解するための前提知識となります。夏が終わる頃には、Unity について学習する 2 日間のイベントを企画していますので、Microsoft Virtual Academy をチェックしていてください。また、お住まいの地域の学習イベントについては unity3d.com/pages/windows/events (英語) をご覧ください。
Adam Tuliper は、南カリフォルニア在住のマイクロソフトのシニア テクニカル エバンジェリストです。彼は、インディーズ ゲーム開発者であり、Orange County Unity Meetup の共同管理者であり、pluralsight.com の著者でもあります。Tuliper 夫妻には間もなく第 3 子が生まれます。連絡がある場合は、彼に時間の余裕がある間に、[email protected] (英語のみ) または Twitter (twitter.com/AdamTuliper、英語) に連絡してください。
この記事のレビューに協力してくれた技術スタッフの Matt Newman (Subscience Studios)、Jaime Rodriguez (マイクロソフト)、および Tautvydas Žilys (Unity) に心より感謝いたします。