Skip to content

第14回 パワーアップカプセル ​

この回でつくるもの:赤い敵を倒すとオレンジのカプセルが出て、取るたびに画面下のゲージが1マスずつ光ります。好きなところで X キーを押すと「決定」できるようにします(パワーアップの中身は次回)。

この回で学ぶこと ​

  • グラディウス式パワーアップの仕組み(ゲージとカーソル)
  • Horizontal Layout Group で UI を横にきれいにならべる
  • Input Actions に新しいアクション(PowerUp)を追加する
  • int の変数で「今どこを指しているか」を覚える
  • switch 文で、番号ごとにちがう処理をする

前回までのふりかえり ​

前回は Prefab Variant で Enemy_Wave(波の敵)と Enemy_Shooter(弾を撃つ敵)を作り、EnemySpawner が配列からランダムに、編隊で敵を出すようにしました。 敵の弾に当たると自機がやられるようにもなりました。

1. グラディウスのパワーアップの仕組み ​

1-1. ゲージとカーソル ​

グラディウスのパワーアップは、ふつうのゲームとちょっとちがいます。カプセルを取っても すぐには強くなりません。

画面の下に、6つのマスがならんだ ゲージ があります。

 [SPEED][MISSILE][DOUBLE][LASER][OPTION][  ?  ]
   0       1        2       3       4      5     ← マスの番号

カプセルを1個取るたびに、光るマス(カーソル)が1つ右へ進みます。

 はじめ        [SPEED][MISSILE][DOUBLE][LASER][OPTION][ ? ]   どこも光っていない
 カプセル1個   [*SPEED*][MISSILE][DOUBLE][LASER][OPTION][ ? ]  SPEED が光る
 カプセル2個   [SPEED][*MISSILE*][DOUBLE][LASER][OPTION][ ? ]  MISSILE が光る
 カプセル3個   [SPEED][MISSILE][*DOUBLE*][LASER][OPTION][ ? ]  DOUBLE が光る

光っているマスで X キー(パワーアップボタン) を押すと、そのパワーアップが手に入ります。決定すると光は消え、また左はしからやりなおしです。

 DOUBLE が光っているときに X キー
   → DOUBLE(2方向に撃てる)を手に入れる!
   → [SPEED][MISSILE][DOUBLE][LASER][OPTION][ ? ]   光が消えて、はじめにもどる

1-2. 何がおもしろいの? ​

  • 「スピードがほしいからすぐ決定する」
  • 「がまんしてカプセルをためて、レーザーまで進めてから決定する」

というように、どれを選ぶか、いつ決定するか をプレイヤーが自分で決められるのがおもしろいところです。 いちばん右(?)の次にカプセルを取ると、また SPEED に戻ります。

マス番号中身(次回作る)
SPEED0自機が速くなる
MISSILE1斜め下にミサイルも撃つ
DOUBLE2前と斜め上の2方向に撃つ
LASER3敵をつらぬくレーザー
OPTION4自機についてくる分身(第16回)
?5シールド(1回だけ身代わりになる)

今回は「カプセル → ゲージが光る → X で決定」までを作ります。決定したときは、まず Console に文字を出すだけにしておきます。

2. PowerUpCapsule プレハブ ​

2-1. カプセルの見た目 ​

  1. Hierarchy で右クリック 3D Object > Capsule を選び、名前を PowerUpCapsule にします
  2. Position を (0, 0, 0)、Scale を (0.4, 0.4, 0.4) にします
  3. Assets/_Game/Materials で右クリック Create > Material、名前を Capsule_Orange にします
  4. Base Map をオレンジにし、Emission にチェックを入れて Emission Map の色もオレンジにします
  5. Capsule_Orange を Scene ビューのカプセルにドラッグします

2-2. 当たり判定 ​

  1. PowerUpCapsule を選び、Capsule Collider の Is Trigger にチェックを入れます
  2. Add Component で Rigidbody を追加し、Use Gravity をオフ、Is Kinematic をオンにします
  3. PowerUpCapsule を Hierarchy から Assets/_Game/Prefabs へドラッグしてプレハブにします
  4. Hierarchy の PowerUpCapsule は消します
  5. シーンを保存します(Ctrl+S、Macは Cmd+S)

先生へ

カプセルを動かすスクリプト PowerUpCapsule は、PowerUpManager を使うので、PowerUpManager を作ったあと(6章)で書きます。先に書くとコンパイルエラーになり、Play できなくなるためです。

3. Enemy_Red とカプセルを落とす処理 ​

3-1. Enemy にカプセルを落とす処理を足す ​

  1. Enemy を開き、次のように書きかえます
csharp
using UnityEngine;

// 敵の基本(移動・HP・やられたとき)
public class Enemy : MonoBehaviour
{
    public float speed = 3f;              // 左へ進む速さ
    public int hp = 1;                    // 体力(第8回)
    public GameObject explosionPrefab;    // 爆発(第9回)
    public AudioClip explosionSound;      // 爆発音(第9回)
    public int score = 100;               // 倒したときの点数(第10回)
    public GameObject capsulePrefab;      // 落とすカプセル(第14回) // ★追加

    bool isDead = false;

    void Update()
    {
        transform.Translate(Vector3.left * speed * Time.deltaTime, Space.World);

        // 画面の左に出たら消す
        if (transform.position.x < -12f)
        {
            Destroy(gameObject);
        }
    }

    // ダメージを受ける(第8回)
    public void TakeDamage(int damage)
    {
        if (isDead) return;

        hp -= damage;
        if (hp <= 0)
        {
            Die();
        }
    }

    void Die()
    {
        isDead = true;

        GameManager.instance.AddScore(score);

        if (explosionPrefab != null)
        {
            Instantiate(explosionPrefab, transform.position, Quaternion.identity);
        }
        if (explosionSound != null)
        {
            AudioSource.PlayClipAtPoint(explosionSound, Camera.main.transform.position);
        }
        if (capsulePrefab != null) // ★追加
        { // ★追加
            Instantiate(capsulePrefab, transform.position, Quaternion.identity); // ★追加
        } // ★追加

        Destroy(gameObject);
    }
}
  1. 保存します

コードの解説

  • capsulePrefab が空(null)の敵は何も落としません。ふつうの敵はそのまま空にしておきます
  • カプセルを入れた敵だけが、やられた場所にカプセルを出します

3-2. Enemy_Red を作る ​

  1. Project ウィンドウで Enemy_Wave を右クリックし、Create > Prefab Variant を選びます
  2. 名前を Enemy_Red にします(Variant の Variant も作れます)
  3. Assets/_Game/Materials で Enemy_Red という赤いマテリアルを作ります(Base Map を赤)
  4. Enemy_Red プレハブをダブルクリックしてプレハブ編集モードにします
  5. Model(またはその子)を選び、Mesh Renderer の Materials の Element 0 に赤いマテリアルをドラッグします
  6. 親の Enemy_Red を選び、Enemy の Capsule Prefab に PowerUpCapsule プレハブをドラッグします
  7. < を押してプレハブ編集モードを終わります

3-3. スポナーに登録する ​

  1. Hierarchy で EnemySpawner を選びます
  2. Enemy Prefabs の + を押し、新しい Element に Enemy_Red をドラッグします
  3. シーンを保存します

ヒント

本物のグラディウスでは「赤い敵の編隊を全部倒すと1個出る」ですが、この教材ではかんたんにするため 赤い敵1機につき1個 出します。

3-4. 動作確認 ​

▶ Play ボタンを押して、赤い敵を倒すとその場所にオレンジのカプセルが出ればOK。 (カプセルはまだ止まったままで、取ることもできません。6章で動くようにします)

4. ゲージ UI を作る ​

4-1. ゲージの入れ物 ​

  1. Hierarchy の Canvas を右クリックし、Create Empty を選びます
  2. 名前を PowerUpGauge にします
  3. Inspector の Rect Transform の左上にあるアンカーの四角をクリックし、bottom-center(下の中央)を選びます
  4. Pos X を 0、Pos Y を 60、Width を 1200、Height を 60 にします
  5. Add Component で Horizontal Layout Group を追加します
  6. Spacing を 10 にします
  7. Control Child Size の Width と Height にチェックを入れます
  8. Child Force Expand の Width と Height にチェックが入っていることを確認します

Horizontal Layout Group は、子どもの UI を 自動で横一列にならべてくれる コンポーネントです。マスの位置を1つずつ手で合わせなくてよくなります。

項目意味
Spacingマスとマスのすきま
Control Child Size子の大きさを、このコンポーネントが決める
Child Force Expand子を引きのばして、すきまなくうめる

4-2. 1つ目のマス ​

  1. PowerUpGauge を右クリックし、UI > Image を選びます。名前を Slot0 にします
  2. Slot0 を右クリックし、UI > Text - TextMeshPro を選びます
  3. できた Text (TMP) を選び、Rect Transform のアンカーの四角をクリックして、Alt(Macは Option)を押しながら右下の stretch(全体に広げる)を選びます
  4. 文字の欄に SPEED と入力します
  5. Font Size を 28、Alignment を左右中央・上下中央にします
  6. Vertex Color(文字の色)を白にします

4-3. 残りの5マス ​

  1. Hierarchy で Slot0 を選び、Ctrl+D(Macは Cmd+D)を5回押して複製します
  2. 複製したものの名前を、上から順に Slot1, Slot2, Slot3, Slot4, Slot5 に変えます
  3. それぞれの子の文字を、MISSILE、DOUBLE、LASER、OPTION、? に変えます
  4. シーンを保存します
Canvas
 ├ ScoreText
 ├ LivesText
 ├ PowerUpGauge   … Horizontal Layout Group
 │  ├ Slot0 … SPEED
 │  ├ Slot1 … MISSILE
 │  ├ Slot2 … DOUBLE
 │  ├ Slot3 … LASER
 │  ├ Slot4 … OPTION
 │  └ Slot5 … ?
 └ GameOverPanel

注意

PowerUpGauge は GameOverPanel より 上(Hierarchy で先)に置いてください。UI は Hierarchy で下にあるものほど手前に描かれます。ゲージが下にあると、ゲームオーバー画面の上にゲージが出てしまいます。

マスの色はスクリプトが決めるので、ここでは白のままで OK です。

5. PowerUp アクションを作る ​

5-1. 新しいアクションを追加する ​

  1. Project ウィンドウで InputSystem_Actions をダブルクリックして開きます(第5回で Attack にキーを足したのと同じ画面です)
  2. 左の Action Maps で Player を選びます
  3. まん中の Actions の右上の + を押します
  4. 新しいアクションの名前を PowerUp にします
  5. 右の Action Properties で Action Type が Button になっていることを確認します

5-2. キーを割り当てる ​

  1. PowerUp の下の <No Binding> を選びます(なければ PowerUp の右の + を押して Add Binding を選びます)
  2. 右の Binding Properties の Path の欄をクリックし、Listen を押してから、キーボードの X キーを押します
  3. 出てきた X [Keyboard] を選びます
  4. もう一度 PowerUp の右の + から Add Binding を選びます
  5. Path で Gamepad の中の Button East を選びます(ゲームパッドの右のボタン)
  6. ウィンドウ上の Save Asset を押して保存します

よくあるまちがい

Save Asset を押し忘れると、アクションが保存されません。スクリプトで FindAction("PowerUp") が見つからず、エラーになります。名前の大文字・小文字(PowerUp)もぴったり合わせましょう。

6. PowerUpManager と PowerUpCapsule ​

6-1. switch 文とは ​

switch(スイッチ)文は、番号によって、ちがう処理に分かれる 書き方です。エレベーターのボタンのようなものです。

csharp
switch (index)
{
    case 0:      // index が 0 のとき
        // ...
        break;   // ここで終わり
    case 1:      // index が 1 のとき
        // ...
        break;
}

if (index == 0) {...} else if (index == 1) {...} と書くのと同じですが、番号がたくさんあるときは switch の方が読みやすくなります。 それぞれの case の最後に break; が必要 です。

6-2. PowerUpManager スクリプト ​

  1. Assets/_Game/Scripts に PowerUpManager を作り、次のように書きます
csharp
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

// グラディウス風パワーアップゲージ
public class PowerUpManager : MonoBehaviour
{
    // ゲージのマス(0:SPEED 1:MISSILE 2:DOUBLE 3:LASER 4:OPTION 5:?)
    public Image[] gaugeSlots;
    public Color normalColor = new Color(0.2f, 0.2f, 0.5f);
    public Color selectedColor = Color.yellow;

    int cursor = -1;          // 光っているマス(-1 はどこも光っていない)
    InputAction powerUpAction;

    void Start()
    {
        powerUpAction = InputSystem.actions.FindAction("PowerUp");
        UpdateGauge();
    }

    void Update()
    {
        if (powerUpAction.WasPressedThisFrame() && cursor >= 0)
        {
            Activate(cursor);
            cursor = -1;
            UpdateGauge();
        }
    }

    // カプセルを取ったとき:光るマスを1つ右へ
    public void AddCapsule()
    {
        cursor++;
        if (cursor >= gaugeSlots.Length)
        {
            cursor = 0;
        }
        UpdateGauge();
    }

    void Activate(int index)
    {
        switch (index)
        {
            case 0: // SPEED UP
                Debug.Log("SPEED UP");
                break;
            case 1: // MISSILE
                Debug.Log("MISSILE");
                break;
            case 2: // DOUBLE
                Debug.Log("DOUBLE");
                break;
            case 3: // LASER
                Debug.Log("LASER");
                break;
            case 4: // OPTION
                Debug.Log("OPTION");
                break;
            case 5: // ?(シールド)
                Debug.Log("SHIELD");
                break;
        }
    }

    void UpdateGauge()
    {
        for (int i = 0; i < gaugeSlots.Length; i++)
        {
            if (i == cursor)
            {
                gaugeSlots[i].color = selectedColor;
            }
            else
            {
                gaugeSlots[i].color = normalColor;
            }
        }
    }
}
  1. 保存します

コードの解説

部分意味
Image[] gaugeSlots6つのマス(Image)を入れる配列
int cursor = -1;今光っているマスの番号。-1 は「どこも光っていない」
WasPressedThisFrame()ボタンを 押した瞬間だけ true。押しっぱなしでは何回も決定しない
cursor >= 0どこかが光っているときだけ決定できる
cursor++;1つ右へ進める(cursor = cursor + 1; と同じ)
if (cursor >= gaugeSlots.Length) cursor = 0;右はし(5)の次は、左はし(0)に戻る
UpdateGauge()光っているマスだけ黄色、ほかは紺色にぬる

cursor の動きを表にすると、こうなります。

できごとcursor光るマス
はじめ-1なし
カプセル1個0SPEED
カプセル2個1MISSILE
X キー-1なし(MISSILE を決定)
カプセルを6個続けて取る0→1→2→3→4→5?
もう1個0SPEED(一周した)

6-3. PowerUpCapsule スクリプト ​

  1. Assets/_Game/Scripts に PowerUpCapsule を作り、次のように書きます
csharp
using UnityEngine;

// パワーアップカプセル
public class PowerUpCapsule : MonoBehaviour
{
    public float speed = 1.5f;

    void Start()
    {
        Destroy(gameObject, 15f);
    }

    void Update()
    {
        transform.Translate(Vector3.left * speed * Time.deltaTime, Space.World);
        transform.Rotate(0f, 180f * Time.deltaTime, 0f);   // くるくる回す
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            PowerUpManager powerUp = other.GetComponent<PowerUpManager>();
            if (powerUp != null)
            {
                powerUp.AddCapsule();
            }
            Destroy(gameObject);
        }
    }
}
  1. 保存します

コードの解説

  • Space.World を付けて、回転していても 画面の左 へゆっくり流れるようにしています
  • transform.Rotate(0f, 180f * Time.deltaTime, 0f) … 1秒に180度、Y軸のまわりに回ります
  • 自機(Player タグ)に当たったら、自機の PowerUpManager の AddCapsule() を呼んでから、カプセル自身を消します

6-4. Inspector でつなぐ ​

  1. Project ウィンドウで PowerUpCapsule プレハブを選び、Add Component で PowerUpCapsule を付けます
  2. Hierarchy で Player を選び、Add Component で PowerUpManager を付けます
  3. Gauge Slots の右下の + を6回押して、要素を6つにします
  4. Hierarchy から Slot0 〜 Slot5 を、Element 0 〜 Element 5 に順番にドラッグします
  5. シーンを保存します

ヒント

Inspector の右上にある小さな錠前(じょうまえ)のアイコンを押すと、ほかのオブジェクトを選んでも Inspector が Player のままになります。6つドラッグするときに便利です。ドラッグし終わったら、もう一度押して鍵を外しましょう。

6-5. 動作確認 ​

▶ Play ボタンを押して、次のようになればOK。

  1. 赤い敵を倒すと、カプセルがくるくる回りながら左へ流れる
  2. カプセルを取ると、ゲージの SPEED が黄色く光る
  3. もう1個取ると、光が MISSILE に移る
  4. X キーを押すと光が消え、Console に MISSILE と表示される

テストのコツ

赤い敵がなかなか出ないときは、EnemySpawner の Enemy Prefabs を一時的に Enemy_Red だけにすると、テストが楽になります。テストが終わったら元に戻しましょう。

チャレンジ課題 ​

  • Normal Color と Selected Color を、自分の好きな色に変えてみよう(Inspector で変えられます)
  • カプセルを取ったときに音が鳴るようにしてみよう(ヒント: 第9回の AudioSource.PlayClipAtPoint)
  • カプセルの回り方を変えてみよう(Rotate の数字を変える、X軸で回す など)
  • 決定したときに、文字だけでなくスコアも 500 点入るようにしてみよう

つまずきポイント ​

こんなときはここを確認
カプセルを取ってもゲージが光らないPowerUpManager が Player に付いているか。カプセルの Is Trigger と Rigidbody。Player のタグが Player か
Play すると NullReferenceException(PowerUpManager の Update の行)Input Actions に PowerUp を作って Save Asset したか。名前のつづり
IndexOutOfRangeException が出るGauge Slots の6つ全部に Slot が入っているか
X を押しっぱなしにすると困る/何も起きないWasPressedThisFrame と書いているか。ゲージがどこか光っているか
ゲージが横にならばない/重なるPowerUpGauge に Horizontal Layout Group が付いているか。Slot が PowerUpGauge の 子 になっているか
赤い敵を倒してもカプセルが出ないEnemy_Red の Capsule Prefab にプレハブが入っているか

まとめ ​

  • グラディウスのパワーアップは「カプセルでカーソルが進み、好きなところで決定する」仕組み
  • Horizontal Layout Group で UI を自動で横にならべられる
  • int cursor で「今どこを指しているか」を覚え、switch で番号ごとに処理を分ける
  • 次回は、Debug.Log の代わりに本当のパワーアップを作ります