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のマス目になったステージを、斜め上から見下ろすカメラ
- クリックしたマスへ、バスケットがパッと移動する
- 上から落ちてくるリンゴや爆弾をバスケットで受け止めると、音が鳴って消える
| 項目 | 内容 |
|---|---|
| Unity | Unity 6(サンプルは6000.1で作成) |
| テンプレート | Universal 3D(URP) |
| 使う素材 | stage.fbx、basket.fbx、apple.fbx、bomb.fbx、get_se.mp3、damage_se.mp3 |
1. ステージとカメラを準備する
Universal 3D のテンプレートでプロジェクトを作り、素材をProjectウィンドウに入れたら、次のように配置します。
| オブジェクト | Position | Rotation | メモ |
|---|---|---|---|
| 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 とは
Igaguriでは、Rayの向き(ray.direction)だけを使いました。今回は Physics.Raycast で、実際にRayを飛ばしてどこに当たったかを調べています。
| 書き方 | 意味 |
|---|---|
Physics.Raycast(ray, out hit, Mathf.Infinity) | Rayを飛ばして、コライダーに当たったら true を返す |
out hit | 当たった情報を hit に入れてもらう |
Mathf.Infinity | どこまでも遠くまで調べる(距離の上限なし) |
hit.point | Rayが当たった場所の座標(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)を使います。
- リンゴを選び、Inspectorの一番上にある Tag のメニューから Add Tag… を選ぶ
- + ボタンで
AppleとBombの2つのタグを作る - リンゴのTagを
Apple、爆弾のTagをBombに設定する
タグは、オブジェクトに付ける「名札」のようなものです。スクリプトからは other.gameObject.tag で読み取れます。
タグを付けたら、リンゴと爆弾をそれぞれProjectウィンドウへドラッグして applePrefab、bombPrefab というPrefabにしておきます(シーンのものは動作確認用に残しておいてかまいません)。
5. バスケットでキャッチする
バスケットにトリガーとRigidbodyを付ける
basketに、次の3つのコンポーネントを追加します。
| コンポーネント | 設定 | 役割 |
|---|---|---|
| BoxCollider | Center (0, 0.5, 0)/Size (0.5, 0.1, 0.5)/Is Trigger:オン | カゴの口のあたりに、すり抜ける当たりを作る |
| Rigidbody | Is Kinematic:オン | トリガーの判定を動かすため(物理では動かさない) |
| AudioSource | そのままでOK | 効果音を鳴らす |
Is Trigger をオンにしたコライダーは、ぶつからずにすり抜けますが、「重なった」ことは知らせてくれます。カゴの中に入った瞬間を知りたいので、トリガーにしています。
Rigidbodyを付けるのは、Unityの当たり判定はどちらか一方にRigidbodyが付いていないと起きないからです。リンゴや爆弾は Translate で動かしていてRigidbodyがないので、バスケットの側に付けます。ただし、重力で落ちたり押されたりしては困るので、Is Kinematic をオンにして、物理の影響を受けないようにします(Igaguriで刺さったイガグリを止めたのと同じ設定です)。

ステップ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での学習上のポイントを加えて解説しています。











ディスカッション
コメント一覧
まだ、コメントがありません