第05回

当たり判定とアニメーションイベントとレイヤー

プロジェクトの準備

  1. このページは 2Dシューティングゲーム を初心者用に簡略化したものです。
    ダウンロードした「第04回プロジェクト」から Assets/Scene/Stage.unity のダブルクリックで起動します。
    Script にエラーがあるらしく、このままでは動かないのでエラーを修正します。
    1. Assets/Script/Bullet.cs を修正します。
      Bullet.cs は PlayerBullet にアタッチして、弾を移動するために使われます。
      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class Bullet : MonoBehaviour
      {   public int speed = 10;
      
          void Start ()
          {   GetComponent<Rigidbody2D>().velocity = transform.up.normalized * speed;
          }
      }
      
    2. Assets/Script/Spaceship.cs を修正します。
      Spaceship.cs は Player.cs や Enemy.cs と組み合わせて使用します。
      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      [RequireComponent(typeof(Rigidbody2D))]
      public class Spaceship : MonoBehaviour
      {
          public float speed;         // 移動スピード
          public float shotDelay;     // 弾を撃つ間隔
          public GameObject bullet;   // 弾のPrefab
          public bool canShot;        // 弾を撃つかどうか
          
          // 弾の作成
          public void Shot (Transform origin)
          {
              Instantiate (bullet, origin.position, origin.rotation);
          }
          
          // 機体の移動
          public void Move (Vector2 direction)
          {
              GetComponent<Rigidbody2D>().velocity = direction * speed;
          }
      }
      
    3. Assets/Script/Player.cs を修正します。
      Player.cs は Player にアタッチして、弾丸を連射するためと矢印キーで Player を操作するために使われます。
      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class Player : MonoBehaviour
      {   Spaceship spaceship;
          
          IEnumerator Start ()
          {
              spaceship = GetComponent<Spaceship> ();
              while (true) {
                  spaceship.Shot (transform);
                  yield return new WaitForSeconds (spaceship.shotDelay);
              }
          }
          
          void Update ()
          {
              float x = Input.GetAxisRaw ("Horizontal");
              float y = Input.GetAxisRaw ("Vertical");
              Vector2 direction = new Vector2 (x, y).normalized;
              spaceship.Move (direction);
          }
      }
      
    4. Assets/Script/Enemy.cs を修正します。
      Enemy.cs は Enemy にアタッチして、3発ずつの弾丸を連射するために使われます。
      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class Enemy : MonoBehaviour
      {
          Spaceship spaceship;
          
          IEnumerator Start ()
          {
              spaceship = GetComponent<Spaceship> ();
              spaceship.Move (transform.up * -1);     // Y軸のマイナス方向に移動する
              if (spaceship.canShot == false) {
                  yield break;
              }
              
              while (true) {
                  for (int i = 0; i < transform.childCount; i++) {
                      Transform shotPosition = transform.GetChild(i);
                      spaceship.Shot (shotPosition);
                  }
                  yield return new WaitForSeconds (spaceship.shotDelay);
              }
          }
      }
      
  2. 修正が終わると、画面下の赤いエラーメッセージが消えます。
    Player と Enemy が表示されていないときは Assets/Scenes/Stage をダブルクリックして下さい。
    再生ボタンをクリックすると Player と Enemy がアニメーションしながら弾を発射します。
    矢印キーで Player を操作できることも確認して下さい。
    確認が終わるとメニューの File→Save Scene で保存して下さい。

当たり判定を付ける

  1. いよいよプログラムも佳境に入ってきました。
    Unity では当たり判定に Collider(コライダー)を使います。
    Collider は Rigidbody の物理挙動と強く関係しています。
    最初に Player と Enemy に Collider を設定して当たり判定をします。
    Collider にも何種類かあるのですが、Player には Box Collider を、Enemy には Circle Collider を使ってみます。
  2. Player を選択して Add Component ボタンから Physics 2D→Box Collider 2D をアタッチして下さい。
    Size は X 0.06 Y 0.06で、4ドット程度の大きさの当たり判定にします。
    当たり判定の範囲は、実際にプレイした感覚で決めるのが良いでしょう。
    Collider の Is Trigger をチェックして、当たっても反発しないように設定します。
    Enemy には Circle Collider 2D をアタッチします。
    Rudius を 0.4 に設定します。
    Collider の Is Trigger をチェックして、当たっても反発しないように設定します。
    設定が終わったら Apply をクリックして Prefab を更新して下さい。
  3. Assets/Script/Spaceship.cs に explosion(爆発)を追加します。
    爆発は開始から 0.25 秒後に消滅します。
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    [RequireComponent(typeof(Rigidbody2D))]
    public class Spaceship : MonoBehaviour
    {
        public float speed;         // 移動スピード
        public float shotDelay;     // 弾を撃つ間隔
        public GameObject bullet;   // 弾のPrefab
        public bool canShot;        // 弾を撃つかどうか
        public GameObject explosion;  // 爆発のPrefab
    
        // 爆発の作成
        public void Explosion ()
        {
            Destroy (gameObject, 0.25f);
            Instantiate (explosion, transform.position, transform.rotation);
        }
        
        // 弾の作成
        public void Shot (Transform origin)
        {
            Instantiate (bullet, origin.position, origin.rotation);
        }
        
        // 機体の移動
        public void Move (Vector2 direction)
        {
            GetComponent<Rigidbody2D>().velocity = direction * speed;
        }
    }
    
  4. スクリプトで当たり判定を検出するのですが、今回は OnTriggerEnter2D() 関数を使います。
    Assets/Script/Player.cs を修正します。
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Player : MonoBehaviour
    {   Spaceship spaceship;
        
        IEnumerator Start ()
        {
            spaceship = GetComponent<Spaceship> ();
            while (true) {
                spaceship.Shot (transform);
                yield return new WaitForSeconds (spaceship.shotDelay);
            }
        }
        
        void Update ()
        {
            float x = Input.GetAxisRaw ("Horizontal");
            float y = Input.GetAxisRaw ("Vertical");
            Vector2 direction = new Vector2 (x, y).normalized;
            spaceship.Move (direction);
        }
    
        // 衝突すると呼び出される
        void OnTriggerEnter2D (Collider2D c)
        {   string layerName = LayerMask.LayerToName(c.gameObject.layer);
    Debug.Log("Player:" + layerName);
            // 弾の削除
            Destroy(c.gameObject);
            // 爆発する
            spaceship.Explosion();
            // プレイヤーを削除
            Destroy (gameObject);
        }
    }
    
  5. Player と Enemy のインスペクターから Spaceship の Explosion(public GameObject explosion;) に Prefab/Explosion を設定して下さい。
    このままでは Player と Enemy が衝突すると爆発が延々と続きます。
    Animations/Explosion の Explode からインスペクターに表示される「Loop Time」のチェックを外して下さい。
    衝突すると爆発が一度だけ起こり、Player と Enemy が削除されます。
    確認が終わったら保存して終了して下さい。

layerName

  1. 次のステップとして Player から弾を発射するのですが、このままでは自分が発射した弾に Player 自身が当たってしまいます。
    そこで衝突した相手を layerName で識別出来るように Layer を設定します。
    OnTriggerEnter2D() の先頭で layerName を印字しているのですが、規定値では全てに "Default" が設定されています。
    Player:Default
    Enemy:Default
    
  2. メニューから[Edit][Project Settings][Tags and Layers] を選択します。
    Inspector から Layers を開いて User Layer 8 ~ User Layer 10 に次のIDを設定します。
    Layer ID
    User Layer 8 Player
    User Layer 9 Enemy
    User Layer 10 Bullet(Player)
  3. Player の Inspector から Layer(画面右上のボタン)に Player を選択して下さい。
    同様に Enemy の Layer に Enemy を選択して下さい。
    Assets/Prefab/PlayerBullet をヒエラルキービューにドラッグして表示します。
    PlayerBullet の2発の弾に Bullet(Player) を選択して下さい。
    Object に子が含まれているとき、子も対象にするか否かの問い合わせがあるので、否で応答して下さい。
    Prefabs の Object を修正したときは、Inspector から Prefab/Apply タブをクリックして更新して下さい。
  4. Assets/Script/Player.cs の OnTriggerEnter2D() 関数を修正します。
    Enemy に衝突するとゲーム終了です。(Enemy Bullet は、まだ使っていません)
    Enemy 以外(Player Bullet など)では何も起こりません。
        // 衝突すると呼び出される
        void OnTriggerEnter2D (Collider2D c)
        {   string layerName = LayerMask.LayerToName(c.gameObject.layer);
    Debug.Log("Player: " + layerName);
            if( layerName == "Enemy")
            {   // 弾の削除
                Destroy(c.gameObject);
                // プレイヤーを削除
                Destroy (gameObject);
            }
        }
    
  5. 衝突した相手を識別する Assets/Script/Enamy.cs です。
    Player や PlayerBullet との衝突を検出します。
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Enemy : MonoBehaviour
    {   Spaceship spaceship;
    
        IEnumerator Start ()
        {   spaceship = GetComponent<Spaceship> ();
            spaceship.Move (transform.up * -1);
            if (spaceship.canShot == false) {
                yield break;  }
    
            while (true) {
                for (int i = 0; i < transform.childCount; i++) {
                    Transform shotPosition = transform.GetChild(i);
                    spaceship.Shot (shotPosition);
                }
                yield return new WaitForSeconds (spaceship.shotDelay);
            }
        }
    
        void OnTriggerEnter2D (Collider2D c)
        {   string layerName = LayerMask.LayerToName(c.gameObject.layer);
    Debug.Log("Enemy: " + layerName);
            if (layerName == "Player" || layerName == "Bullet(Player)")
            {
                Destroy(c.gameObject);
                spaceship.Explosion();
                Destroy(gameObject);
            }
        }
    }
    
  6. Player と Enemy が衝突すると爆発が起こり、Player と Enemy が削除されます。
    確認が終わったらメニューから File→Save Scene で保存して下さい。

Player Bullet

  1. Player の弾(Bullet)に Collider を設定して当たり判定を設定します。
    Player と Enemy が表示されていないときは Assets/Scenes/Stage をダブルクリックして下さい。
    Assets/Prefab/PlayerBullet をヒエラルキービューにドラッグして表示します。(既に表示されている?)
  2. PlayerBullet の2個の Bullet に Box Collider 2D をアタッチします。
    Size は X 0.05 Y 0.1 に設定します。
    Is Trigger をチェックして、当たっても反発しないように設定します。
    設定が終わったら Apply をクリックして Prefab を更新します。
  3. Enemy の座標をもっと上(Y: 2.0)に設定してテストプレイをしやすくします。
    再生ボタンをクリックして、ゲームを実行して下さい。
    Player の弾が Enemy に当たると爆発して Enemy が消滅します。
    シーンとプレハブを更新して終了します。
  1. Collider にも種類があって、用途に応じて使い分けます。
    (Box Collider, Circle Collider, Polygon Collider)
  2. 今のままだと発射した弾やエネミーは延々と画面の外へ移動してしまいます。
    そこで範囲外に出た Object を削除する仕組み(DestroyArea)が必要です。
  3. Player や Enemy から発射される弾にも当たり判定が必要です。
    また Object が入り乱れたとき、当たり判定の制御(自機から発射した弾には当たらない,DestroyArea とは衝突しない)が必要です。
  4. 省略したプログラムの詳細は 2Dシューティングゲーム から 「第05回 当たり判定とアニメーションイベントとレイヤー」を参照して下さい。
    第05回 の完成したプロジェクトは、後部の「今回のプロジェクトファイルをダウンロード」からダウンロードすることが出来ます。

[Next Chapter ↓] Shooting-06
[Previous Chapter ↑] Shooting-04

前田稔の超初心者のプログラム入門
超初心者のプログラム入門(Unity)