ダークモード
第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 に戻ります。
| マス | 番号 | 中身(次回作る) |
|---|---|---|
| SPEED | 0 | 自機が速くなる |
| MISSILE | 1 | 斜め下にミサイルも撃つ |
| DOUBLE | 2 | 前と斜め上の2方向に撃つ |
| LASER | 3 | 敵をつらぬくレーザー |
| OPTION | 4 | 自機についてくる分身(第16回) |
| ? | 5 | シールド(1回だけ身代わりになる) |
今回は「カプセル → ゲージが光る → X で決定」までを作ります。決定したときは、まず Console に文字を出すだけにしておきます。
2. PowerUpCapsule プレハブ
2-1. カプセルの見た目
- Hierarchy で右クリック 3D Object > Capsule を選び、名前を
PowerUpCapsuleにします - Position を (0, 0, 0)、Scale を (0.4, 0.4, 0.4) にします
Assets/_Game/Materialsで右クリック Create > Material、名前をCapsule_Orangeにします- Base Map をオレンジにし、Emission にチェックを入れて Emission Map の色もオレンジにします
Capsule_Orangeを Scene ビューのカプセルにドラッグします
2-2. 当たり判定
PowerUpCapsuleを選び、Capsule Collider の Is Trigger にチェックを入れます- Add Component で
Rigidbodyを追加し、Use Gravity をオフ、Is Kinematic をオンにします PowerUpCapsuleを Hierarchy からAssets/_Game/Prefabsへドラッグしてプレハブにします- Hierarchy の
PowerUpCapsuleは消します - シーンを保存します(Ctrl+S、Macは Cmd+S)
先生へ
カプセルを動かすスクリプト PowerUpCapsule は、PowerUpManager を使うので、PowerUpManager を作ったあと(6章)で書きます。先に書くとコンパイルエラーになり、Play できなくなるためです。
3. Enemy_Red とカプセルを落とす処理
3-1. Enemy にカプセルを落とす処理を足す
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);
}
}- 保存します
コードの解説
capsulePrefabが空(null)の敵は何も落としません。ふつうの敵はそのまま空にしておきます- カプセルを入れた敵だけが、やられた場所にカプセルを出します
3-2. Enemy_Red を作る
- Project ウィンドウで
Enemy_Waveを右クリックし、Create > Prefab Variant を選びます - 名前を
Enemy_Redにします(Variant の Variant も作れます) Assets/_Game/MaterialsでEnemy_Redという赤いマテリアルを作ります(Base Map を赤)Enemy_Redプレハブをダブルクリックしてプレハブ編集モードにしますModel(またはその子)を選び、Mesh Renderer の Materials の Element 0 に赤いマテリアルをドラッグします- 親の
Enemy_Redを選び、Enemy の Capsule Prefab にPowerUpCapsuleプレハブをドラッグします - < を押してプレハブ編集モードを終わります
3-3. スポナーに登録する
- Hierarchy で
EnemySpawnerを選びます - Enemy Prefabs の + を押し、新しい Element に
Enemy_Redをドラッグします - シーンを保存します
ヒント
本物のグラディウスでは「赤い敵の編隊を全部倒すと1個出る」ですが、この教材ではかんたんにするため 赤い敵1機につき1個 出します。
3-4. 動作確認
▶ Play ボタンを押して、赤い敵を倒すとその場所にオレンジのカプセルが出ればOK。 (カプセルはまだ止まったままで、取ることもできません。6章で動くようにします)
4. ゲージ UI を作る
4-1. ゲージの入れ物
- Hierarchy の
Canvasを右クリックし、Create Empty を選びます - 名前を
PowerUpGaugeにします - Inspector の Rect Transform の左上にあるアンカーの四角をクリックし、bottom-center(下の中央)を選びます
- Pos X を 0、Pos Y を 60、Width を 1200、Height を 60 にします
- Add Component で
Horizontal Layout Groupを追加します - Spacing を 10 にします
- Control Child Size の Width と Height にチェックを入れます
- Child Force Expand の Width と Height にチェックが入っていることを確認します
Horizontal Layout Group は、子どもの UI を 自動で横一列にならべてくれる コンポーネントです。マスの位置を1つずつ手で合わせなくてよくなります。
| 項目 | 意味 |
|---|---|
| Spacing | マスとマスのすきま |
| Control Child Size | 子の大きさを、このコンポーネントが決める |
| Child Force Expand | 子を引きのばして、すきまなくうめる |
4-2. 1つ目のマス
PowerUpGaugeを右クリックし、UI > Image を選びます。名前をSlot0にしますSlot0を右クリックし、UI > Text - TextMeshPro を選びます- できた
Text (TMP)を選び、Rect Transform のアンカーの四角をクリックして、Alt(Macは Option)を押しながら右下の stretch(全体に広げる)を選びます - 文字の欄に
SPEEDと入力します - Font Size を 28、Alignment を左右中央・上下中央にします
- Vertex Color(文字の色)を白にします
4-3. 残りの5マス
- Hierarchy で
Slot0を選び、Ctrl+D(Macは Cmd+D)を5回押して複製します - 複製したものの名前を、上から順に
Slot1,Slot2,Slot3,Slot4,Slot5に変えます - それぞれの子の文字を、
MISSILE、DOUBLE、LASER、OPTION、?に変えます - シーンを保存します
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. 新しいアクションを追加する
- Project ウィンドウで
InputSystem_Actionsをダブルクリックして開きます(第5回で Attack にキーを足したのと同じ画面です) - 左の Action Maps で
Playerを選びます - まん中の Actions の右上の + を押します
- 新しいアクションの名前を
PowerUpにします - 右の Action Properties で Action Type が Button になっていることを確認します
5-2. キーを割り当てる
PowerUpの下の<No Binding>を選びます(なければPowerUpの右の + を押して Add Binding を選びます)- 右の Binding Properties の Path の欄をクリックし、Listen を押してから、キーボードの X キーを押します
- 出てきた
X [Keyboard]を選びます - もう一度
PowerUpの右の + から Add Binding を選びます - Path で
Gamepadの中の Button East を選びます(ゲームパッドの右のボタン) - ウィンドウ上の 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 スクリプト
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;
}
}
}
}- 保存します
コードの解説
| 部分 | 意味 |
|---|---|
Image[] gaugeSlots | 6つのマス(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個 | 0 | SPEED |
| カプセル2個 | 1 | MISSILE |
| X キー | -1 | なし(MISSILE を決定) |
| カプセルを6個続けて取る | 0→1→2→3→4→5 | ? |
| もう1個 | 0 | SPEED(一周した) |
6-3. PowerUpCapsule スクリプト
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);
}
}
}- 保存します
コードの解説
Space.Worldを付けて、回転していても 画面の左 へゆっくり流れるようにしていますtransform.Rotate(0f, 180f * Time.deltaTime, 0f)… 1秒に180度、Y軸のまわりに回ります- 自機(Player タグ)に当たったら、自機の
PowerUpManagerのAddCapsule()を呼んでから、カプセル自身を消します
6-4. Inspector でつなぐ
- Project ウィンドウで
PowerUpCapsuleプレハブを選び、Add Component でPowerUpCapsuleを付けます - Hierarchy で
Playerを選び、Add Component でPowerUpManagerを付けます - Gauge Slots の右下の + を6回押して、要素を6つにします
- Hierarchy から
Slot0〜Slot5を、Element 0 〜 Element 5 に順番にドラッグします - シーンを保存します
ヒント
Inspector の右上にある小さな錠前(じょうまえ)のアイコンを押すと、ほかのオブジェクトを選んでも Inspector が Player のままになります。6つドラッグするときに便利です。ドラッグし終わったら、もう一度押して鍵を外しましょう。
6-5. 動作確認
▶ Play ボタンを押して、次のようになればOK。
- 赤い敵を倒すと、カプセルがくるくる回りながら左へ流れる
- カプセルを取ると、ゲージの SPEED が黄色く光る
- もう1個取ると、光が MISSILE に移る
- 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 の代わりに本当のパワーアップを作ります