Help us understand the problem. What is going on with this article?

【Unity2D】Unityで2Dミニゲームを作るチュートリアル(第3回)

More than 5 years have passed since last update.

はじめに

この記事は、Unity初心者が2Dゲームの作成に慣れることを目的としたチュートリアルとなります。前回は敵を出現させてクリックすると消えるところまでを作りました。

今回で演出とシーンの遷移を作り、ミニゲームを完成させたいと思います。

Unityで2Dミニゲームを作るチュートリアル(第3回)

パーティクルの追加

たこ焼きが消えるときに演出がないので違和感があります。そこでパーティクルを追加します。パーティクルとは「粒子」の意味で、これをたくさん発生させて演出とする仕組みです。Unityには「Shuriken」というパーティクルシステムが用意されていますが、ここではそれを使わずに独自のパーティクル生成を行うようにします。
まずはパーティクルに使うスプライトを登録します。Projectビューの「Assets/Sprites」フォルダにある「effect1」スプライトをSceneビューにドラッグ&ドロップします。そうすると「effect1」オブジェクトが作られるので名前を「Particle」に変更します。
02-45.png

ただ見た目がおかしなことになっていますね。理由はこの画像は加算合成して使うことを前提としているのですが、Unityの標準では半透明合成の設定となっているためです。なお、加算合成とは画像の重なる部分の色味を足し込むことで、結果、重なる部分を明るくしていく方法です。
加算合成を行うにはマテリアルの変更が必要なので、マテリアルを作成していきます。まずはProjectビューのAssetsフォルダを右クリックして、マテリアル保存用のフォルダを作成します。名前は「Materials」とします。
02-46.png

フォルダを作成したら、Assets/Materialsフォルダを右クリックして、Create > Material を選んでマテリアルを作成します。
02-47.png

名前は「mat_particle」としておきます。作成したマテリアルを選択してInspectorビューからマテリアルのパラメータを変更します。Shaderの項目を選んでパラメータを「Particle > Additive」に変更します。
02-48.png

設定できたら、HierarchyビューのParticleオブジェクトを選びます。そしてInspectorビューのSprite Rendererコンポーネントから、Materialの項目の右にある歯車のようなアイコンをクリックします。するとマテリアル選択ダイアログが表示されるので、そこから「mat_particle」を選択します。
02-49.png

すると、Particleのスプライトが加算合成で描画されるようになります。
02-50.png

パーティクルの動きを作る

パーティクルのスプライトが正しく描画されるようになったので、動きを作っていきます。
まずはParticleオブジェクトを選択して、Add Componentから、Physics 2D > Rigidbody 2Dを選択します。そしてInsupectorビューから、Rigidbody 2DのGravity Scaleを「0」にして重力を無効にします。
02-51.png

次に、ProjectビューのScriptsを右クリックして「Create > C# Script」でスクリプトを作成します。名前は「Particle」とします。
作成できたら、Particleスプライトを、Particleオブジェクトにドラッグ&ドロップしてアタッチします。
02-52.png

アタッチできたら、Particleスクリプトを編集します。

Particle.cs
using UnityEngine;
using System.Collections;

/// パーティクル
public class Particle : Token
{

  /// 開始。コルーチンで処理を行う
  IEnumerator Start()
  {
    // 移動方向と速さをランダムに決める
    float dir = Random.Range(0, 359);
    float spd = Random.Range(10.0f, 20.0f);
    SetVelocity(dir, spd);

    // 見えなくなるまで小さくする
    while (ScaleX > 0.01f)
    {
      // 0.01秒ゲームループに制御を返す
      yield return new WaitForSeconds(0.01f);
      // だんだん小さくする
      MulScale(0.9f);
      // だんだん減速する
      MulVelocity(0.9f);
    }

    // 消滅
    DestroyObj();
  }
}

修正点としては、まず継承元クラスをTokenクラスに変更しています。そして、Start関数ですが戻り値を「void」から「IEnumerator」に変更しています。これはこの関数内でのコルーチンを有効にする設定です。コルーチンとは関数の途中で処理を停止し、再開するときはその続きから実行できるようにする制御機構となります。正確には違いますがマルチスレッドのようなもので、コルーチンを使うと並列して複数の処理を走らせることができます。
そしてここではスプライトのサイズを MulScale 関数でどんどん小さくしている(スケール値に 0.7 をかけ続けることで 0 に近づくようにしている)という処理を「yield return new WaitForSeconds」という記述により、0.01秒止めて、その時間経過後、またスプライトを小さくする、という処理を行っています。この「yield ~」という記述なしで while文による繰り返しを行うと、ゲームループ(ゲームの更新)を止めてしまうので意図した動きとはなりません。
そうして、スケール値が「0.01」以下になったら、Particleオブジェクトを破棄する、という処理になっています。
では、Unityエディタに戻って実行し、Particleオブジェクトが移動しながら消えることを確認します。

Particleオブジェクトをプレハブ化する

敵を倒したらパーティクルを生成できるように、Particleオブジェクトをプレハブ化します。Particleオブジェクトを選択し、ProjectビューのAssets/Resources/Prefabsフォルダにドラッグ&ドロップしてプレハブ化します。プレハブ化できたら、ParticleオブジェクトはHierarchyビューから削除しておきます。
02-53.png

次に、Particleプレハブをスクリプトから生成できるようにします。まずはParticleスクリプトに、Particleオブジェクトを生成するAdd関数を追加します。

Particle.cs
/// パーティクル
public class Particle : Token
{
  /// プレハブ
  static GameObject _prefab = null;
  /// パーティクルの生成
  public static Particle Add(float x, float y)
  {
    // プレハブを取得
    _prefab = GetPrefab(_prefab, "Particle");
    // プレハブからインスタンスを生成
    return CreateInstance2<Particle>(_prefab, x, y);
  }
  ……(以下省略)

プレハブからオブジェクトを生成するには、まずプレハブを取得する必要があります。GetPrefab関数はTokenクラスに実装した関数ですが、これを呼び出すと、"Resources/Prefabs"にあるプレハブを取得することができます。そして次の CreateInstance2 関数で、プレハブからオブジェクトのインスタンスを作成しています。これらの関数の説明もゲーム完成後に説明します。あとプレハブ変数や生成関数は static キーワードをつけて外部からいつでも呼び出すことができるようにしています。
ひとまず、これで外部から生成することが可能になりました。次にEnemyスクリプトを開いて、OnMouseDown関数を修正します。

Particle.cs
  /// クリックされた
  public void OnMouseDown()
  {
    // パーティクルを生成
    for (int i = 0; i < 32; i++)
    {
      Particle.Add(X, Y);
    }

    // 破棄する
    DestroyObj();
  }

DestroyObjで破棄する前に、Enemyオブジェクトが存在する座標を中心にパーティクルを生成するようにしています。
修正できたら、Unityエディタに戻って実行します。たこ焼きをクリックするとパーティクルが生成されるようになります。
02-54.png

パーティクルのサイズがちょっと大きい気もしますが、ひとまずこれでOKとしておきます。

ゲームクリア判定を作る

ゲームクリア判定を作ります。敵が全滅したらクリアとします。まずはEnemyスクリプトを開いて、敵の生存数をカウントする処理を作ります。

Enemy.cs
/// 敵
public class Enemy : Token
{
  // 生存数
  public static int Count = 0;
  /// 開始
  void Start()
  {
    // 生存数を増やす
    Count++;

    // サイズを設定
    ……(省略)

まずは static変数で「Count」を用意します。そして、Start関数の最初でCountを増やして生存数をカウントしています。Start関数は生成時に必ず呼び出されるのでここで生存数を増やしているわけです。
そして OnMouseDown関数も修正します。

Enemy.cs
  /// クリックされた
  public void OnMouseDown()
  {
    // 生存数を減らす
    Count--;

    // パーティクルを生成
    ……(省略)

クリック時に消滅するのでここで生存数を減らしています。これで敵の生存数が取得できるようになりました。
続けて、ゲームの制御を行うオブジェクトを作成します。Unityエディタに戻って、メインメニューから「GameObject > Create Empty」を選択して空のゲームオブジェクトを作成します。02-55.png

GameObjectという名前でHierarchyビューに空のゲームオブジェクトが作成されるので、この名前を「GameMgr」に変更します。
次に、Projectビューの「Assets/Scripts」フォルダを右クリックして「Create > C# Script」でスクリプトを作成します。名前は「GameMgr」とします。作成したスクリプト「GameMgr」はGameMgrオブジェクトにドラッグ&ドロップしてアタッチします。
スクリプトをアタッチしたら、「GameMgr」スクリプトを編集します。GameMgrにOnGUI関数を追加します。この関数は、GUIの描画を行うためのイベント関数となります。

GameMgr.cs
using UnityEngine;
using System.Collections;

public class GameMgr : MonoBehaviour {
  void OnGUI()
  {
    if (Enemy.Count == 0)
    {
      // 敵が全滅した
      // フォントサイズ設定
      Util.SetFontSize(32);
      // 中央揃え
      Util.SetFontAlignment(TextAnchor.MiddleCenter);

      // フォントの位置
      float w = 128; // 幅
      float h = 32; // 高さ
      float px = Screen.width / 2 - w / 2;
      float py = Screen.height / 2 - h / 2;

      // フォント描画
      Util.GUILabel(px, py, w, h, "Game Clear!");
    }
  }
}

Start関数とUpdate関数はひとまず消しています。やっていることは、敵の生存数が「0」になったときに「Game Clear!」のテキストを表示しているだけです。
スクリプトに記述できたら実行して、敵を全滅させるとフォントが描画されることを確認します。

タイトル画面を作る

メインとなるゲームは完成したので、タイトル画面を作成します。タイトル画面を作る前の準備として、Mainシーンをアプリケーションに登録する作業をします。
メインメニューから「File > Build Settings...」を選びます。
02-56.png

すると「Build Settings」ダイアログが表示されるので、右の真ん中あたりにある「Add Current」ボタンをクリックします。すると「Scenes In Build」に現在のシーン「Scenes/Main.unity」が追加されます。
02-57.png

Unityは、このビルド設定にシーンを追加しないとビルド対象とならず、別のシーンから呼び出すことができません。登録したら右上の×ボタンで閉じます。
次にタイトルシーンの作成です。メインメニューから、「File > New Scene」を選択します。
02-58.png

すると、現在のシーンを保存するかどうかの確認ダイアログが出ます。「Save」を選ばないと保存されずに作業した内容が消えてしまうので、「Save」ボタンをクリックします。
02-59.png

「Save」ボタンをクリックすると、まっさらなシーンができあがります。まずは新しく作成したシーンを保存しましょう。メインメニューから「File > Save Scene」を選んで保存します。そうすると保存ダイアログが表示されるので、「Scenes」フォルダを選んで「Title」という名前で保存します。
02-60.png

保存すると、ProjectビューのAssets/ScenesフォルダにTitleシーンが表示されます。
02-61.png

なお、ここからシーンをダブルクリックするとそのシーンを直接開くことができます。試しに「Main」をダブルクリックしてみましょう。するとMainシーンの配置情報が表示されます。確認したら「Title」シーンをダブルクリックしてタイトル画面の実装に戻りましょう。
まずはTitleシーンをビルド設定に登録しておきます。メインメニューから「File > Build Settings...」を選んでビルド設定ダイアログを表示します。そして、「Add Current」をクリックして、Titleシーンを追加します。
02-61a.png

シーンの追加ができたら背景の表示を実装します。Projectビューの「Assets/Sprites」から「bg_back」をSceneビューにドラッグ&ドロップして背景を配置します。そして、InspectorビューからTransform.Positionの値を(X, Y, Z)=(0, 0, 0)にして、背景を中央に移動します。
次にカメラの調整をします。Hierarchyビューから「Main Camera」を選択して、InspectorビューからCameraコンポーネントのSizeを「2.4」にします。これで背景が画面いっぱいに表示されるようになります。
続けて、メインメニューから「GameObject > Create Empty」を選んで空のゲームオブジェクトを作成します。そして名前を「TitleMgr」に変更します。
02-62.png

そしてProjectビューの「Assets/Scripts」フォルダを右クリックして「Create > C# Script」を選択してスクリプトを作成します。スクリプトの名前は「TitleMgr」に変更します。そうしたら、TitleMgrスクリプトをTitleMgrオブジェクトにドラッグ&ドロップしてアタッチします。
02-63.png

アタッチしたら、TitleMgrスクリプトを開いて、OnGUI関数を追加します。

TitleMgr.cs
using UnityEngine;
using System.Collections;

public class TitleMgr : MonoBehaviour {
  void OnGUI()
  {
    // フォントサイズ
    Util.SetFontSize(32);
    // 中央揃え
    Util.SetFontAlignment(TextAnchor.MiddleCenter);

    // フォントの位置
    float w = 128; // 幅
    float h = 32; // 高さ
    float px = Screen.width / 2 - w / 2;
    float py = Screen.height / 2 - h / 2;

    // フォント描画
    Util.GUILabel(px, py, w, h, "MINI GAME");

    // ボタンは少し下にずらす
    py += 60;
    if (GUI.Button(new Rect(px, py, w, h), "START"))
    {
      // メインゲーム開始
      Application.LoadLevel("Main");
    }
  }
}

GameMgr同様、Start関数とUpdate関数は削除しています。GUI.Button関数はボタンを表示する関数で、ボタンが押されるとtrueを返却します。
OnGUI関数を追加したら、Unityエディタに戻って実行します。テキストとボタンが表示され、ボタンをクリックするとメインゲームが始まります。

ただ、メインゲーム画面でたこ焼きが正常に表示されなくなっていますね。
02-64.png

Hierarchyビュー上では「Enemy」オブジェクトが存在しているけれども、画面(シーン)上にはいません。これは背景の描画順とEnemyオブジェクトの描画順が同じになっているためです。これを解決するには、どちらかを前に出すか後ろに下げるかをしないといけません。
メインゲーム画面の描画順を修正します。まずはMainシーンに切り替えるために、Projectビューから「Assets/Scenes」フォルダにあるMainシーンをダブルクリックして、Mainシーンを開きます(保存ダイアログは「Save」を選んで保存します)。そして、Hierarchyビューからbg_backオブジェクトを選択し、InspectorビューからSpriteRendererの「Order In Layer」を「-1」にします。
02-65.png

これにより背景の描画順が後ろに移動します。Order In Layerは数値が大きくなると手前に描画され、小さくすると奥に描画されます。
実行して、たこ焼きが表示されることを確認します。
これでタイトル画面とメインゲーム画面がつながったので、メインゲーム画面からタイトル画面に戻れるようにします。
GameMgrスクリプトを開いて、OnGUI関数を修正します。

GameMgr.cs
  public void OnGUI()
  {
    if (Enemy.Count == 0)
    {
      // 敵が全滅した
      // フォントサイズ設定
      Util.SetFontSize(32);
      // 中央揃え
      Util.SetFontAlignment(TextAnchor.MiddleCenter);

      // フォントの位置
      float w = 128; // 幅
      float h = 32; // 高さ
      float px = Screen.width / 2 - w / 2;
      float py = Screen.height / 2 - h / 2;

      // フォント描画
      Util.GUILabel(px, py, w, h, "Game Clear!");

      // ※ここから追加
      // ボタンは少し下にずらす
      py += 60;
      if (GUI.Button(new Rect(px, py, w, h), "Back to Title"))
      {
        // タイトル画面にもどる
        Application.LoadLevel("Title");
      }
    }
  }

修正したのは「※ここから追加」の部分で、ボタン表示とタイトル画面に戻る部分を追加しました。
実行して敵を全滅させたら、ボタンを押してタイトル画面に戻れることを確認します。

実行ファイルの作成

最後に、配布用に単体で動作する実行ファイルを作成して終わりにします。
メインメニューから「File > Build Settings...」を選び、ビルド設定ウィンドウを表示します。
02-66.png

今回はPC用にスタンドアロンでビルドを行います。Plattformから「PC, Mac, & Linux Standalone」を選びます。
ただ、ここで出力をする前に、シーンの設定を修正しておきます。というのも、Mainシーンが最初に来ているため、このままだとタイトル画面から始まらないからです。そのためTitleシーンをドラッグして一番上に移動します。
02-66a.png

Titleシーンを一番上にしたら、右下の「Build」ボタンをクリックします。
02-67.png

すると出力先を選ぶダイアログが表示されるので、好きな出力先を選び、ファイル名には「minigame」と入力して保存ボタンを押します(ここではプロジェクトフォルダ内に「Build」フォルダを作成してそこを保存先としています)。「Build」ボタンを押すとビルドが始まり、終わると出力先のフォルダが自動で開きます。
02-68.png

ただ、終了ボタンを用意していないので、フルスクリーンで起動した場合は「Alt+F4」で終了させることになります。では、実行ファイルをダブルクリックして、動作を確認しましょう。
これでミニゲーム作成は完了となります。



宣伝

ここの記事を含むチュートリアルをまとめたKindle本を配信しました。
unity2d.jpg

2Dシューティングゲーム・2Dアクションゲームの作り方を解説した本です。詳細は以下のページで紹介しているので、よろしければ見てみてくださいね。

2dgames_jp
ゲーム作ってます
http://2dgames.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away