ダークモード
第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 Trigger | Collider | オン | ぶつかっても押し返さず、すり抜けながら「ぶつかったよ」と知らせるだけにする |
| Is Kinematic | Rigidbody | オン | 物理の力(押される・はね返る)で動かさない。スクリプトで動かす |
| Use Gravity | Rigidbody | オフ | 重力で下へ落ちないようにする(宇宙なので) |
このゲームのルールはかんたんです。
- 動くもの全部 に 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. 敵のプレハブを開く
- Project ウィンドウで
Assets/_Game/Prefabsを開きます。 Enemy_Straightをダブルクリックします。- プレハブモード(プレハブだけを編集する画面)になります。
- Hierarchy でいちばん上の
Enemy_Straight(親)を選びます。
2-3. タグを作る
- Inspector のいちばん上にある Tag の欄(今は Untagged)をクリックします。
- いちばん下の Add Tag... を選びます。
- Inspector が Tags & Layers の画面に変わります。
- Tags の下にある + ボタンをクリックします。
- New Tag Name に
Enemyと入力します(大文字・小文字も同じに)。 - Save をクリックします。
2-4. 敵にタグを付ける
- Hierarchy(プレハブモードのまま)で、もう一度
Enemy_Straight(親)を選びます。 - Inspector の Tag の欄をクリックします。
- Enemy を選びます。
- Tag が Enemy になったことを確かめます。
注意
タグを付けるのは 親の Enemy_Straight です。子の Model に付けても、スクリプトからは見つけられません。
3. 弾に Rigidbody と Sphere Collider を付ける
3-1. 弾のプレハブを開く
- Hierarchy の左上の < を押して、いったんシーンにもどります。
- Project の
PrefabsフォルダにあるPlayerBulletをダブルクリックして、プレハブモードにします。 - Hierarchy で
PlayerBulletを選びます。
3-2. Rigidbody を付ける
- Inspector の Add Component をクリックします。
Rigidbodyと入力して、Rigidbody をクリックします(Rigidbody 2D ではありません)。- Use Gravity のチェックを オフ にします。
- Is Kinematic のチェックを オン にします。
3-3. Sphere Collider を付ける
- もう一度 Add Component をクリックします。
Sphere Colliderと入力して、Sphere Collider をクリックします。- Is Trigger のチェックを オン にします。
- Scene ビューで、弾のまわりに緑の線(当たりの形)が表示されることを確かめます。
ヒント
弾は横に細長いのに、緑の線は丸く、見た目より少し大きくなります。Sphere Collider は球の形しか作れないからです。当たりが少し大きいと敵に当てやすくなるので、このままでOKです。
よくあるまちがい
Rigidbody 2D や Circle Collider 2D を付けてしまうと、当たり判定が動きません。この教材は3Dの物理を使うので、名前に「2D」が付かないものを選びます。
4. 敵のプレハブに Rigidbody と Box Collider を付ける
4-1. 部品を付ける
- シーンにもどってから、Project の
Enemy_Straightをダブルクリックしてプレハブモードにします。 - Hierarchy で
Enemy_Straight(親)を選びます。 - Add Component で Rigidbody を付けます。
- Use Gravity を オフ、Is Kinematic を オン にします。
- Add Component で Box Collider を付けます。
- Is Trigger を オン にします。
4-2. 大きさをモデルに合わせる
今の Box Collider は 1×1×1 の箱なので、モデルと大きさが合っていません。
- Scene ビューで、敵のまわりの緑の線を見ます(見にくいときは Scene ビューの上で F キーを押すと、敵がアップになります)。
- Inspector の Box Collider の Size の X・Y・Z を変えて、緑の箱がモデルをだいたい包むようにします(例: (1.5, 0.8, 1))。
- 箱がずれているときは Center を変えて、モデルの真ん中に合わせます。
ヒント
Box Collider の Edit Collider ボタンを押すと、Scene ビューで緑の箱の面をドラッグして大きさを変えられます。当たりの箱は、見た目より 少し小さめ にすると、プレイヤーが「今のは当たってないのに!」と感じにくくなります。
4-3. シーンにもどる
- Hierarchy の左上の < を押してシーンにもどります。
- 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 を付ける
- Hierarchy で
Player(親)を選びます。 - Add Component で Rigidbody を付けます。
- Use Gravity を オフ、Is Kinematic を オン にします。
- Add Component で Sphere Collider を付けます。
- Is Trigger を オン にします。
- Radius を 0.4 くらいにして、緑の球が自機の真ん中あたりを包むようにします。
ヒント
シューティングゲームでは、自機の当たり判定は 見た目よりかなり小さく するのがふつうです。翼のはしをかすっただけでやられると、くやしいですよね。細長い機体なら Box Collider を使ってもかまいません。
7-2. PlayerLife スクリプトを作る
- Project の
Assets/_Game/Scriptsを開きます。 - 右クリックして Create > Scripting > MonoBehaviour Script を選びます。
- 名前を
PlayerLifeにして Enter を押します。 - ダブルクリックして開き、中身をすべて消して次のコードを書きます。
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 を付ける
- Hierarchy で
Playerを選びます。 - Add Component で
PlayerLifeを検索して付けます。 - 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で調べるとエラーを防げる