Unity

Unity と C# を使用した初めてのゲーム開発 (第 2 部)

Adam Tuliper

コード サンプルのダウンロード

Unity シリーズの第 2 回です。前回は、Unity の基本とアーキテクチャを取り上げました。今回は、バージョン 4.3 で追加された 2D サポートを踏まえて、Unity の 2D について説明します。バージョン 4.3 より前の Unity でも 2D ゲームを作成できましたが、そのプロセスはサードパーティ製ツールキットがなければ非常に面倒な作業でした。ドラッグ/ドロップ インターフェイスを使用して画像をドラッグ アンド ドロップするだけでシーンに表示して操作する機能は、Unity 4.3 で実装されました。今回は、基本的な 2D プラットフォーム ゲームを開発しながら、Unity の機能を詳しく取り上げ、Unity の基本的な考え方を説明します。

Unity における 2D

Unity で 2D サポートを利用するには、プロジェクトの新規作成時、新しいプロジェクト ダイアログのドロップダウンで [2D] を選択します。その結果、プロジェクトの既定の状態が 2D に設定され ([Edit] (編集)、[Project Settings] (プロジェクト設定)、[Editor] (エディター) の順に選択して確認できます)、プロジェクトにインポートする画像がすべてテクスチャではなく、スプライトになります (これについてはこの後説明します)。また、シーン ビューが既定で 2D モードになります。実際には、シーンの開発中に 2 つの軸に固定するヘルパー ボタンが提供されるだけで、開発されるゲームには特に影響はありません。いつでもヘルパー ボタンをクリックして、2D 作業モードのオンとオフを切り替えることができます。Unity の 2D ゲームは、開発作業が X 軸と Y 軸に限定されるだけにすぎず、根本は変わらず 3D 環境です。図 1図 2 はそれぞれ 2D モードを選択した状態と選択していない状態を示しています。カメラを強調表示しているため、カメラの表示領域の輪郭が分かります。カメラは、領域を長方形でとらえています。

2D モードを選択した状態 (カメラにフォーカス)
図 1 2D モードを選択した状態 (カメラにフォーカス)

2D モードを選択していない状態 (カメラにフォーカス)
図 2 2D モードを選択していない状態 (カメラにフォーカス)

強調表示されているカメラは、Unity に 2 つあるカメラ モードの 1 つ、平行投影 (Orthographic) カメラとして設定しています。このカメラの種類は 2D で使用されることが多く、両目で物体を見る場合と異なり、距離に応じて物体の大きさが変わることはありません。つまり、カメラの位置から見て奥行きがありません。もう 1 つのカメラの種類は透視投影 (Perspective) です。このカメラでは、両目で見る場合と同じように、奥行きのある表示になります。使用するカメラの種類の選択理由はさまざまですが、一般的には、視覚的な奥行きが必要な場合や、物体の大きさを相応に変化させる場合は透視投影カメラを選択します。カメラのモードは、カメラを選択して [Projection] (投影) の種類を変えるだけで変更できます。物体を Z 軸上で動かす場合は、カメラを変更してみて、カメラの表示領域がどのように変化するかを確認することをお勧めします。既定の動作モードはいつでも変更できます。動作モードによる影響があるのは、プロジェクトに画像をインポートする場合のみです。

Unity の既存のプロジェクトが手元にある場合、またはプロジェクト ダイアログで 2D を選択したかどうか分からない場合は、[Edit] (編集)、[Project Settings] (プロジェクト設定)、[Editor] (エディター) の順に移動して、プロジェクトの既定の状態を 2D に設定できます。既定の状態を 2D にしないと、インポートするすべての 2D 画像の型を手動でテクスチャに設定することになるため、アートワークが多い場合は少し面倒になります。

スプライトこそすべて

既定の動作モードとして 3D を選択すると、画像は Texture 型として認識されます。テクスチャをシーンにドラッグすることはできないため、テクスチャをオブジェクトに適用しなくてはなりません。このやり方では、2D ゲームの作成を楽しめません。画像をドラッグ アンド ドロップして、シーンに表示することができれば便利です。既定の動作モードが 2D の場合、作業は簡単になります。画像を Unity にドラッグ アンド ドロップすると、Sprite 型として認識されるようになります。

この動作は、アートワークを Unity にドラッグ アンド ドロップし、Unity からそのアートワークをシーンにドラッグ アンド ドロップしてゲームを構築できるようにします。アートワークが小さく見えるのなら、サイズを変更するのではなく、どこでも [Pixels To Units] (単位あたりのピクセル数) の値を減らすだけです。これは、Unity では 2D でも 3D でもごくありふれた操作で、通常、[Transform] (変換) の [Scale] (スケール) プロパティからオブジェクトの拡大/縮小をするよりも高いパフォーマンスが得られます。

オブジェクトをドロップすると、そのオブジェクトが別のオブジェクトの上に重ねて表示されるのが分かります。Unity は、2D 画像の場合も自動的に一連の頂点を作成するため、描画順序は画像の各部分によって異なります。したがって、画像の Z オーダーを明示的に指定することが重要になります。Z オーダーの指定には以下の 3 つの方法があります。Unity は、以下の順序でスプライトを描画します。

  1. [Sprite Renderer] (スプライト レンダラー) で [Sorting Layer] (並べ替えレイヤー) プロパティを設定する。
  2. [Sprite Renderer] で [Order in layer] (レイヤー内の順序) プロパティを設定する。
  3. [Transform] (変換) の [Position] (位置) プロパティの Z 位置の値を設定する。

最も優先順位が高いのは [Sorting Layer] (並べ替えレイヤー) です。次に [Order in layer] (レイヤー内の順序)、[Transform] (変換) の Z の値と続きます。

並べ替えレイヤーで定義した順に描画されます。([Edit] (編集)、[Project Settings] (プロジェクト設定)、[Tags and Layers] (タグとレイヤー) の順に移動して) 他のレイヤーを追加した場合、Unity はまず Default (既定) のレイヤーのオブジェクトを描画します (レイヤー内の描画順序は [Order in layer] (レイヤー内の順序)、[Transform] (変換) の [Position] (位置) プロパティの Z 位置の値の順に判断されます)。Default (既定) のレイヤーの後、背景レイヤー、足場レイヤーなどを描画します。そのため、重なって表示される画像を足場レイヤーに設定し、その上に表示する画像の [Order in layer] (レイヤー内の順序) を 1 に設定することで、[Order in layer] (レイヤー内の順序) が 0 の画像の後に描画されるように簡単に修正できます。

一般的な機能

図 3 に、ドラッグ アンド ドロップと並べ替えレイヤーの設定を行って構築した、いくつかの足場と背景の画像を含むステージを示します。

 ゲームのステージ
図 3 ゲームのステージ

現状、外観はゲームらしくなっていますが、ゲームらしい動きはしません。少なくとも、ゲームとして動かすには、多くの機能の追加が必要です。ここからは、その機能をいくつか説明します。

キーボード、マウス、およびタッチの動作: Unity では、入力システムからキーボード、マウス、加速度計、およびタッチが読み取られます。次のようなスクリプトを使用して、メイン プレイヤーの入力動作、マウス クリック、タッチなどを簡単に読み取れます (このスクリプトの作成はこの後行います)。

void Update()
{
  // Returns -1 to 1
  var horizontal = Input.GetAxis("Horizontal");
  // Returns true or false. A left-click
  // or a touch event on mobile triggers this.
  var firing = Input.GetButtonDown("Fire1");
}

[Edit] (編集)、[Project Settings] (プロジェクト設定)、[Input] (入力) の順に移動すると、既定の入力 (Unity では新しいプロジェクトごとに多くの入力方法が用意されています) の確認や新しい入力方法の設定ができます。図 4 に水平方向移動の読み取りの既定の設定を示します。"left" および "right" の設定は、左矢印キーと右矢印キーを表していますが、"a" と "d" も水平方向移動に使用されていることに注目してください。これらは、ジョイスティックの入力にマッピングできます。新しい入力方法の追加や、既定値の変更も可能です。[Sensitivity] (感度) フィールドは、Unity が 0 から 1 (または -1) に移動する際の速度を制御します。右矢印キーを押すと、最初のフレームでは 0.01 の値が生成され、すぐに 1 にまで急上昇します。ただし、この速度は調整可能で、キャラクターに瞬間的な水平方向移動速や動きを与えます。これらの値をゲーム オブジェクトに適用するコードはこの後お見せします。これらの値を読み取るのに実際の GameObject は必要ありません。コード内で Input キーワードを使用するだけで、入力を読み取る機能にアクセスできます。入力は、原則として、Update 関数内で読み取ります。入力イベントの見落としを避けるための FixedUpdate とは対照的です。

 水平方向入力の既定値
図 4 水平方向入力の既定値

直線運動: 動きに必須の機能です。トップダウン ゲーム (俯瞰型のゲーム) の場合、通常、重力は重要になりません。プラットフォーム ゲームの場合は、重力が非常に重要になります。どちらの場合も、オブジェクトの衝突検出は不可欠です。ここでは基本原則を説明します。ゲーム オブジェクトに Rigidbody2D または RigidBody (3D で使用) を追加することによって、そのコンポーネントに質量が自動的に付加され、重力を認識して力を受けるようになります。Wikipedia には、「物理学では、剛体 (rigid body) とは変形することのない理想化された固体のことである。つまり、ある剛体の特定の 2 点間の距離は、その剛体に加えられる外力にかかわらず、常に一定である」とあります。ゲームにも、同じ原理が当てはまります。剛体を追加すると、図 5 のような呼び出しができるようになります。

図 5 運動と速度を追加する

void FixedUpdate()
{
  // -1 to 1 value for horizontal movement
  float moveHorizontal = Input.GetAxis("Horizontal");
  // A Vector gives you simply a value x,y,z, ex  1,0,0 for
  // max right input, 0,1,0 for max up.
  // Keep the current Y value, which increases 
  // for each interval because of gravity.
  var movement = new Vector3(moveHorizontal * 
    _moveSpeed, rigidbody.velocity.y, 0);
  rigidbody.velocity = movement;
  if (Input.GetButtonDown("Fire1"))
  {
    rigidbody.AddForce(0, _jumpForce, 0);
  }
}

原則として、直線運動は Update で行い、加速運動は FixedUpdate で行います。初心者の場合、何をいつ使えばよいのか混乱するかもしれませんが、実際のところ、直線運動はどちらの関数でも機能します。ただし、先ほど説明した原則に従うことで、表示結果が良くなります。

衝突検出: RigidBody コンポーネントによりオブジェクトに質量が追加されますが、さらにそのオブジェクトでの衝突の処理方法を Unity に指示する必要があります。ここでは、アートワークやモデルのサイズと形状は重要ではありません。ただし、スケーリングはオブジェクト自体の物理特性に影響します。重要なのは、コライダー コンポーネントのサイズと形状です。コライダーとは、Unity で別のオブジェクトが接近していることを検出するオブジェクトの周囲、上部、内部などに定義される領域です。コライダーを使用すると、待機状態のゾンビや巨岩が存在する領域にプレイヤーが侵入するとそれを検出し、プレイヤーの進行に合わせて山から岩を転がすなどのシナリオが可能になります。

コライダーの形状はさまざまです。2D 用のコライダーには、Circle (円)、Edge (境界線)、Polygon (多角形)、または Box (四角形) を指定できます。Box Collider は、正方形や長方形のような形状のオブジェクトや、単純に正方形の領域で衝突を検出する場合に適しています。プレイヤーが立つ足場は、Box Collider の良い例です。このコンポーネントをゲーム オブジェクトに追加するだけで、物理衝突を利用できます。図 6 では、キャラクターに Circle Collider と RigidBody を追加し、足場に Box Collider を追加しています。エディターで再生ボタンをクリックすると、その直後にプレイヤーが足場上に落下して、停止します。コードは必要ありません。

コライダーを追加する
図 6 コライダーを追加する

コライダーがカバーする領域は、コライダー コンポーネントのプロパティを変更することで移動したり、サイズを変えることができます。既定では、コライダーが設定されたオブジェクトは互いに通り抜けることはできません (次に説明するトリガーを除きます)。衝突には、ゲーム オブジェクトの両方にコライダー オブジェクトが設定されており、さらに少なくとも一方のオブジェクトに RigidBody コンポーネントが追加されている必要があります (オブジェクトがトリガーの場合を除きます)。

衝突が初めて発生したときに Unity がコードを呼び出すようにするには、スクリプト コンポーネントを使用して次のコードをゲーム オブジェクトに追加するだけです (これについては前回の記事で説明しました)。

void OnCollisionEnter2D(Collision2D collision)
{
  // If you want to check who you collided with,
  // you should typically use tags, not names.
  if (collision.gameObject.tag == "Platform")
  {
    // Play footsteps or a landing sound.
  }
}

トリガー: 物理現象を発生させることなく衝突を検出したい場合があります。ゲーム内で宝物を手に入れるというシナリオを考えてみます。プレイヤーがコインに近づいたらコインが蹴り飛ばされるようでは困ります。プレイヤーの動きに影響されずに、コインを手に入れられるようにしたいと考えます。この場合、トリガーと呼ばれるコライダーを使用します。これは、[IsTrigger] チェック ボックスをオンにすることを除けば、他のコライダーと変わりません。このチェック ボックスにより、物理特性がオフになり、Unity はオブジェクト A (コライダーあり) がオブジェクト B (こちらもコライダーあり) の領域内に入った場合は、コードが呼び出されるだけです。トリガーを使用する場合、コード メソッドは OnCollisionEnter2D の代わりに OnTriggerEnter2D を使用します。

void OnTriggerEnter2D(Collider2D collider)
{
  // If the player hits the trigger.
  if (collider.gameObject.tag == "Player")
  {
    // More on game controller shortly.
    GameController.Score++;
    // You don’t do: Destroy(this); because 'this'
    // is a script component on a game object so you use
    // this.gameObject or just gameObject to destroy the object.
    Destroy(gameObject);
  }
}

トリガーでは、物理作用が起こらず、基本的には通知だけが行われます。また、トリガーでは、力の計算が行われないため、ゲーム オブジェクトに Rigidbody コンポーネントは不要です。

ゲーム開発に不慣れな開発者は、剛体にコライダーを追加する際に、剛体の動作につまづきがちです。オブジェクトに Circle Collider を設定して、そのオブジェクトを斜面に設置すると、(コライダーの形が示すとおり) 転がり始めます (図 7 参照)。これは、物理世界で斜面に車輪を置いた場合の動きを模しています。キャラクターに Box Collider を使用しないのは、他のコライダーの境界線上を移動する際に四角形の境界線が干渉して、動きが滑らかではなくなる可能性があるためです。Circle Collider を使用すると、動きが滑らかになります。しかし、滑らかな回転を許容できない場合に備えて、Rigidbody コンポーネントの [Fixed Angle] (固定角) 設定を使用することができます。

Circle Collider を使用して滑らかな移動を実現する
図 7 Circle Collider を使用して滑らかな移動を実現する

オーディオ: 音が聞こえるようにするには、Audio Listener コンポーネントが必要です。このコンポーネントは、すべてのカメラに既定で存在します。音を再生するには、ゲーム オブジェクトに Audio Source コンポーネントを追加してオーディオ クリップを設定します。Unity は主要オーディオ形式のほとんどをサポートしており、長いオーディオ クリップは MP3 にエンコードされます。Unity エディターに、多数のオーディオ ソースとクリップを割り当てている場合、読み込まれるのは実行時であることに注意してください。そこで、開発中は、特殊なリソース フォルダー内のコードに配置してオーディオを読み込み、開発が完了したらコードを破棄することができます。

私がプロジェクトにオーディオをインポートした際は、WAV ファイル (圧縮されていないオーディオ) として保持しました。Unity は長いオーディオを再エンコードして最適化するため、常に最高品質のオーディオを使用するようにしてください。Unity がエンコードを行わないサウンド効果などの短いファイルには、特に当てはまります。Audio Source コンポーネントもメイン カメラに追加しました (ただし、Audio Source コンポーネントは任意のゲーム オブジェクトに追加できます)。その後、Adventure オーディオ クリップをこの Audio Source コンポーネントに割り当て、[Loop] (ループ) チェック ボックスをオンにして、絶えずループするようにしました。この 3 つの簡単な手順で、ゲーム プレイ中のバックグラウンド ミュージックの設定は完了です。

GUI/ヘッドアップ ディスプレイ: GUI システムはゲーム内のさまざまな部分を構成します。たとえば、メニュー システム、体力やスコア表示、所有する武器などです。通常、GUI システムはカメラの方向に関係なく画面上に表示されます (ただし、常に表示されている必要はありません)。Unity の GUI 機能は現在全面的な改訂中で、新しい uGUI システムが Unity 4.6 で登場します。まだリリース前のため、ここでは基本機能の一部を紹介するにとどめますが、新しい GUI システムの詳細については私の channel9 ブログ (channel9.msdn.com/Blogs/AdamTuliper、英語) をチェックしてください。

画面に単純な表示テキスト(たとえば "スコア: 0") を追加するには、[Game Object] (ゲーム オブジェクト)、[Create Other] (その他を作成)、[GUI Text] (GUI テキスト) の順にクリックします。Unity 4.6 ではこのオプションがなくなるので、前述の uGUI に関するビデオをご覧になることをお勧めします。ただし、[Editor] (エディター) メニューからコマンドがなくなるだけで、Unity 4.6 でも引き続き [Add Component] (コンポーネントの追加) ボタンをクリックして GUI Text コンポーネントをゲーム オブジェクトに追加できます。既存の (従来の) Unity GUI システムでは、シーン ビューに GUI オブジェクトを表示できず、ゲーム ビューにのみ表示できました (ゲーム ビューではレイアウトの作成が少し奇妙になります)。必要に応じて、純粋なコードを使用して GUI を設定できます。Unity には、自動的にウィジェットを追跡できる GUILayout クラスが用意されています。しかし、私はクリックやドラッグで簡単に操作できる GUI システムが好きなので、uGUI は非常に優れていると感じます (uGUI の登場前、NGUI と呼ばれる非常に機能豊富なサードパーティ製品がこの分野で優勢でした。実際、NGUI は uGUI の初期コード ベースとして使用されました)。

この表示テキストを更新する最も簡単な方法は、エディター内で参照を検索して GUI Text ゲーム オブジェクトに割り当て、.NET のラベルのように処理してテキストを適宜更新することです。この方法を使用して、次のように画面の GUI テキストを簡単に更新できます。

void UpdateScore()
{
  var score = GameObject.Find("Score").GetComponent<GUIText>();
  score.text = "Score: 0";
}

この例は少し簡略化しています。パフォーマンスを考えると、実際は、メソッド呼び出しのたびに GUIText コンポーネントへの参照をクエリすることがないよう、Start メソッドで参照をキャッシュすることをお勧めします。

スコア追跡: スコアの追跡は簡単です。スコアの設定先となるパブリック メソッドまたはプロパティを公開するクラスを作成するだけです。ゲームには、ゲームの管理者の役割を果たす Game Controller (ゲーム コントローラー) と呼ばれるオブジェクトが存在するのが一般的です。Game Controller では、ゲームの保存、読み込み、スコアの記録などの処理を行います。今回の例では、単純にスコア変数を公開するクラスを作成しました (図 8 参照)。このコンポーネントを空のゲーム オブジェクトに割り当てたので、シーンが読み込まれると使用できるようになります。スコアを更新した後、GUI も更新します。_scoreText 変数の代入は Unity エディターで行います。任意の GUIText ゲーム オブジェクトをこの公開されたフィールドにドロップするか、検索ウィジェットを使用してこのスクリプト コンポーネントを検索してエディター内の _scoreText 変数に公開します。

図 8 _scoreText 変数を作成する

public class GameController : MonoBehaviour
{
  private int _score;
  // Drag a GuiText game object in the editor onto
  // this exposed field in the Editor or search for it upon startup
  // as done in Figure 12.
  [SerializeField]
  private GUIText _scoreText;
  void Start()
  {
    if (_scoreText == null)
    {
      Debug.LogError("Missing the GuiText reference. ");
    }
  }
  public int Score
  {
    get { return _score; }
    set
    {
      _score = value;
        // Update the score on the screen
      _scoreText.text = string.Format("Score: {0}", _score);
    }
  }
}

単純に (今回の例では) キノコのトリガー コードを次のように更新して、キノコを手に入れるたびにスコアをインクリメントできます。

void OnTriggerEnter2D(Collider2D collider)
{
  if (collider.gameObject.tag == "Player")
  {
    GameController.Score++;
    Destroy(gameObject);
  }
}

アニメーション: XAML と同様に、キー フレームでさまざまな処理を実行することで、アニメーションが作成されます。Unity におけるアニメーションだけで簡単に 1 本の記事が書けますが、記事のスペースの都合上、ここでは概要にとどめます。Unity のアニメーション システムには、従来のシステムと新しい Mecanim の 2 つがあります。従来のシステムではアニメーション (.ani) ファイルを使用しますが、Mecanim ではステートを使用して再生するアニメーション ファイルを制御します。

2D のアニメーションでは既定で Mecanim を使用します。アニメーションを作成する最も単純な方法は、シーンに画像をドラッグ アンド ドロップして、Unity にアニメーションを作成させることです。始めに、単独のスプライトをいくつか Unity にドラッグします。Unity ではいくつかのものが自動的に作成されます。まず、スプライトを描画するための Sprite Renderer コンポーネントが設定されたゲーム オブジェクトが作成されます。その後、アニメーション ファイルが作成されます。これは、[Window] (ウィンドウ)、[Animator] (アニメーター) の順に移動して、ゲーム オブジェクトを選択すると確認できます。アニメーターには、割り当てられたアニメーション ファイルが表示されます。今回の場合、シーンに 6 個の画像をドロップしたので、6 個のキー フレームが含まれています。各キー フレームでは、何らかのコンポーネントの 1 つ以上のパラメーターが制御されます。ここでは、Sprite Renderer コンポーネントの Sprite プロパティが変更されます。アニメーションは、単独の画像を一定の速度で表示して、視覚に動きを認識させているにすぎません。

次に、Unity によってゲーム オブジェクト上に Animator コンポーネントが作成されます (図 9 参照)。

コントローラーを参照する Animator コンポーネント
図 9 コントローラーを参照する Animator コンポーネント

このコンポーネントは、アニメーション コントローラーと呼ばれる単純なステート マシンを指しています。アニメーション コントローラーは、Unity によって作成されるファイルで、既定のステートを示します。つまり、"idle" (待機) ステートが唯一の使用可能なステートであるため、常にこの状態にあります。この idle ステートでは、アニメーション ファイルを指す以外には何も行われません。図 10 は、実際のキー フレーム データを時系列に示しています。

Idle アニメーション データ
図 10 Idle アニメーション データ

アニメーションを再生するには、多くの作業が必要なように感じられるでしょう。しかし、ステート マシンの真価は、単純な変数を設定することで開発者がステート マシンを制御できることにあります。ステートはアニメーション ファイルを指す以外になにも必要なかったことを思い出してください (ただし、3D では工夫してアニメーションをブレンドすることができます)。

次に、さらに画像を追加して、駆け足のアニメーションを作成し、アニメーションを Yeti ゲーム オブジェクトにドロップします。ゲーム オブジェクトには既に Animator コンポーネントを設定したので、Unity は新しいアニメーション ファイルの作成と "run" (駆け足) という名前の新しいステートの追加を行います。idle ステートを右クリックすると、run ステートへの遷移を作成できます。idle ステートから run ステートへの矢印が作成されるので、"Running" という新しい変数を追加します。この変数の使い方は簡単で、ステート間の矢印をクリックして、変数を使用する条件を変更するだけです (図 11 参照)。

Idle ステートから Run ステートに変更する
図 11 Idle ステートから Run ステートに変更する

Running が true になると、run アニメーション ファイルが再生されて idle アニメーション ステートが run アニメーション ステートに変わります。この変数はコードで簡単に制御できます。マウスのボタンがクリックされたときに run ステートをトリガーして run アニメーションを開始する場合は、図 12 に示すコードを追加します。

図 12 コードを使用してステートを変更する

private Animator _animator;
void Awake()
{
    // Cache a reference to the Animator 
    // component from this game object
    _animator = GetComponent<Animator>();
}
void Update()
{
  if (Input.GetButtonDown("Fire1"))
  {
    // This will cause the animation controller to
    // transition from idle to run states 
    _animator.SetBool("Running", true);
  }
}

この例では、単独のスプライトを使用してアニメーションを作成しました。しかし、スプライト シート (複数の画像から構成される単独の画像ファイル) を使用するのがごく一般的です。Unity はスプライト シートをサポートしているので、Unity にスプライトのスライス方法を指定して、そのスライスをシーンにドロップすることが重要になります。スプライトのプロパティで [Sprite Mode] (スプライト モード) を [Single] (単独) から [Multiple] (複数) に変更して、Sprite Editor (スプライト エディター) を開きます。Sprite Editor (スプライト エディター) では、自動的にスプライトがスライスされて変更が適用されます (図 13 参照)。最後に、スプライトを展開して (プロジェクト ビューに表示されるスプライトのアイコンに小さな矢印があります)、結果のスプライトを強調表示し、先ほどと同じようにシーンにドロップします。

スプライト シートを作成する
図 13 スプライト シートを作成する

アニメーションは、システムに習熟していない人にとって複雑なテーマです。詳細については、私の channel9 ブログを参照するか、Unity ラーニング サイトにある優れたリソースをご覧ください。

ステージの終わり: ステージのゴールを設定するには、ゴール地点にプレイヤーが到達したときに実行されるトリガーを設定したコライダーを用意します。ステージが終了したら、次のステージを読み込むか、現在のステージを読み込み直します。

void OnTriggerEnter2D(Collider2D collider)
{
  // If the player hits the trigger.
  if (collider.gameObject.tag == "Player")
  {
    // Reload the current level.
    Application.LoadLevel(Application.loadedLevel);
    // Could instead pass in the name of a scene to load:
    // Application.LoadLevel("Level2");
  }
}

ゲーム オブジェクトとそれぞれのプロパティは 図 14 に示すとおりです。コライダーがプレイヤーのジャンプでは超えられない高さに設定されていること、およびそのコライダーがトリガーとして設定されていることに注目してください。

ゲーム オブジェクトとそのプロパティ
図 14 ゲーム オブジェクトとそのプロパティ

ゲーム プレイ: このようなシンプルな 2D ゲームでは、ゲームの流れは非常に単純です。ゲームを始めると、剛体にかかる重力によってプレイヤーが落下します。プレイヤーと足場にはコライダーが設定されているため、プレイヤーは停止します。キーボード、マウス、およびタッチによる入力が読み取られ、プレイヤーが動きます。プレイヤーをジャンプさせる rigidbody.AddForce を適用することで、プレイヤーが足場間をジャンプします。Input.GetAxis("Horizontal") を読み取り、rigidbody.velocity を適用することで、プレイヤーが左右に移動します。プレイヤーがキノコを手に入れます。キノコにはトリガーの役割をするコライダーが設定されているので、プレイヤーが接触するとキノコは破壊され、スコアがインクリメントされます。プレイヤーがゴールに到達すると、そこにはコライダー/トリガーが設定されているので、現在のステージが再度読み込まれます。ここでもう 1 つ行う必要のある作業は、プレイヤーが足場から落下したことを検出するために、地面に大きなコライダーを追加することです。また、落下した場合にはそのステージを再度読み込むようにもします。

プレハブ: デザインと同様に、コーディングでも再利用が重要です。いくつかのコンポーネントを割り当ててゲーム オブジェクトをカスタマイズすると、同じシーン内だけでなく、複数のシーンやゲームでも再利用したくなるでしょう。シーンにゲーム オブジェクトのインスタンスをもう 1 つ作成してもかまいませんが、まだシーン内に存在しないプレハブのインスタンスを作成することもできます。足場とそのコライダーを考えてください。これらをシーン間で再利用することは、現在はできませんが、プレハブを作成することはできます。階層からプロジェクト フォルダーにゲーム オブジェクトをドラッグすると、.prefab という拡張子の新しいファイルが作成されます。このファイルには子階層がすべて含まれます。このファイルをシーンにドラッグすることでオブジェクトを再利用できます。元のゲーム オブジェクトはプレハブに結び付けられていることを示すために青に変わります。.prefab ファイルを更新すると、シーンに含まれるすべてのインスタンスが更新されます。また、修正したシーンのプレハブから、.prefab ファイルに変更を反映することもできます。

プレハブをクリックすると、それに含まれるゲーム オブジェクトが表示されます (図 15 参照)。ここで変更すると、シーン内のすべてのインスタンスが更新されます。

プレハブの内容を表示する
図 15 プレハブの内容を表示する

まとめ

さまざまなゲームで多くの操作が共通して実行されています。今回は、プラットフォーム ゲームで使用される、コライダー、剛体、アニメーション、スコア管理、基本的な GUI テキスト、ユーザー入力の読み取りとプレイヤーの動きへの力の適用などの基本事項について説明しました。これらの構成要素は、さまざまな種類のゲームで再利用できます。次回は 3D を取り上げます。お楽しみに。

その他のラーニング リソース

 


Adam Tuliper は、南カリフォルニア在住のマイクロソフトのシニア テクニカル エバンジェリストです。彼は、インディーズ ゲーム開発者であり、Orange County Unity Meetup の共同管理者であり、pluralsight.com の著者でもあります。Tuliper 夫妻には間もなく第 3 子が生まれます。連絡がある場合は、彼に時間の余裕がある間に、adamt@microsoft.com (英語のみ) または Twitter (twitter.com/AdamTuliper、英語) に連絡してください。

この記事のレビューに協力してくれた技術スタッフの Matt Newman (Subscience Studios) と Tautvydas Žilys (Unity) に心より感謝いたします。