ダークモード
第7回 敵が現れる
この回でつくるもの:画面の右の外から、敵が一定の時間ごとに出てきて、左へ進んでくるようにします。出てくる高さは毎回ランダムです。
この回で学ぶこと
- 自機と同じ「親+子(モデル)」の形で敵を作る
- 画面の外に出たオブジェクトを消す
- コルーチン(
IEnumerator、yield return、WaitForSeconds)で「待ってから動く」処理を書く while (true)でくり返すRandom.Rangeでランダムな数を作る
前回までのふりかえり
前回は、Particle System で遠くの星と近くの星を作り、速さを変えて流しました。岩も BackgroundScroller で流れています。 宇宙を飛んでいる感じが出てきましたね。 今回は、いよいよ敵を出します。
1. 敵を作る
1-1. 親のオブジェクトを作る
自機と同じように、「親(空のオブジェクト)」+「子(見た目のモデル)」の2段で作ります。
- Hierarchy の何もないところを右クリックして Create Empty を選びます。
- 名前を
Enemy_Straightにします(まっすぐ進む敵、という意味です)。 - Inspector の Transform で Position を (5, 0, 0) にします(作業しやすい場所に置いておきます)。
- Rotation が (0, 0, 0)、Scale が (1, 1, 1) であることを確かめます。
1-2. 見た目のモデルを入れる
- Project ウィンドウで
Assets/ThirdParty/KenneySpaceKitを開きます。 craft_racerを、Hierarchy のEnemy_Straightの上へドラッグ&ドロップします。Enemy_Straightの子になったモデルの名前をModelに変えます。Modelを選び、Position を (0, 0, 0) にします。- Rotation の Y を 90 か -90 にして、機首が 左(-X) を向くようにします。
- Scale を調整して、横幅が自機と同じくらい(約1.5)になるようにします。
ヒント
ファイル名がちがったら、Project ウィンドウのプレビューを見て好きなモデルを選んでOKです。自機とは形や色がちがうものを選ぶと、見分けやすくなります。
注意
向きを直すのは 子の Model の Rotation です。親の Enemy_Straight の Rotation は (0, 0, 0) のままにしておきます。親を回すと、スクリプトで動かしたときに思わぬ方向へ進むことがあります。
1-3. 色を変える(好みで)
- Project ウィンドウで
Assets/_Game/Materialsを開きます。 - 右クリックして Create > Material を選び、名前を
Mat_Enemyにします。 - Base Map の色を赤やむらさきなど、敵らしい色にします。
Mat_Enemyを Scene ビューの敵のモデルへドラッグ&ドロップします。- Ctrl+S(Macは Cmd+S)でシーンを保存します。
2. Enemy スクリプト
2-1. スクリプトを作る
- Project ウィンドウで
Assets/_Game/Scriptsを開きます。 - 右クリックして Create > Scripting > MonoBehaviour Script を選びます。
- 名前を
Enemyにして Enter を押します。 - ダブルクリックしてエディタで開きます。
2-2. コードを書く
中身をすべて消して、次のコードを書きます。
csharp
using UnityEngine;
// 敵の基本(移動)
public class Enemy : MonoBehaviour
{
public float speed = 3f; // 左へ進む速さ
void Update()
{
transform.Translate(Vector3.left * speed * Time.deltaTime, Space.World);
// 画面の左に出たら消す
if (transform.position.x < -12f)
{
Destroy(gameObject);
}
}
}書けたら保存して Unity にもどります。
2-3. コードの解説
| 行 | 意味 |
|---|---|
public float speed = 3f; | 左へ進む速さ。1秒に3進む |
transform.Translate(Vector3.left * ..., Space.World); | 世界の左へ動かす(前回の BackgroundScroller と同じ) |
if (transform.position.x < -12f) | もし x が -12 より左になったら |
Destroy(gameObject); | 自分自身をすぐに消す |
- 画面の左はしは約 -8.9 です。-12 はそれより外なので、プレイヤーには消える瞬間が見えません。
- 消さないと、画面の外にいつまでも敵がたまっていき、ゲームが重くなります。弾を2秒で消したのと同じ理由です。
- 第5回の
Destroy(gameObject, lifeTime)は「時間がたったら消す」でしたが、今回は数字がないので「すぐに消す」になります。
2-4. 敵に付けて試す
- Hierarchy で
Enemy_Straight(親)を選びます。 - Add Component をクリックして、
Enemyを検索して付けます。 - ▶ Play ボタンを押します。
敵が左へ進んでいき、画面の外へ出たあとで Hierarchy から Enemy_Straight が消えればOKです。確かめたら ▶ を押して止めます。
3. プレハブにする
3-1. プレハブを作る
敵もたくさん出したいので、弾と同じようにプレハブにします。
- Project ウィンドウで
Assets/_Game/Prefabsを開きます。 - Hierarchy の
Enemy_Straightを、Prefabsフォルダへドラッグ&ドロップします。 - Project に
Enemy_Straightプレハブができたことを確かめます。
3-2. シーンの敵を消す
- Hierarchy の
Enemy_Straightを選びます。 - Delete キー(Macは Cmd+Delete)を押して消します。
- Ctrl+S(Macは Cmd+S)でシーンを保存します。
ヒント
プレハブをあとで直したいときは、Project の Enemy_Straight をダブルクリックします。プレハブモード(プレハブだけを編集する画面)になります。終わったら、Hierarchy の左上の <(矢印)を押してシーンにもどります。
4. EnemySpawner(敵を出す係)
4-1. コルーチンとは
「1.5秒待つ → 敵を出す → また1.5秒待つ → 敵を出す…」という処理を作りたいとします。 Update は毎フレーム呼ばれるので、「ここで1.5秒待って」と書くことができません。
そこで使うのが コルーチン です。コルーチンは「とちゅうで一時停止して、時間がたったら続きから動く」ことができる特別な関数です。
csharp
IEnumerator SpawnLoop()
{
while (true)
{
yield return new WaitForSeconds(interval); // ここで interval 秒、一時停止
// 時間がたったら、ここから続きが動く
}
}| 書き方 | 意味 |
|---|---|
IEnumerator | 「これはコルーチンです」というしるし(ふつうの関数の void の代わり) |
yield return new WaitForSeconds(秒数); | 指定した秒数だけ一時停止する |
StartCoroutine(SpawnLoop()); | コルーチンを動かし始める |
while (true) { } | { } の中をずっとくり返す |
while (条件) は「条件が正しいあいだ、くり返す」という意味です。true(いつでも正しい)と書くと、ゲームが終わるまでずっとくり返します。
よくあるまちがい
while (true) の中に yield return を書き忘れると、一時停止せずに一瞬で無限にくり返してしまい、Unity が固まって動かなくなります。while (true) を書いたら、中に yield return があるか必ず確かめましょう。もし固まったら、先生に知らせてください。
4-2. スクリプトを作る
- Project ウィンドウで
Assets/_Game/Scriptsを開きます。 - 右クリックして Create > Scripting > MonoBehaviour Script を選びます。
- 名前を
EnemySpawnerにして Enter を押します。 - ダブルクリックしてエディタで開き、中身をすべて消して次のコードを書きます。
csharp
using System.Collections;
using UnityEngine;
// 敵を一定時間ごとに出す
public class EnemySpawner : MonoBehaviour
{
public GameObject enemyPrefab; // 出す敵
public float interval = 1.5f; // 出す間隔(秒)
public float minY = -4f; // 出す高さの範囲
public float maxY = 4f;
void Start()
{
StartCoroutine(SpawnLoop());
}
IEnumerator SpawnLoop()
{
while (true)
{
yield return new WaitForSeconds(interval);
// ランダムに高さを決める
float y = Random.Range(minY, maxY);
Vector3 pos = new Vector3(transform.position.x, y, 0f);
Instantiate(enemyPrefab, pos, Quaternion.identity);
}
}
}書けたら保存して Unity にもどります。
4-3. コードの解説
| 行 | 意味 |
|---|---|
using System.Collections; | IEnumerator を使うために必要な1行 |
public GameObject enemyPrefab; | 出す敵のプレハブを入れる箱 |
public float interval = 1.5f; | 敵を出す間隔(秒) |
public float minY / maxY | 敵を出す高さの、いちばん下といちばん上 |
StartCoroutine(SpawnLoop()); | ゲームが始まったら、コルーチンを動かし始める |
Random.Range(minY, maxY) | -4 から 4 のあいだの、ランダムな数を1つ作る |
new Vector3(transform.position.x, y, 0f) | x は EnemySpawner の位置、y はランダム、z は 0 |
Instantiate(enemyPrefab, pos, Quaternion.identity); | その場所に敵を作る(第5回の弾と同じ) |
Random.Rangeは、使うたびにちがう数を返します。だから敵が出てくる高さが毎回変わります。- x に
transform.position.xを使っているので、EnemySpawner を置いた場所の x から敵が出てきます。
先生へ
Random.Range は、float(小数)を渡すと最大値もふくむ範囲の小数を、int(整数)を渡すと最大値を ふくまない 整数を返します。第13回で配列から敵を選ぶときに整数版を使うので、そのときに説明するとスムーズです。
5. EnemySpawner を置く
5-1. 置き場所を作る
- Hierarchy の何もないところを右クリックして Create Empty を選びます。
- 名前を
EnemySpawnerにします。 - Inspector の Transform で Position を (11, 0, 0) にします。
x = 11 は、画面の右はし(約 8.9)より外です。こうすると、敵が画面の外から入ってくるように見えます。
5-2. スクリプトを付けてプレハブを入れる
- Hierarchy で
EnemySpawnerを選んだまま、Add Component をクリックします。 EnemySpawnerを検索して付けます。- Project の
PrefabsフォルダにあるEnemy_Straightを、Inspector の Enemy Prefab の欄へドラッグ&ドロップします。 - Interval が 1.5、Min Y が -4、Max Y が 4 になっていることを確かめます。
- Ctrl+S(Macは Cmd+S)でシーンを保存します。
注意
Enemy Prefab には、必ず Project の Prefabs フォルダの Enemy_Straight を入れます。シーンの敵はもう消してあるので、Hierarchy にはありません。
5-3. 動作確認
▶ Play ボタンを押して、次のようになればOKです。
- 1.5秒ごとに、画面の右の外から敵が出てくる
- 敵が出てくる高さが毎回ちがう
- 敵が左へ進み、画面の外へ出ると Hierarchy から消える(
Enemy_Straight(Clone)が増えたり減ったりする)
まだ当たり判定がないので、弾を当てても敵は倒れず、自機にぶつかってもすり抜けます。次の回で作ります。
チャレンジ課題
- Interval を 0.5 にして、敵がたくさん出る「はげしいステージ」にしてみよう
- プレハブの
Enemy_Straightを開き、Speed を変えて、速い敵・おそい敵を試してみよう - Min Y と Max Y を変えて、画面の上半分だけから敵が出るようにしてみよう
- ヒント:
Random.Rangeを使って、敵の速さもランダムにできるかな?(EnemyのStartでspeed = Random.Range(2f, 5f);など)
つまずきポイント
| こんなときは | ここを確認 |
|---|---|
| 敵が出てこない | EnemySpawner に EnemySpawner スクリプトが付いているか。Enemy Prefab の欄が空になっていないか |
| 敵が右へ進む・上下に進む | 親の Enemy_Straight の Rotation が (0, 0, 0) か。Vector3.left と書いたか |
| 敵がうしろ向き(機首が右) | 子の Model の Rotation Y を 90 と -90 で入れかえてみる |
| Unity が固まった | while (true) の中に yield return new WaitForSeconds(interval); を書いたか |
IEnumerator に赤い波線が出る | 1行目に using System.Collections; を書いたか |
| 敵が画面の外へ出ても消えない | if (transform.position.x < -12f) の - を忘れていないか |
まとめ
- 敵も「親+子(モデル)」で作り、向きは子の Rotation で直す
- 画面の外へ出たものは
Destroyで消して、ゲームを軽く保つ - コルーチンを使うと「待ってから動く」をくり返す処理が書ける
Random.Rangeでランダムな数を作ると、毎回ちがう遊びになる