Unity 6でAppleCatchを作る① ― クリックしたマスへ動かして、リンゴをキャッチする(PC版)

広告

この記事は、北村愛実 著『Unityの教科書 Unity 6完全対応版』(SBクリエイティブ)で扱われているAppleCatch(第8章)制作を題材に、授業でつまずきやすい点や、コードの意味を補足するために作成した学習記事です。コードおよび制作手順の詳細は、書籍もあわせてご参照ください。

いよいよ最後のゲーム「AppleCatch」です。上から落ちてくるリンゴを、カゴ(バスケット)でキャッチするゲームです。ステージは3×3のマス目になっていて、クリックしたマスへバスケットが移動します。リンゴを取れば得点、爆弾を取ると得点が半分になります。

Igaguriで学んだ「クリックした方向へのRay」を一歩進めて、今回はRayが何に当たったかを調べて、その場所へバスケットを動かします。

Unity 6でIgaguriを作る② ― クリックした方向へ飛ばして、パーティクルで演出する(PC版)

AppleCatchも2回に分けて解説します。

回内容
今回(①)ステージの準備/クリックしたマスへバスケットを動かす(Raycast)/アイテムを落とす/キャッチして音を鳴らす
次回(②)アイテムをランダムに降らせる/得点と残り時間のUI/時間に合わせて難しくする(レベルデザイン)

この記事で作るもの

  • 3×3のマス目になったステージを、斜め上から見下ろすカメラ
  • クリックしたマスへ、バスケットがパッと移動する
  • 上から落ちてくるリンゴや爆弾をバスケットで受け止めると、音が鳴って消える
項目内容
UnityUnity 6(サンプルは6000.1で作成)
テンプレートUniversal 3D(URP)
使う素材stage.fbx、basket.fbx、apple.fbx、bomb.fbx、get_se.mp3、damage_se.mp3

1. ステージとカメラを準備する

Universal 3D のテンプレートでプロジェクトを作り、素材をProjectウィンドウに入れたら、次のように配置します。

オブジェクトPositionRotationメモ
Main Camera(0, 3.8, -1.6)(60, 0, 0)斜め上から、ステージを見下ろす
stage(0, 0, 0)(0, 0, 0)stage.fbx をシーンにドラッグ
basket(0, 0, 0)(0, 0, 0)basket.fbx をシーンにドラッグ

カメラの Rotation X を60にすると、カメラが60度下を向きます。これで、ステージ全体を斜め上から見下ろす、ゲームらしい画面になります。

ステージにコライダーを付ける

stageを選び、BoxCollider を追加して、Size を (3, 0.1, 3) にします。これは、あとでクリックした場所を調べるときに、Rayが当たる「床」になります。

ステージは縦横3の大きさなので、x も z も -1.5〜1.5 の範囲です。この中に、1マスの大きさが1のマスが3×3個並んでいる、と考えます。

2. クリックしたマスへバスケットを動かす(Raycast)

Create → Scripting → MonoBehaviour Script で BasketController を作り、basketに付けます。

ステップ1:クリックした場所へ移動する

using UnityEngine;
using UnityEngine.InputSystem;  // 入力を検知するために必要!!

public class BasketController : MonoBehaviour
{
    void Start()
    {
        Application.targetFrameRate = 60;
    }

    void Update()
    {
        if (Mouse.current.leftButton.wasPressedThisFrame)
        {
            // カメラから、クリックした点を通るRayを作る
            Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.value);
            RaycastHit hit;

            // Rayが何かのコライダーに当たったら
            if (Physics.Raycast(ray, out hit, Mathf.Infinity))
            {
                // 当たった場所の x と z を、いちばん近い整数(マス)にそろえる
                float x = Mathf.RoundToInt(hit.point.x);
                float z = Mathf.RoundToInt(hit.point.z);
                transform.position = new Vector3(x, 0, z);
            }
        }
    }
}
イラスト:Physics.RaycastでクリックしたステージのhitPointを調べ、RoundToIntで3×3のマスにそろえる仕組み

Physics.Raycast とは

Igaguriでは、Rayの向き(ray.direction)だけを使いました。今回は Physics.Raycast で、実際にRayを飛ばしてどこに当たったかを調べています。

書き方意味
Physics.Raycast(ray, out hit, Mathf.Infinity)Rayを飛ばして、コライダーに当たったら true を返す
out hit当たった情報を hit に入れてもらう
Mathf.Infinityどこまでも遠くまで調べる(距離の上限なし)
hit.pointRayが当たった場所の座標(Vector3)

out は、「メソッドの中で値を入れてもらう変数」に付けるキーワードです。Raycast の戻り値は「当たったかどうか」の true/false だけなので、当たった場所などの詳しい情報は out hit を通して受け取ります。

ステージにBoxColliderを付けたのは、このRayを受け止める床にするためです。コライダーがないと、Rayはステージをすり抜けてしまい、Raycast は false になります。

RoundToInt でマス目にそろえる

hit.point は、たとえば (0.73, 0.05, -1.21) のような細かい値です。これをそのまま使うと、バスケットがマスの真ん中からずれた場所に止まってしまいます。

Mathf.RoundToInt は、いちばん近い整数にそろえるメソッドです。ステージの範囲(-1.5〜1.5)の値は、-1、0、1 のどれかになるので、バスケットは必ず3×3のマスのどれかの真ん中に移動します。

クリックした場所(hit.point.x)RoundToInt の結果移動する列
-1.4 〜 -0.5 あたり-1左の列
-0.5 〜 0.5 あたり0真ん中の列
0.5 〜 1.4 あたり1右の列

3. アイテムを落とす(ItemController)

apple.fbx と bomb.fbx をシーンにドラッグし、それぞれ Position を (-1, 3, 0)、(1, 3, 0) にします。次に、2つともに SphereCollider を追加して、Center を (0, 0.25, 0)、Radius を 0.25 にします。

ItemController スクリプトを作り、リンゴと爆弾の両方に付けます。

using UnityEngine;

public class ItemController : MonoBehaviour
{
    public float dropSpeed = -0.03f;

    void Update()
    {
        // 毎フレーム、少しずつ下へ動かす
        transform.Translate(0, this.dropSpeed, 0);

        // 画面の外(ステージより下)まで落ちたら消す
        if (transform.position.y < -1.0f)
        {
            Destroy(gameObject);
        }
    }
}

CatEscapeの矢と同じく、Translate で毎フレーム少しずつ下へ動かし、見えなくなったら Destroy で消しています。dropSpeed を public にしているのは、次回、落ちる速さを外から変えるためです。

Unity 6でCatEscapeを作る① ― 猫の移動と、Prefabで矢を降らせる仕組み(PC版)

4. リンゴと爆弾を見分けるタグ

バスケットに入ったものが、リンゴなのか爆弾なのかを見分けるために、タグ(Tag)を使います。

  1. リンゴを選び、Inspectorの一番上にある Tag のメニューから Add Tag… を選ぶ
  2. + ボタンで Apple と Bomb の2つのタグを作る
  3. リンゴのTagを Apple、爆弾のTagを Bomb に設定する

タグは、オブジェクトに付ける「名札」のようなものです。スクリプトからは other.gameObject.tag で読み取れます。

タグを付けたら、リンゴと爆弾をそれぞれProjectウィンドウへドラッグして applePrefab、bombPrefab というPrefabにしておきます(シーンのものは動作確認用に残しておいてかまいません)。

5. バスケットでキャッチする

バスケットにトリガーとRigidbodyを付ける

basketに、次の3つのコンポーネントを追加します。

コンポーネント設定役割
BoxColliderCenter (0, 0.5, 0)/Size (0.5, 0.1, 0.5)/Is Trigger:オンカゴの口のあたりに、すり抜ける当たりを作る
RigidbodyIs Kinematic:オントリガーの判定を動かすため(物理では動かさない)
AudioSourceそのままでOK効果音を鳴らす

Is Trigger をオンにしたコライダーは、ぶつからずにすり抜けますが、「重なった」ことは知らせてくれます。カゴの中に入った瞬間を知りたいので、トリガーにしています。

Rigidbodyを付けるのは、Unityの当たり判定はどちらか一方にRigidbodyが付いていないと起きないからです。リンゴや爆弾は Translate で動かしていてRigidbodyがないので、バスケットの側に付けます。ただし、重力で落ちたり押されたりしては困るので、Is Kinematic をオンにして、物理の影響を受けないようにします(Igaguriで刺さったイガグリを止めたのと同じ設定です)。

イラスト:トリガーとKinematicなRigidbodyでキャッチを判定し、タグでリンゴと爆弾を見分ける流れ

ステップ2:キャッチしたら音を鳴らして消す(①の完成)

using UnityEngine;
using UnityEngine.InputSystem;  // 入力を検知するために必要!!

public class BasketController : MonoBehaviour
{
    public AudioClip appleSE;
    public AudioClip bombSE;
    AudioSource aud;

    void Start()
    {
        Application.targetFrameRate = 60;
        this.aud = GetComponent<AudioSource>();
    }

    // トリガーに何かが入った瞬間に呼ばれる
    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Apple")
        {
            this.aud.PlayOneShot(this.appleSE);
            Debug.Log("リンゴをキャッチ!");
        }
        else
        {
            this.aud.PlayOneShot(this.bombSE);
            Debug.Log("爆弾をキャッチ…");
        }
        // キャッチしたアイテムを消す
        Destroy(other.gameObject);
    }

    void Update()
    {
        if (Mouse.current.leftButton.wasPressedThisFrame)
        {
            Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.value);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit, Mathf.Infinity))
            {
                float x = Mathf.RoundToInt(hit.point.x);
                float z = Mathf.RoundToInt(hit.point.z);
                transform.position = new Vector3(x, 0, z);
            }
        }
    }
}

スクリプトを保存したら、basketのInspectorで Apple SE に get_se.mp3、Bomb SE に damage_se.mp3 をドラッグ&ドロップします。

OnTriggerEnter は3D用のトリガーのメソッドで、ClimbCloudで使った OnTriggerEnter2D の3D版です。引数も Collider2D ではなく Collider になります。

Unity 6でClimbCloudを作る② ― パラパラ漫画のアニメーションと、ゴール・シーンの切り替え(PC版)

PlayOneShot は、渡した音を1回だけ鳴らすメソッドです。AudioSourceに音をセットしておかなくても、その場で鳴らしたい音を渡せるので、リンゴと爆弾で音を切り替えられます。

再生して、リンゴや爆弾の下のマスをクリックしてみましょう。バスケットが移動し、アイテムがカゴに入ると音が鳴って消えます。Consoleにもメッセージが表示されます。

6. 数字で見るアイテムの落ち方

  • dropSpeed は -0.03 なので、1フレームに0.03ずつ下がる。60fpsなら、1秒で 1.8 下がる
  • アイテムは y = 3 からスタートし、カゴの口(y = 0.5 あたり)に届くまでにおよそ2.5下がるので、約1.4秒でキャッチされる
  • キャッチしなかったアイテムは、y = -1 より下になった時点(約2.2秒後)で消える

Translate の値は「1フレームあたり」なので、フレームレートが変わると落ちる速さも変わります。このサンプルでは Application.targetFrameRate = 60 で60fpsにそろえています。CatEscapeで説明した「フレーム」と「秒」の話と同じです。

7. よくあるつまずき

クリックしてもバスケットが動かない

  • ステージにBoxColliderが付いていない(Rayが当たる床がない)
  • カメラのTagが MainCamera になっておらず、Camera.main が見つからない
  • using UnityEngine.InputSystem; を書き忘れている

アイテムがカゴをすり抜ける(OnTriggerEnter が呼ばれない)

  • バスケットのBoxColliderの Is Trigger がオフになっている
  • バスケットに Rigidbody が付いていない(アイテムにもバスケットにもRigidbodyがないと、判定が起きない)
  • アイテムに SphereCollider が付いていない
  • メソッド名が OnTriggerEnter2D、引数が Collider2D になっている(3Dでは2Dが付かないほう)

バスケットが下に落ちていく

Rigidbodyの Is Kinematic がオフになっていて、重力で落ちています。オンにしましょう。

爆弾なのにリンゴの音が鳴る/全部爆弾扱いになる

タグの付け忘れか、スペルの違いです。"Apple" は大文字・小文字も区別されるので、タグの名前とスクリプトの文字列が完全に同じか確認しましょう。

音が鳴らない

basketに AudioSource が付いているか、Inspectorの Apple SE/Bomb SE 欄に音が入っているかを確認します。

まとめ

  • Physics.Raycast(ray, out hit, ...) で、Rayがどこに当たったかを調べられる。当たった場所は hit.point
  • Rayを受け止めるには、相手にコライダーが必要(今回はステージのBoxCollider)
  • Mathf.RoundToInt で座標を整数にそろえると、マス目の移動になる
  • タグを付けると、スクリプトから other.gameObject.tag で種類を見分けられる
  • 当たり判定には、どちらかにRigidbodyが必要。物理で動かしたくないときは Is Kinematic をオン
  • PlayOneShot で、その場で好きな効果音を1回鳴らせる

次回は、リンゴと爆弾をランダムに降らせる工場と、得点・残り時間のUIを作り、時間がたつほど難しくなるレベルデザインを加えて、ゲームを完成させます。

Unity 6でAppleCatchを作る② ― アイテムを降らせて、得点・時間とレベルデザインで仕上げる(PC版)

このシリーズの記事一覧(全10本)は、Unity 6 ゲーム制作 補足解説シリーズまとめからご覧いただけます。

参考書籍

北村愛実 著
『Unityの教科書 Unity 6完全対応版』
SBクリエイティブ
書籍サポートページ(サンプルファイル)

本記事では、書籍で扱われているAppleCatch(第8章)制作を題材に、初学者向けの補足説明やUnity 6での学習上のポイントを加えて解説しています。

訪問数 1 回, 今日の訪問数 1回

広告