【Unity】2Dシューティングゲームの作り方⑥:スクロールと敵の攻撃処理

Unity

こんにちは、ともくんのゲーム作り部屋にようこそ!

このページでは、Unityを使った2Dシューティングゲームの作り方6回目として、画面のスクロールと敵の攻撃処理について紹介しています。

前回、敵にダメージを与える処理を作っていきました。

今度は、敵側の移動として画面を自動スクロールさせて、敵が攻撃してくる処理を作っていきましょう。

敵の弾の発射は、プレイヤーの弾の発射方法と作り方は似ていて、説明が少しかぶっている部分もありますが、発射間隔を設定したりとまた新しい機能が出てきます。

そこで、ステージを移動させて画面をスクロールさせる処理と、敵の攻撃処理までをまとめていきます。

この記事を書いた人

ゲーム作りを学び始めた一児のパパです。
このブログは、子供から「ゲームを作ってみたい!」と言われ、非プログラマーでゲーム作りをしたことない僕が、ゲーム作りの本を読んで独学でゲーム開発を学んでいるブログです。
同じように初めてゲーム作りをしている方と一緒に学んでいけるようなブログに出来たらいいなと思っています。
また、「このコードはおかしい」とか「もっと良い書き方があるよ!」などあれば、どんどん指摘して頂けると助かります。

ステージを移動させる処理を作る

ここでは、ゲーム画面をスクロールさせて、ステージが移動している処理を作っていきましょう。

シューティングゲームで画面をスクロールさせる方法として、

の大きく2つの方法があります。

今回は、カメラの位置は固定したままで、敵のオブジェクトを動かすことで、ゲーム画面を動かしていきます。

まずは、ステージを移動させるための空オブジェクトを作成していきます。

空オブジェクトの作成は、ヒエラルキーウィンドウの「+」ボタンから「Create Empty」で作成します。

この空オブジェクトの名前を「stage」として、オブジェクトの座標を(0, 0, 0)にしておきます。

そして、敵をこの空オブジェクトの子オブジェクトにして、階層を作ります。

次に、新しくスクリプトを作成して、この空オブジェクトに対してアタッチしてあげます。

今回は、ステージを動かすためのスクリプトになるので、「StageController」としています。

using UnityEngine;

public class StageController : MonoBehaviour
{
    public float stageSpeed = 0.03f;    // ステージの速さを決める変数

    void Update()
    {
        transform.Translate(0, -stageSpeed, 0);  // ステージの座標を動かす処理
    }
}

5行目でスクロール速度を決めるstageSpeed変数を作成し、9行目でtransform.Translateでステージを下に向けて動かす処理を書いています。

これでゲームを実行してみると、

敵のオブジェクトが下に移動してきて、スクロールできているのが確認できました。

今後、追加する敵オブジェクトも、すべてこのstageオブジェクトの子オブジェクトに入れてあげることで、スクロールさせることができます。

なお、変数やスクリプトについては、以下の記事でも詳しく解説していますので、参考にしてみてください。

敵の攻撃処理を作る

ステージを移動させることで、敵を移動することができたので、次に敵の攻撃処理を作っていきます。

敵の攻撃は、プレイヤーと異なり特定の間隔で弾を自動的に発射するようにして、プレイヤーに向かって弾が飛んでいくように作っていこうと思います。

まずは、プレイヤーの弾を発射した時と同じように、敵の弾をゲーム内に配置しておきます。

この配置する場所は、stageオブジェクト外に配置しておきましょう。

そして、敵の弾を動かす処理を作るスクリプトを新たに作成して、弾のオブジェクトにアタッチしておきます。

今回は、このスクリプト名を「EnemyBulletMove」としています。

using UnityEngine;

public class EnemyBulletMove : MonoBehaviour
{
    float enemyBulletSpeed = 0.08f; // 弾のスピードの変数
    Vector2 differencePosition; // プレイヤーと敵の弾の座標の位置の差
    float enemyBulletTime;  //  座標の距離÷弾のスピードの値を代入

    void Start()
    {
        Vector2 playerPosition = GameObject.Find("player").transform.position;  // プレイヤーの座標を取得する
        Vector2 enemyBulletPosition = this.transform.position;  // 敵の弾の座標を取得する
        differencePosition = playerPosition - enemyBulletPosition;  // それぞれの座標の差を代入する
        enemyBulletTime = Vector2.Distance(playerPosition, enemyBulletPosition) / enemyBulletSpeed; // 座標の距離÷弾のスピードを計算
    }
    
    void Update()
    {
        transform.Translate(differencePosition.x / enemyBulletTime, differencePosition.y / enemyBulletTime, 0); // プレイヤーに向かって弾が移動する処理
        if (this.transform.position.x > 4 ||    // X座標が4以上になった場合
            this.transform.position.x < -4 ||   // X座標が-4以下になった場合
            this.transform.position.y > 5 ||    // Y座標が5以上になった場合
            this.transform.position.y < -5)     // Y座標が-5以下になった場合
        {
            Destroy(gameObject);    // 弾を破壊する
        }
    }
}

5行目で敵の弾のスピードを決める変数として、enemyBulletSpeedを宣言しています。

6行目でVector2型の変数differencePositionを設定していて、11~13行目でプレイヤーと敵の弾の座標の位置をtransform.positionで取得して、その差を代入しています。

このdifferencePositionは、その敵の弾をどれだけ移動させたらプレイヤーに当たるかを計算した座標ということになります。

また、7行目でfloat型の変数enemyBulletTimeを設定していて、14行目で何回移動させたらプレイヤーに当たるかという計算結果を代入しています。

この14行目は、Distanceメソッドを使って2つの座標の距離を取得して、それを弾のスピードの値で割っています。

Distanceメソッドは、引数にVector型の座標2つをカンマ(,)で繋げて渡すことで、2つの座標の距離を取得することができます。

Vector2.Distance(座標A, 座標B)

座標の位置を取得するのは、弾が発射されたタイミングで取得したいので、ここまではStartメソッド内に書いています。

そして、19行目では、このdifferencePositionenemyBulletTimeの値を割って、transform.Translateのそれぞれの座標に設定して弾を移動させる処理を作っています。

この計算によって、発射されたタイミングで、プレイヤーのいる座標に向かって弾がまっすぐ飛んでいくという処理を行うことができます。

敵の弾は、カメラ外にいってもそのまま飛び続けることになるので、カメラ外の座標にいったら弾を破壊するように、20行目以降で座標の条件を指定してDestroyメソッドで消去しています。

少し難しい計算になってしまいましたが、これでゲームを実行してみましょう。

ちゃんとプレイヤーに向かって弾が飛んでいますね。

弾をPrefab化して敵から発射させる

弾の動きを作ることができたら、プレイヤーの弾と同じく複製していくことになるのでPrefab化していきます。

敵の弾のオブジェクトをアセットフォルダ内にドラッグ&ドロップすることで、Prefab化することができます。

今回は、敵の弾のPrefabなので、「EnemyBullet」というPrefab名で保存しています。

Prefab化ができたら、一旦ヒエラルキーウィンドウから敵の弾のオブジェクトを削除しておきます。

次に、空オブジェクトを作成して、敵オブジェクトを親として設定しておきます。

この空オブジェクトの名前は、敵の弾の発射位置を決めることになるので「enemyBulletPoint」という名前にしています。

そして、この空オブジェクトの座標を敵の弾を発射させたい場所に移動させておきます。

次にenemyBulletPointから、弾を発射するためのスクリプトを作成していきます。

ここでは、「EnemyShoot」というスクリプト名で作成しています。

using UnityEngine;

public class EnemyShoot : MonoBehaviour
{
    public GameObject enemyBullet; // publicを付けてGameObject型の変数を宣言する
    float span = 2.0f;  // 弾を撃つ間隔を指定
    float delta = 2.0f; // 加算用の変数を宣言

    void Update()
    {
        this.delta += Time.deltaTime;   // フレーム間の差をdelta変数に加算している
        if (this.delta > this.span) // delta変数がspanで指定した間隔を上回った場合
        {
            this.delta = 0; // 0に戻す処理
            GameObject cloneObject = Instantiate(enemyBullet, this.transform.position, Quaternion.identity);  // 弾を表示させて座標を移動させる処理
            cloneObject.name = "EnemyBullet";   // Cloneの名前を削除する処理
        }
    }
}

5行目でGameObject型の変数enemyBulletをpublicのアクセス修飾子を付けて宣言していて、この変数にPrefabファイルを後ほど代入します。

弾を発射する間隔を調整するために、6・7行目でfloat型のspandeltaという変数を宣言、11行目でTime.deltaTimeを使いフレーム間の差を加算して、12行目でdeltaがspanを上回った際に発射処理を行うということを書いています。

Time.deltaTimeは、前のフレームからの経過時間を表している変数で、フレーム間の秒数を表していて、これを足し上げてspanで指定した値である2秒を上回ると、発射されるようになっています。

簡単に言えば、ここでは2秒に1回敵が発射する処理を作っているということになります。

実際の発射する処理は、15行目でInstantiate関数で弾を表示させるという処理を書いています。

第二引数に、「this.transform.position」でこの座標がアタッチしているオブジェクトの座標を指定しています。

また、複製された際の(Clone)表記を削除しておくために、16行目でname変数の書き換えを行っています。

スクリプトが書き終わったら、空オブジェクトを選択してインスペクターウィンドウのEnemyShootから、変数enemyBulletの欄にPrefab化した敵の弾をドラッグ&ドロップしてきます。

これでゲームを実行して確認してみましょう。

設定した間隔ごとに、弾が自動的に発射するという処理を作ることができました。

まとめ

このページでは、ステージをスクロールさせて敵が弾を発射するという処理を作っていきました。

敵が弾を発射させる処理は、プレイヤーが弾を発射する処理と同じような仕組みですが、自動的に発射させるため、Time.deltaTimeという変数を使う必要がでてきます。

プレイヤーに向かって弾を飛ばしたり、発射間隔を作ったり少々ややこしいですが、一度作ってしまえばそのあとが非常に楽になるので、頑張って作っていきましょう。

次は、プレイヤー側に弾が当たった際の当たり判定処理を作っていきます。

最後までお読みいただきまして、ありがとうございました!

コメント