方向キーで動く球体(プレイヤー)が、フィールドに配置されたアイテムに触れると回収(削除)されるゲームを実装します。これまでに学んだ Rigidbody・AddForce・Collider・Prefab を組み合わせて、インタラクティブなゲームの基本構造を作ります。
OnTriggerEnter でアイテムの回収を検知できるTransform.Rotate で継続的な回転アニメーションを実装できるStage にする| プロパティ | 値 |
|---|---|
| Position | X=0, Y=0, Z=0 |
| Scale | X=10, Y=0.5, Z=10 |
Player にするX=0, Y=0.75, Z=0 に設定する(ステージの上面に乗せる)
Player GameObject に Player スクリプトを作成してアタッチします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Player.cs
using UnityEngine;
using UnityEngine.InputSystem;
public class Player : MonoBehaviour
{
private Rigidbody _rigidbody;
private void Start()
{
_rigidbody = GetComponent<Rigidbody>();
}
private void Update()
{
var move = Vector3.zero;
if (Keyboard.current.rightArrowKey.isPressed) move.x += 1f;
if (Keyboard.current.leftArrowKey.isPressed) move.x -= 1f;
if (Keyboard.current.upArrowKey.isPressed) move.z += 1f;
if (Keyboard.current.downArrowKey.isPressed) move.z -= 1f;
_rigidbody.AddForce(move);
}
}
Play ボタンを押して方向キーで球体が動くことを確認しましょう。
Item にする| プロパティ | 値 |
|---|---|
| Position | X=2, Y=0.75, Z=2 |
| Rotation | X=45, Y=45, Z=45 |
| Scale | X=0.5, Y=0.5, Z=0.5 |
Item GameObject に Item スクリプトを作成してアタッチします。Transform.Rotate() は現在の向きに回転量を加算するメソッドです。Update 内で繰り返し呼ぶことで継続的な回転アニメーションになります。
1
2
3
4
5
6
7
8
9
10
// Item.cs
using UnityEngine;
public class Item : MonoBehaviour
{
private void Update()
{
transform.Rotate(0, 1f, 0, Space.World);
}
}
Space.World を指定すると、オブジェクト自身の傾きに関わらず世界座標の Y 軸を中心に回転します。
Item GameObject の Box Collider コンポーネントを開き、Is Trigger にチェックを入れます。これで Player が Item に触れてもぶつかって弾かれず、交差を検知できるようになります。

Hierarchy ビューの Item を Project ビューの Assets フォルダーにドラッグ & ドロップして、プレハブとして保存します。

プレハブを保存したら、Project ビューの Item プレハブを Scene ビューにドラッグ & ドロップして複数配置します。

Player スクリプトに OnTriggerEnter を追加して、アイテムへの接触時に削除します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Player.cs
using UnityEngine;
using UnityEngine.InputSystem;
public class Player : MonoBehaviour
{
private Rigidbody _rigidbody;
private void Start()
{
_rigidbody = GetComponent<Rigidbody>();
}
private void Update()
{
var move = Vector3.zero;
if (Keyboard.current.rightArrowKey.isPressed) move.x += 1f;
if (Keyboard.current.leftArrowKey.isPressed) move.x -= 1f;
if (Keyboard.current.upArrowKey.isPressed) move.z += 1f;
if (Keyboard.current.downArrowKey.isPressed) move.z -= 1f;
_rigidbody.AddForce(move);
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Item"))
{
Destroy(other.gameObject);
}
}
}
CompareTag("Item") は、触れた相手のタグが "Item" かどうかを調べます。これにより、Is Trigger がオンの別のオブジェクト(例: ゴールゾーンなど)を誤って削除することを防げます。
タグの設定: Item プレハブを選択し、Inspector 上部の Tag ドロップダウンから Add Tag… で
Itemタグを追加し、Item プレハブに設定してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using UnityEngine;
using UnityEngine.InputSystem;
public class Player : MonoBehaviour
{
private Rigidbody _rigidbody;
private void Start()
{
_rigidbody = GetComponent<Rigidbody>();
}
private void Update()
{
var move = Vector3.zero;
if (Keyboard.current.rightArrowKey.isPressed) move.x += 1f;
if (Keyboard.current.leftArrowKey.isPressed) move.x -= 1f;
if (Keyboard.current.upArrowKey.isPressed) move.z += 1f;
if (Keyboard.current.downArrowKey.isPressed) move.z -= 1f;
_rigidbody.AddForce(move);
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Item"))
{
Destroy(other.gameObject);
}
}
}
1
2
3
4
5
6
7
8
9
using UnityEngine;
public class Item : MonoBehaviour
{
private void Update()
{
transform.Rotate(0, 1f, 0, Space.World);
}
}
アイテムを回収するたびに得点が加算されるようにしましょう。
ヒント: Player スクリプトにスコアフィールドを追加し、OnTriggerEnter の中でカウントアップします。Debug.Log でスコアを表示してみましょう。
1
2
3
4
5
6
7
8
9
10
11
private int _score = 0;
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Item"))
{
Destroy(other.gameObject);
_score++;
Debug.Log($"スコア: {_score}");
}
}
アイテムによって得点が異なるようにしてみましょう。
ヒント: Item.cs に [SerializeField] public int point = 1; フィールドを追加し、Player.cs の OnTriggerEnter 内で other.GetComponent<Item>().point を読み取ります。
1
2
// Item.cs に追加
[SerializeField] public int point = 1;
1
2
3
4
5
6
7
8
9
10
11
// Player.cs の OnTriggerEnter
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Item"))
{
var item = other.GetComponent<Item>();
_score += item.point;
Debug.Log($"スコア: {_score}");
Destroy(other.gameObject);
}
}
Time.time を使って制限時間を実装しましょう。一定時間が経過したらゲームを停止して最終スコアを表示します。
ヒント: [SerializeField] private float _timeLimit = 30f; と private float _startTime; を組み合わせます。Start で _startTime = Time.time; を記録し、Update で Time.time - _startTime が _timeLimit を超えたら Debug.Log でスコアを表示して enabled = false; でスクリプトを止めます。
色の異なる別の Item プレハブを作り、触れると残り時間が増えるアイテムを実装してみましょう。
GetComponent<Rigidbody>() を Start でキャッシュし、AddForce で物理移動を実装したTransform.Rotate() を Update で繰り返し呼ぶことで継続的な回転アニメーションを作れるOnTriggerEnter でアイテム回収のような「交差検知」を実装できるCompareTag で相手の種類を識別することで誤動作を防げる_rigidbody = GetComponent<Rigidbody>() を Update ではなく Start に書く理由は何ですか?CompareTag("Item") を使わずに Destroy(other.gameObject) だけ書いた場合、何が問題になりますか?GetComponent はコストのかかる処理であり、毎フレーム呼ぶとパフォーマンスへの影響が積み重なるため。Start で一度だけ取得してフィールドに保持する。UnityEngine.Random で乱数を生成する では、ゲームでよく使う乱数生成の基本を学びます。