【Unity】学習用シューティング

2023年3月7日

次のようなイメージで考えてみましょう

企画・仕様

ゲームの流れ、登場するキャラクタなどを考えます
今回は、おおまかな企画を作り、サンプルとして、一部の制作を行います

ゲームのタイトル

「宇宙戦争シューティング」

ゲームの概要

プレイヤーは宇宙空間で敵を撃破し、ステージをクリアしていくシューティングゲームです。プレイヤーは宇宙船を操作し、敵の攻撃を回避しながら攻撃を行います。

ゲームのルール

・プレイヤーは宇宙船を操作して、敵を撃破しステージをクリアすることが目的です。
・敵の攻撃を回避しながら、敵を攻撃して撃破していきます。
・プレイヤーの宇宙船にはシールドがあり、ダメージを受けた場合はシールドが減ります。シールドがなくなると宇宙船が爆発してしまいます。
・敵を倒すとポイントが入り、一定のポイントを達成することでボス戦に挑戦できます。

ゲームの操作方法

・キーボードの矢印キーで移動します。
・マウスの左クリックで攻撃します。

ゲームのステージ

・ステージは全部で5つあります。
・各ステージは複数の敵が登場し、最後にはボスが登場します。
・ステージをクリアするたびに難易度が上がり、より強力な敵が登場します。

ゲームのグラフィック

・宇宙空間を舞台にしたシューティングゲームなので、背景は宇宙空間を表現したものにします。
・プレイヤーや敵の宇宙船、攻撃なども美麗な3Dグラフィックで表現します。

ゲームのサウンド

・プレイヤーが操作する宇宙船のエンジン音や攻撃音、敵が攻撃する音などを用意します。
・BGMには壮大な宇宙空間を表現したものを使用します。

以上が、Unityを使ってシューティングゲームを作る企画案です。必要に応じて、さらに詳細を追加していくことも可能です。

モックアップ

基本的な動作を作ります

プレイヤーキャラクターが移動し、弾を発射することができ、敵キャラクターが自動的に生成され、プレイヤーキャラクターに向かって攻撃してくる仕組みが実装されています。また、プレイヤーキャラクターと敵キャラクターが互いにダメージを与えることができ、一定のダメージを受けると爆発エフェクトを生成することもできます。ゲームマネージャーは、プレイヤーキャラクターの死亡判定を行い、ゲームオーバー時にシーンを再読み込むことでゲームをリスタートする機能を持っています。

プリミティブな3Dモデル(Unityエンジンの標準モデル)を使って、基本構成を作ってみます

プロジェクト構成

プロジェクトウィンドウに必要とする素材を保存します

Assetsフォルダ

親フォルダになります
すべての素材は、このフォルダ内に収めることになります

ここにはGameManagerスクリプトが保存されています
(Scriptsフォルダへの保存でも構いません)

マテリアル

Unityのマテリアルは、3Dオブジェクトに色やテクスチャなどの表面特性を与えるための機能です。つまり、オブジェクトの見た目を定義するために使用されます。

マテリアルは、色、テクスチャ、シェーディング、反射率などのパラメータを持ち、これらのパラメータを変更することでオブジェクトの外観を制御できます。たとえば、マテリアルの色を変更することで、オブジェクトの色を変更できます。また、テクスチャを適用することで、オブジェクトの表面に画像を貼り付けることができます。

Unityには、標準的なマテリアルが用意されており、デフォルトの設定で使用することができます。また、ユーザーが独自にカスタムマテリアルを作成することもできます。マテリアルは、Unityのシェーダーと密接に関連しています。シェーダーは、マテリアルに対して実際の描画を行うためのプログラムであり、マテリアルによって使用されるシェーダーによって、オブジェクトの見た目が大きく変化することがあります。

モックアップ用に青と赤を描画するマテリアルを作成しています

プレファブ

Unityにおいて、Prefabとはシーン上のゲームオブジェクトをベースとして、そのオブジェクトの構造やコンポーネント、設定を保存して、後で再利用できるようにする機能です。つまり、Prefabを作成することで、同じようなオブジェクトを簡単に生成することができます。例えば、プレイヤーキャラクター、敵キャラクター、アイテムなどをPrefab化することで、同じオブジェクトを何度も使いまわすことができます。Prefabは、ゲーム開発の効率化に大きく貢献しています。

実行時の搭乗するオブジェクトのクラスを作成して保存しています

シーン

Unityのシーンとは、ゲームの世界を構成する要素を配置して組み合わせた、一つの独立した作業単位のことを指します。つまり、シーンとはゲームのレベルやステージのようなものであり、その中にはキャラクターやオブジェクト、カメラ、照明、エフェクト、音楽など、ゲームに必要なすべての要素を配置することができます。

Unityのシーンは、複数のシーンを作成して、それらを組み合わせることで、ゲームの進行状況を管理することができます。また、シーンにはヒエラルキーと呼ばれる階層構造があり、オブジェクトやコンポーネントなどを階層化して管理することができます。シーンの作成や編集は、Unityのエディター上で行うことができます。

1つのシーンを作成しています
メニューシーン、ゲームオーバーシーンなどを今後追加していくといいでしょう

スクリプト

Unityのスクリプトは、C#プログラミング言語を使用して、Unityプロジェクトでオブジェクトの振る舞いを制御するコードです。スクリプトは、オブジェクトの移動、コリジョンの処理、アニメーションの制御、カメラの操作、オブジェクトの生成、削除など、様々なタスクを実行できます。スクリプトをオブジェクトにアタッチすることで、そのオブジェクトに独自の振る舞いを与えることができます。また、複数のスクリプトを同時にアタッチすることで、複雑な動作を実現することもできます。Visual StudioといったIDEで開発することができます。

それぞれのプレファブにアタッチするスクリプトを保存しています

Prefab構成

プロジェクトウィンドウで、各プレファブを選択した時のインスペクターを確認してください

ColliderとEnemyControllerをアタッチしています

当たり判定に接触判定を使いますので、 ColliderのisTriggerをチェックします
EnemyControllerの各パラメータは、ここで設定します

敵の弾

ColliderとBulletControllerをアタッチしています

当たり判定に接触判定を使いますので、 ColliderのisTriggerをチェックします
BulletControllerの各パラメータは、ここで設定します

爆弾

ColliderとExplosionControllerをアタッチしています

当たり判定に接触判定を使いますので、 ColliderのisTriggerをチェックします
EnemyControllerの各パラメータは、ここで設定します

プレイヤー

ColliderとPlayerControllerをアタッチしています

当たり判定に接触判定を使いますので、 ColliderのisTriggerをチェックします
PlayerControllerの各パラメータは、ここで設定します

プレイヤーの弾

ColliderとBulletControllerをアタッチしています

当たり判定に接触判定を使いますので、 ColliderのisTriggerをチェックします
BulletControllerの各パラメータは、ここで設定します

Tags & Layers構成

弾同士など、衝突判定が不要なところは、レイヤーによってコントロールしています

Layers

Project Settings

コリジョンマトリックスで、衝突が必要なところ以外のチェックを外すことでスルーするように設定できます

Physics構成

クラス図

継承やインターフェースの実装の様子がわかると思います

スクリプト

これらのコードは、Unityのゲーム開発において使用されるスクリプトで、以下の機能を持っています。

  • プレイヤーキャラクターと敵キャラクターを制御するスクリプト
  • ゲームマネージャーとしてゲームの開始、敵キャラクターの自動生成、ゲームオーバー判定、シーンの再読み込みなどを行うスクリプト
  • 弾の制御、当たり判定、ダメージ計算などを行うスクリプト
  • 爆発エフェクトを制御するスクリプト

これらのスクリプトを総合的に使用することで、2Dシューティングゲームのような動作を実現することができます。具体的には、プレイヤーキャラクターが移動し、弾を発射することができ、敵キャラクターが自動的に生成され、プレイヤーキャラクターに向かって攻撃してくる仕組みが実装されています。また、プレイヤーキャラクターと敵キャラクターが互いにダメージを与えることができ、一定のダメージを受けると爆発エフェクトを生成することもできます。ゲームマネージャーは、プレイヤーキャラクターの死亡判定を行い、ゲームオーバー時にシーンを再読み込むことでゲームをリスタートする機能を持っています。

GameManager

using UnityEngine;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    public GameObject playerPrefab; // プレイヤーのプレハブ
    public GameObject enemyPrefab; // 敵のプレハブ
    GameObject player;
    public float spawnInterval; // 敵の出現間隔
    private float nextSpawnTime; // 次に敵を出現させる時間

    void Start()
    {
        player = Instantiate(playerPrefab, Vector3.zero, Quaternion.identity); // プレイヤーを生成

        InvokeRepeating(nameof(CreateEnemy), 1, spawnInterval);
    }

    void CreateEnemy()
    {
        Instantiate(enemyPrefab, new Vector3(Random.Range(-7f, 7f), 6f, 0f), Quaternion.identity);
    }
    void Update()
    {
        // ゲームオーバー判定
        if (!GameObject.FindGameObjectWithTag("Player").GetComponent<Renderer>().enabled)
        {
            Invoke(nameof(GameEnd), 1);
        }
    }

    private void GameEnd()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); // 現在のシーンを再読み込みしてリスタート
    }
}

このコードは、Unityゲームエンジンを使用して2Dの横スクロールシューティングゲームを作成するためのスクリプトの一部です。

GameManagerという名前のクラスが定義されています。このクラスには、プレイヤーキャラクターと敵キャラクターを生成する機能、敵キャラクターの出現間隔を管理する変数、そしてゲームオーバーになった場合にゲームを再起動する機能が含まれています。

具体的には、Startメソッドでは、プレイヤーキャラクターを生成し、CreateEnemyメソッドを指定された間隔で繰り返し呼び出します。CreateEnemyメソッドでは、敵キャラクターをランダムな位置に生成します。

Updateメソッドでは、プレイヤーキャラクターが死亡した場合にゲームオーバーとみなし、GameEndメソッドを1秒後に呼び出します。GameEndメソッドは、現在のシーンを再読み込みしてリスタートします。

このコードは、Unityの公式ドキュメントなどから得られる基本的な知識を前提としています。UnityEngineおよびUnityEngine.SceneManagementの名前空間がインポートされており、InstantiateやInvokeRepeating、SceneManager.LoadSceneなどのUnityのAPIが使用されています。

BulletController

using UnityEngine;

public class BulletController : MonoBehaviour
{
    public int lifeTime; // 弾の飛行時間
    public int damage; // 弾の攻撃力

    private void Start()
    {
        Destroy(gameObject, lifeTime);
    }
    private void OnTriggerEnter(Collider other)
    {
        other.GetComponent<ITakeDamage>().TakeDamage(damage);
        Destroy(gameObject);
    }
}

このコードはUnityゲームエンジンで作られた2Dまたは3Dのシューティングゲームにおいて、弾の振る舞いを制御するスクリプトです。

BulletControllerクラスは、MonoBehaviourクラスを継承しています。MonoBehaviourクラスはUnityのコンポーネントの基本クラスで、オブジェクトの振る舞いを制御するために使用されます。

BulletControllerクラスには、lifeTimeとdamageというパブリックな変数があります。lifeTimeは弾が生存する時間を、damageは弾の攻撃力を表します。

Start()メソッドは、Destroy()メソッドを使用して、弾がlifeTime秒数経過した後に自動的に削除されるようにします。

OnTriggerEnter(Collider other)メソッドは、弾が他のオブジェクトに衝突したときに実行されます。このメソッドは、衝突したオブジェクトにITakeDamageコンポーネントがある場合、TakeDamage()メソッドを呼び出してダメージを与えます。そして、弾自身を削除することで、消滅します。

このコードは、シューティングゲームにおいて、弾の生存期間と攻撃力を制御し、他のオブジェクトに衝突した際にダメージを与えることができます。ただし、衝突するオブジェクトには、ITakeDamageコンポーネントが必要です。

CharacterController

using UnityEngine;

public abstract class CharacterController : MonoBehaviour
{
    public float speed; // 移動速度
    public GameObject bulletPrefab; // 弾のプレハブ
    public float bulletSpeed; // 弾の速度
    public float fireRate; // 弾の発射間隔
    public GameObject explosionPrefab; // 爆発のプレハブ
    public int maxHP; // 最大HP
    public float delayTimeforZeroLife; // Lifeが0になってからデストロイするまでの時間

    [SerializeField]
    int currentHp;

    public int CurrentHp
    {
        get => currentHp;
        set
        {
            currentHp = value;

            if (currentHp <= 0)
            {
                gameObject.GetComponent<Renderer>().enabled = false;
                Destroy(gameObject, delayTimeforZeroLife); // 宇宙船を削除
                Instantiate(explosionPrefab, transform.position, Quaternion.identity); // 爆発を生成
            }
        }
    }
}

このコードは、UnityのCharacterControllerクラスを拡張した抽象クラスを定義しています。以下は、各フィールドの説明です。

  • speed:キャラクターの移動速度を表します。
  • bulletPrefab:弾のプレハブを表します。
  • bulletSpeed:弾の速度を表します。
  • fireRate:弾の発射間隔を表します。
  • explosionPrefab:爆発のプレハブを表します。
  • maxHP:最大ヒットポイントを表します。
  • delayTimeforZeroLife:Lifeが0になってからデストロイするまでの時間を表します。

また、[SerializeField]属性が付与されたcurrentHpフィールドは、ヒットポイントの現在値を表します。getアクセサーとsetアクセサーを持ち、currentHpの値が0以下になった場合には、ゲームオブジェクトのレンダラーを非表示にしてから、delayTimeforZeroLifeで指定された時間後に削除し、explosionPrefabで指定された爆発エフェクトを生成します。

このクラスは、具体的なキャラクターの操作や挙動を実装するクラスであり、抽象クラスであるため、このクラス自体はインスタンス化できません。具体的なキャラクターの操作や挙動を実装するために、このクラスを継承したサブクラスを作成する必要があります。

ITakeDamage

public interface ITakeDamage
{
    void TakeDamage(int damage);
}

このコードは、C#言語においてインターフェイスを定義しています。インターフェイスは、複数のクラスで共通のメソッドやプロパティを定義することができます。

このインターフェイスの名前は ITakeDamage であり、1つのメソッド TakeDamage を持っています。このメソッドは、1つの整数パラメータ damage を受け取ります。このインターフェイスを実装するクラスは、TakeDamage メソッドを必ず実装しなければなりません。

このインターフェイスは、オブジェクトにダメージを与える機能を持たせるために使用される場合があります。たとえば、ゲームのキャラクターや敵のクラスがこのインターフェイスを実装することで、他のオブジェクトからダメージを受け取ることができます。

PlayerController

using UnityEngine;

public class PlayerController : CharacterController, ITakeDamage
{

    private float nextFireTime; // 次に弾を発射できる時間

    void Update()
    {
        if (CurrentHp <= 0) return;

        // 移動
        float hInput = Input.GetAxis("Horizontal");
        float vInput = Input.GetAxis("Vertical");
        Vector3 direction = new Vector3(hInput, vInput, 0f).normalized;
        transform.position += direction * speed * Time.deltaTime;

        // 弾の発射
        if (Input.GetButton("Fire1") && Time.time > nextFireTime)
        {
            nextFireTime = Time.time + fireRate;
            GameObject bullet = Instantiate(bulletPrefab, transform.position + transform.up * 1, Quaternion.identity);
            bullet.GetComponent<Rigidbody>().velocity = transform.up * bulletSpeed;
        }
    }

    public void TakeDamage(int damege)
    {
        CurrentHp -= damege;
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Enemy"))
        {
            CurrentHp = 0;
            other.GetComponent<CharacterController>().CurrentHp = 0;
        }
    }
}

このコードは、UnityのPlayerControllerというクラスで、キャラクターをコントロールする機能を提供します。このクラスは、CharacterControllerというクラスを継承しています。

このクラスには、次のメンバーが含まれます。

  • nextFireTime: 次に弾を発射できる時間を保持する変数。
  • Updateメソッド: ゲームオブジェクトの状態を更新するために毎フレーム呼び出されるメソッド。
  • TakeDamageメソッド: ITakeDamageインターフェースによって宣言されたメソッドで、ダメージを受けたときに呼び出されます。
  • OnTriggerEnterメソッド: オブジェクトがトリガーに触れたときに呼び出されるメソッド。

Updateメソッドでは、キャラクターを移動させるコードと、弾を発射するコードが含まれています。移動には、Input.GetAxisメソッドを使用して水平および垂直の入力を取得し、normalizedを呼び出して入力の方向を正規化しています。次に、transform.positionを使用してキャラクターを入力方向に移動します。弾を発射するには、Input.GetButtonメソッドを使用してユーザーがボタンを押しているかどうかを確認し、Time.timeとnextFireTimeを比較して、次に弾を発射できるかどうかを判断します。弾のプレハブをInstantiateメソッドを使用して生成し、transform.upを使用してプレイヤーの方向に向かって発射します。

TakeDamageメソッドでは、キャラクターがダメージを受けた場合にCurrentHpを減らします。OnTriggerEnterメソッドでは、トリガーに触れたオブジェクトがEnemyタグを持つ場合、キャラクターのCurrentHpを0に設定し、接触した敵のCurrentHpも0に設定します。

EnemyController

using System;
using UnityEngine;

public class EnemyController : CharacterController, ITakeDamage
{
    private Transform player; // プレイヤーの位置
    Vector3 directionForPlayer;

    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player").transform;

        // 弾の発射
        InvokeRepeating(nameof(CreateBullet), 0, fireRate);
    }

    private void Update()
    {
        // プレイヤーに向かって移動
        directionForPlayer = (player.position - transform.position).normalized;
        transform.position += directionForPlayer * speed * Time.deltaTime;
    }
    private void CreateBullet()
    {
        GameObject bullet = Instantiate(bulletPrefab, transform.position - transform.up * 1, Quaternion.identity);
        bullet.GetComponent<Rigidbody>().velocity = directionForPlayer * bulletSpeed;
    }

    public void TakeDamage(int damege)
    {
        CurrentHp -= damege;
    }
}

このコードは、UnityのEnemyControllerというクラスを定義し、CharacterControllerとITakeDamageの両方のインターフェースを実装しています。CharacterControllerは、UnityのGameObjectに物理的な振る舞いを追加するための基本クラスであり、ITakeDamageはダメージを受けるオブジェクトを表すインターフェースです。

EnemyControllerクラスは、プレイヤーを追跡し、一定の間隔で弾を発射することができます。プレイヤーの位置を取得し、移動方向を計算して、transform.positionを更新することでプレイヤーに向かって移動します。InvokeRepeatingメソッドは、指定した間隔でCreateBulletメソッドを繰り返し呼び出します。

CreateBulletメソッドでは、弾丸のインスタンスを生成し、弾丸に方向と速度を設定します。Instantiateメソッドは、新しいオブジェクトを生成して、指定された位置と回転で配置します。bulletPrefabは、事前に作成された弾丸のプレハブです。Rigidbodyコンポーネントを使用して、弾丸に方向と速度を与えます。

最後に、ITakeDamageインターフェースのTakeDamageメソッドを実装し、EnemyControllerがダメージを受けたときに現在のHPを更新することができます。

ExplosionController

using UnityEngine;

public class ExplosionController : MonoBehaviour
{
    void Start() => Destroy(gameObject, 1);
}

このコードは、Unityのゲームオブジェクトのスクリプトである「ExplosionController」を定義しています。

Start() メソッドは、ゲームオブジェクトがアクティブになった直後に一度だけ実行されます。このメソッドは、Destroy() 関数を使って、このスクリプトがアタッチされたゲームオブジェクトを1秒後に削除するように指示しています。

つまり、このスクリプトは、爆発のような短時間で完了するイベントを表しており、1秒後にそのオブジェクトを削除することで、パフォーマンスやリソースの節約を行っています。

Unity,学習

Posted by hidepon