Skip to content

第8回 当たり判定 ​

この回でつくるもの:弾が敵に当たると敵が消え、敵が自機にぶつかると自機が消えるようにします。いよいよ「ゲーム」らしくなります。

この回で学ぶこと ​

  • Collider(当たり判定の形)と Rigidbody(物理の計算をする部品)の役わり
  • Is Trigger・Is Kinematic・Use Gravity の意味
  • タグ(Tag)を作って、オブジェクトを見分ける
  • OnTriggerEnter で「ぶつかったとき」の処理を書く
  • GetComponent、null、引数のある関数、return

前回までのふりかえり ​

前回は、敵のプレハブ Enemy_Straight を作り、EnemySpawner がコルーチンで1.5秒ごとに敵を出すようにしました。 でも、弾を当てても敵は倒れず、敵は自機をすり抜けてしまいます。 今回は、ものとものが「ぶつかった」ことがわかるようにします。

1. Collider と Rigidbody ​

1-1. 当たり判定に必要な2つの部品 ​

Unity で「ぶつかった」ことを知るには、2つのコンポーネントを使います。

コンポーネント役わりたとえると
Collider(コライダー)当たり判定の 形。目に見えない「当たりの箱」体のまわりのバリア
Rigidbody(リジッドボディ)物理の計算をする部品。これがあると Unity が「ぶつかったかどうか」を調べてくれるバリアのセンサーのスイッチ

Collider には形のちがういくつかの種類があります。この教材では Box Collider(箱)、Sphere Collider(球)、Capsule Collider(カプセル)を使います。

1-2. 3つのチェックの意味 ​

項目付いている場所この教材での設定意味
Is TriggerColliderオンぶつかっても押し返さず、すり抜けながら「ぶつかったよ」と知らせるだけにする
Is KinematicRigidbodyオン物理の力(押される・はね返る)で動かさない。スクリプトで動かす
Use GravityRigidbodyオフ重力で下へ落ちないようにする(宇宙なので)

このゲームのルールはかんたんです。

  • 動くもの全部 に Rigidbody を付ける(Is Kinematic オン、Use Gravity オフ)
  • Collider は Is Trigger をオン
  • Collider と Rigidbody は 親 のオブジェクトに付ける(子の Model には付けない)

先生へ

Is Kinematic の Rigidbody どうしでも、Collider の Is Trigger がオンなら OnTriggerEnter は呼ばれます。物理の力で動かさないので、スクリプトの Translate や position での移動とけんかしません。メッシュの形そのままの Mesh Collider は重く、設定もむずかしいので使いません。

2. タグ Enemy を作って付ける ​

2-1. タグとは ​

タグ(Tag) は、オブジェクトに付ける「名札」です。スクリプトで「ぶつかった相手は敵かな?」と調べるときに使います。 Player タグは最初からあるので、Enemy タグを新しく作ります。

2-2. 敵のプレハブを開く ​

  1. Project ウィンドウで Assets/_Game/Prefabs を開きます。
  2. Enemy_Straight をダブルクリックします。
  3. プレハブモード(プレハブだけを編集する画面)になります。
  4. Hierarchy でいちばん上の Enemy_Straight(親)を選びます。

2-3. タグを作る ​

  1. Inspector のいちばん上にある Tag の欄(今は Untagged)をクリックします。
  2. いちばん下の Add Tag... を選びます。
  3. Inspector が Tags & Layers の画面に変わります。
  4. Tags の下にある + ボタンをクリックします。
  5. New Tag Name に Enemy と入力します(大文字・小文字も同じに)。
  6. Save をクリックします。

2-4. 敵にタグを付ける ​

  1. Hierarchy(プレハブモードのまま)で、もう一度 Enemy_Straight(親)を選びます。
  2. Inspector の Tag の欄をクリックします。
  3. Enemy を選びます。
  4. Tag が Enemy になったことを確かめます。

注意

タグを付けるのは 親の Enemy_Straight です。子の Model に付けても、スクリプトからは見つけられません。

3. 弾に Rigidbody と Sphere Collider を付ける ​

3-1. 弾のプレハブを開く ​

  1. Hierarchy の左上の < を押して、いったんシーンにもどります。
  2. Project の Prefabs フォルダにある PlayerBullet をダブルクリックして、プレハブモードにします。
  3. Hierarchy で PlayerBullet を選びます。

3-2. Rigidbody を付ける ​

  1. Inspector の Add Component をクリックします。
  2. Rigidbody と入力して、Rigidbody をクリックします(Rigidbody 2D ではありません)。
  3. Use Gravity のチェックを オフ にします。
  4. Is Kinematic のチェックを オン にします。

3-3. Sphere Collider を付ける ​

  1. もう一度 Add Component をクリックします。
  2. Sphere Collider と入力して、Sphere Collider をクリックします。
  3. Is Trigger のチェックを オン にします。
  4. Scene ビューで、弾のまわりに緑の線(当たりの形)が表示されることを確かめます。

ヒント

弾は横に細長いのに、緑の線は丸く、見た目より少し大きくなります。Sphere Collider は球の形しか作れないからです。当たりが少し大きいと敵に当てやすくなるので、このままでOKです。

よくあるまちがい

Rigidbody 2D や Circle Collider 2D を付けてしまうと、当たり判定が動きません。この教材は3Dの物理を使うので、名前に「2D」が付かないものを選びます。

4. 敵のプレハブに Rigidbody と Box Collider を付ける ​

4-1. 部品を付ける ​

  1. シーンにもどってから、Project の Enemy_Straight をダブルクリックしてプレハブモードにします。
  2. Hierarchy で Enemy_Straight(親)を選びます。
  3. Add Component で Rigidbody を付けます。
  4. Use Gravity を オフ、Is Kinematic を オン にします。
  5. Add Component で Box Collider を付けます。
  6. Is Trigger を オン にします。

4-2. 大きさをモデルに合わせる ​

今の Box Collider は 1×1×1 の箱なので、モデルと大きさが合っていません。

  1. Scene ビューで、敵のまわりの緑の線を見ます(見にくいときは Scene ビューの上で F キーを押すと、敵がアップになります)。
  2. Inspector の Box Collider の Size の X・Y・Z を変えて、緑の箱がモデルをだいたい包むようにします(例: (1.5, 0.8, 1))。
  3. 箱がずれているときは Center を変えて、モデルの真ん中に合わせます。

ヒント

Box Collider の Edit Collider ボタンを押すと、Scene ビューで緑の箱の面をドラッグして大きさを変えられます。当たりの箱は、見た目より 少し小さめ にすると、プレイヤーが「今のは当たってないのに!」と感じにくくなります。

4-3. シーンにもどる ​

  1. Hierarchy の左上の < を押してシーンにもどります。
  2. Ctrl+S(Macは Cmd+S)でシーンを保存します。

先生へ

プレハブモードは、Scene ビューの右上の Auto Save がオンなら変更が自動で保存されます。オフの場合は Save ボタンを押すよう伝えてください。

5. Enemy に hp と TakeDamage を作る ​

5-1. コードを変更する ​

Enemy をダブルクリックして開き、次のように変えます。

csharp
using UnityEngine;

// 敵の基本(移動・HP・やられたとき) // ★変更
public class Enemy : MonoBehaviour
{
    public float speed = 3f;              // 左へ進む速さ
    public int hp = 1;                    // 体力 // ★追加

    bool isDead = false; // ★追加

    void Update()
    {
        transform.Translate(Vector3.left * speed * Time.deltaTime, Space.World);

        // 画面の左に出たら消す
        if (transform.position.x < -12f)
        {
            Destroy(gameObject);
        }
    }

    // ダメージを受ける // ★追加
    public void TakeDamage(int damage) // ★追加
    { // ★追加
        if (isDead) return; // ★追加

        hp -= damage; // ★追加
        if (hp <= 0) // ★追加
        { // ★追加
            Die(); // ★追加
        } // ★追加
    } // ★追加

    void Die() // ★追加
    { // ★追加
        isDead = true; // ★追加

        Destroy(gameObject); // ★追加
    } // ★追加
}

保存して Unity にもどり、Console に赤いエラーが出ていないことを確かめます。

5-2. コードの解説 ​

行意味
public int hp = 1;敵の体力。0 以下になったらやられる
bool isDead = false;もうやられたかどうか。bool は「はい(true)/いいえ(false)」だけを入れる箱
public void TakeDamage(int damage)ダメージを受ける関数。int damage は外から受け取るダメージの数
if (isDead) return;もうやられていたら、ここで関数を終わりにする
hp -= damage;体力からダメージを引く
if (hp <= 0)体力が 0 以下になったら
Die();やられたときの処理を呼ぶ
  • public を付ける理由:TakeDamage は、このあと「6.」で Bullet から呼びます。ほかのスクリプトから呼べるようにするには、public を付ける必要があります。Die は自分の中でしか使わないので、public を付けません。
  • 引数のある関数:TakeDamage(int damage) のように書くと、呼ぶ側が TakeDamage(1) や TakeDamage(3) のように数を渡せます。後の回の強い武器は、ここに大きな数を渡します。
  • return:return は「この関数はここで終わり」という意味です。弾が2発同時に当たったときに、2回やられないようにしています。

6. Bullet に OnTriggerEnter を書く ​

6-1. コードを変更する ​

Project の Assets/_Game/Scripts にある Bullet をダブルクリックして開き、次のように変えます。

csharp
using UnityEngine;

// 自機の弾
public class Bullet : MonoBehaviour
{
    public float speed = 15f;      // 速さ
    public float lifeTime = 2f;    // 何秒で消えるか
    public int damage = 1;         // 敵に与えるダメージ // ★追加

    void Start()
    {
        Destroy(gameObject, lifeTime);
    }

    void Update()
    {
        // 自分の向いている「右」へ進む(回転させると斜めに飛ぶ)
        transform.Translate(Vector3.right * speed * Time.deltaTime);
    }

    // 何かにぶつかったとき // ★追加
    void OnTriggerEnter(Collider other) // ★追加
    { // ★追加
        Enemy enemy = other.GetComponent<Enemy>(); // ★追加
        if (enemy != null) // ★追加
        { // ★追加
            enemy.TakeDamage(damage); // ★追加
            Destroy(gameObject); // ★追加
        } // ★追加
    } // ★追加
}

保存して Unity にもどり、Console に赤いエラーが出ていないことを確かめます。

6-2. コードの解説 ​

OnTriggerEnter(ぶつかったときに呼ばれる関数)

csharp
void OnTriggerEnter(Collider other)
  • Start や Update と同じように、Unity が自動で呼んでくれる関数です。
  • Is Trigger の Collider どうしが 重なった瞬間に1回 呼ばれます。
  • ( ) の中の Collider other は、ぶつかった相手 の Collider です。other(相手)という名前で使えます。このように関数に渡される材料を 引数(ひきすう)といいます。

GetComponent(相手のコンポーネントを取り出す)

csharp
Enemy enemy = other.GetComponent<Enemy>();
  • 「ぶつかった相手から、Enemy スクリプトを取り出して、enemy という箱に入れる」という意味です。
  • < > の中に、取り出したいコンポーネントの名前を書きます。
  • 相手が星の粒や自機など、Enemy が付いていないものだったら、何も取り出せません。

null(からっぽ)

csharp
if (enemy != null)
  • null(ヌル)は「何も入っていない、からっぽ」という意味です。
  • != は「〜ではない」です。つまり「enemy がからっぽでなければ(=相手が敵だったら)」という意味になります。
  • からっぽのまま enemy.TakeDamage(...) を呼ぶとエラーになるので、先に調べています。
行意味
public int damage = 1;敵に与えるダメージ。int は整数
enemy.TakeDamage(damage);敵の TakeDamage を呼んで、ダメージを渡す
Destroy(gameObject);当たった弾は、すぐに消す

ヒント

Collider を 親 の Enemy_Straight に付けたのは、Enemy スクリプトが付いているのも親だからです。もし子の Model に Collider を付けると、other は Model になり、GetComponent<Enemy>() で見つけられません。

7. 自機に当たり判定と PlayerLife を付ける ​

7-1. Rigidbody と Collider を付ける ​

  1. Hierarchy で Player(親)を選びます。
  2. Add Component で Rigidbody を付けます。
  3. Use Gravity を オフ、Is Kinematic を オン にします。
  4. Add Component で Sphere Collider を付けます。
  5. Is Trigger を オン にします。
  6. Radius を 0.4 くらいにして、緑の球が自機の真ん中あたりを包むようにします。

ヒント

シューティングゲームでは、自機の当たり判定は 見た目よりかなり小さく するのがふつうです。翼のはしをかすっただけでやられると、くやしいですよね。細長い機体なら Box Collider を使ってもかまいません。

7-2. PlayerLife スクリプトを作る ​

  1. Project の Assets/_Game/Scripts を開きます。
  2. 右クリックして Create > Scripting > MonoBehaviour Script を選びます。
  3. 名前を PlayerLife にして Enter を押します。
  4. ダブルクリックして開き、中身をすべて消して次のコードを書きます。
csharp
using UnityEngine;

// 自機がやられる
public class PlayerLife : MonoBehaviour
{
    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Enemy"))
        {
            Die();
        }
    }

    void Die()
    {
        Destroy(gameObject);
    }
}

保存して Unity にもどります。

7-3. コードの解説 ​

行意味
other.CompareTag("Enemy")ぶつかった相手のタグが Enemy なら true(はい)
Die();やられたときの処理を呼ぶ
Destroy(gameObject);自機を消す
  • 弾は GetComponent で相手を調べましたが、ここでは タグ で調べています。「敵かどうかだけ知りたい」ときは、タグで調べるとかんたんです。
  • "Enemy" の文字は、タグの名前と一字一句同じにします。大文字・小文字もまちがえないようにしましょう。
  • 今は自機が消えるだけです。爆発は第9回、残機や復活は第11回で作ります。

7-4. PlayerLife を付ける ​

  1. Hierarchy で Player を選びます。
  2. Add Component で PlayerLife を検索して付けます。
  3. Ctrl+S(Macは Cmd+S)でシーンを保存します。

8. 動作確認 ​

8-1. 弾で敵を倒す ​

▶ Play ボタンを押して、次のようになればOKです。

  • 弾が敵に当たると、敵と弾が消える
  • 弾が当たらなかった敵は、そのまま左へ進んで消える

8-2. 自機がやられる ​

  • 敵に自機をぶつけると、自機が消える
  • 自機が消えたあとも、敵は出てき続ける(ゲームオーバーは第11回で作ります)

確かめたら ▶ を押して止めます。

ヒント

当たりがうまくいかないときは、Play 中に Scene ビューを見てみましょう。オブジェクトを選ぶと、緑の線(Collider)が見えます。弾と敵の緑の線がちゃんと重なっているか確かめられます。

チャレンジ課題 ​

  • プレハブ Enemy_Straight の Hp を 3 にして、3発当てないと倒れない敵にしてみよう
  • 弾の Damage を 3 にすると、Hp 3 の敵が1発で倒れるか試してみよう
  • 自機の Collider の大きさを変えて、遊びやすい大きさをさがしてみよう
  • ヒント:Die の中に Debug.Log("やられた!"); を書くと、Console で確かめられます

つまずきポイント ​

こんなときはここを確認
弾が敵をすり抜ける弾と敵の両方に Rigidbody と Collider が付いているか。Is Trigger がオンか
敵や弾が下へ落ちていくRigidbody の Use Gravity がオフか。Is Kinematic がオンか
敵に当たっても自機が消えない敵の親に Enemy タグが付いているか。CompareTag("Enemy") のつづり
Console に赤いエラーが残っているEnemy に public void TakeDamage(int damage) を書いたか。public を忘れていないか
当たりが合っていない気がするCollider が子の Model ではなく 親 に付いているか。Size や Radius が合っているか
Collider を付けたのに反応しない名前に「2D」が付いた部品(Rigidbody 2D など)を付けていないか

まとめ ​

  • 当たり判定には Collider(形)と Rigidbody(物理の計算)を使う。この教材では Is Trigger オン、Is Kinematic オン、Use Gravity オフ
  • OnTriggerEnter は、ぶつかった瞬間に Unity が呼んでくれる
  • 相手を調べるには GetComponent(スクリプトがあるか)か CompareTag(タグ)を使う
  • null は「からっぽ」。使う前に != null で調べるとエラーを防げる