【学習】横スクロールアクション(ミニゲーム実践②)
これまでの学習では、
- 落ち物キャッチゲームで、プレイヤー・生成・当たり判定・UI を組み合わせた
- 当たり判定で、Trigger と Collision の違いを学んだ
という形でゲームの基本を押さえてきました。
しかし、物理演算で床に立ち、ジャンプして敵を避けるゲームはまだ作っていません。
今回は横スクロールアクションの基本を作ります。床の上を左右に移動・ジャンプし、敵に当たるとゲームオーバーになるゲームです。
この記事の進め方
- Unity Hub で新しいプロジェクトを作成して始めます(プロジェクト名:ActionGame、テンプレート:Universal 2D)
- 最初に「準備」でシーンを用意してから、床 → プレイヤー → カメラ → 敵 → ゲームオーバー の順に作ります。各手順の中でスクリプトを作ってからアタッチするので、上から順に進めればゲームが完成します
- このプロジェクトは、次の「スコアとゲームオーバー」でも続けて使います
- スクリプトの作り方は「C# スクリプトの作成とアタッチ」、入力の設定は「プレイヤーを動かす」、当たり判定は「当たり判定(Collider)」、シーンの登録は「シーンの切り替え」で確認できます
今日作るもの
- プレイヤーが左右に動き、スペースでジャンプする(空中ではジャンプできない)
- 床に立って、落下しない
- カメラがプレイヤーを横に追いかける
- 敵が左右に往復する
- 敵に当たるとゲームオーバー(シーン切り替え)
必要なオブジェクト
| オブジェクト | 役割 |
|---|---|
| Ground | 床。Box Collider 2D のみ(Rigidbody2D なし) |
| Player | 左右移動+ジャンプ。Rigidbody2D(回転を固定)、Box Collider 2D |
| Enemy(プレハブ) | 左右に往復。Rigidbody2D(Kinematic)、Box Collider 2D |
| Main Camera | プレイヤーを横に追いかける(CameraFollow) |
準備:プロジェクトとシーンを用意する
- Unity Hub で新しいプロジェクトを作成する(プロジェクト名:ActionGame、テンプレート:Universal 2D)
- Project ウィンドウの Assets → Scenes にある SampleScene を右クリック → Rename で、名前を GameScene に変える
- File → New Scene で新しいシーンを作り、Assets → Scenes に GameOverScene という名前で保存する(中身は次の第18回で作ります)
- File → Build Profiles を開き、Scene List に GameScene と GameOverScene を追加する(Project からドラッグ)。詳しくは「Build Profiles と Scene List」を参照
- Project の GameScene をダブルクリックして開き直す。ここから先は GameScene で作業します
Scene List に GameOverScene が入っていないと、敵に当たったときにエラーになります。最初に登録しておきましょう。
手順1:床を作る
- Hierarchy の「+」→ 2D Object → Sprites → Square で四角形を作成し、名前を Ground にする
- Transform の Position を (0, -4, 0)、Scale を (20, 1, 1) にして、横長の床にする
- Add Component で Box Collider 2D を追加する(Is Trigger はオフのまま)。動かない床なので Rigidbody2D は追加しません
- Inspector 上部の Tag → Add Tag… を選び、「+」で Ground というタグを作る。Ground オブジェクトを選び直して、Tag を Ground にする
手順2:プレイヤーを作る
- Hierarchy の「+」→ 2D Object → Sprites → Square で四角形を作成し、名前を Player にする。Position は (0, -2, 0)
- Add Component で Rigidbody2D を追加し、Gravity Scale を 3 にする。Constraints → Freeze Rotation Z をオンにする(ぶつかったときに転がらないようにするため)
- Add Component で Box Collider 2D を追加する(Is Trigger はオフのまま。床と物理的にぶつかるため)
- Tag を Player にする(最初から用意されているタグです)
- Add Component で Player Input を追加し、Actions に InputSystem_Actions、Default Map に Player を設定する。Behavior は Send Messages のままにする
- PlayerController スクリプトを作成して次のコードを書き、Player にアタッチする
PlayerController.cs
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
[SerializeField] private float speed = 5f;
[SerializeField] private float jumpForce = 12f;
private Rigidbody2D rb;
private Vector2 moveInput;
private bool isGrounded; // 床に立っているか
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void OnMove(InputValue value)
{
moveInput = value.Get<Vector2>();
}
void OnJump(InputValue value)
{
// 床に立っているときだけジャンプできる
if (value.isPressed && isGrounded)
{
rb.AddForce(new Vector2(0, jumpForce), ForceMode2D.Impulse);
isGrounded = false;
}
}
void Update()
{
float moveX = moveInput.x * speed;
rb.linearVelocity = new Vector2(moveX, rb.linearVelocity.y);
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isGrounded = true;
}
}
}
OnJump は、スペースキー(InputSystem_Actions の Jump)が押されたときに呼ばれます。isGrounded が true のとき、つまり床に立っているときだけジャンプし、ジャンプしたら false に戻します。床に着地すると OnCollisionEnter2D で再び true になります。
※ Unity 6 では Rigidbody2D.velocity が linearVelocity という名前になっています。以前の Unity では rb.velocity と書きます。
ここで ▶ を押して再生し、左右に動けること、ジャンプは床に立っているときだけできることを確認しましょう。
手順3:カメラをプレイヤーに追従させる
このままだとカメラが動かず、プレイヤーが画面の外へ出てしまいます。カメラがプレイヤーを横に追いかけるようにします。
- CameraFollow スクリプトを作成して次のコードを書き、Main Camera にアタッチする
- Main Camera の Inspector にある Target 欄に、Hierarchy の Player をドラッグする
CameraFollow.cs
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
[SerializeField] private Transform target; // 追いかける相手(Player)
void LateUpdate()
{
if (target == null) return;
// 横(x)だけプレイヤーに合わせ、高さと奥行きはそのまま
transform.position = new Vector3(
target.position.x, transform.position.y, transform.position.z);
}
}
LateUpdate は、すべての Update が終わったあとに呼ばれます。プレイヤーが動いたあとでカメラを合わせるので、画面がガタつきません。
手順4:敵を作る
- Hierarchy の「+」→ 2D Object → Sprites → Square で四角形を作成し、名前を Enemy にする。Position を (3, -3, 0) にして床の上に置き、Sprite Renderer の Color を赤などに変える
- Add Component で Rigidbody2D を追加し、Body Type を Kinematic にする(重力で落ちず、スクリプトで動かすため)
- Add Component で Box Collider 2D を追加する(Is Trigger はオフのまま)
- Tag → Add Tag… で Enemy というタグを作り、Enemy の Tag を Enemy にする
- EnemyController スクリプトを作成して次のコードを書き、Enemy にアタッチする
- Hierarchy の Enemy を Project ウィンドウへドラッグしてプレハブにする。Hierarchy の Enemy は削除せず、そのまま残しておく
EnemyController.cs
using UnityEngine;
public class EnemyController : MonoBehaviour
{
[SerializeField] private float speed = 2f;
[SerializeField] private float leftLimit = -4f;
[SerializeField] private float rightLimit = 4f;
private int direction = 1;
void Update()
{
transform.Translate(speed * direction * Time.deltaTime, 0, 0);
if (transform.position.x >= rightLimit) direction = -1;
if (transform.position.x <= leftLimit) direction = 1;
}
}
direction が 1 なら右、-1 なら左に進みます。Right Limit を超えたら左向きに、Left Limit を下回ったら右向きに切り替えて往復します。
手順5:敵に当たったらゲームオーバー
PlayerController を開き、敵に当たったときに GameOverScene へ切り替える処理を追加します。「追加」とコメントがある2か所が、手順2のコードからの変更点です。
PlayerController.cs(完成版)
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement; // 追加
public class PlayerController : MonoBehaviour
{
[SerializeField] private float speed = 5f;
[SerializeField] private float jumpForce = 12f;
private Rigidbody2D rb;
private Vector2 moveInput;
private bool isGrounded; // 床に立っているか
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void OnMove(InputValue value)
{
moveInput = value.Get<Vector2>();
}
void OnJump(InputValue value)
{
// 床に立っているときだけジャンプできる
if (value.isPressed && isGrounded)
{
rb.AddForce(new Vector2(0, jumpForce), ForceMode2D.Impulse);
isGrounded = false;
}
}
void Update()
{
float moveX = moveInput.x * speed;
rb.linearVelocity = new Vector2(moveX, rb.linearVelocity.y);
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isGrounded = true;
}
// 追加:敵に当たったらゲームオーバー
if (collision.gameObject.CompareTag("Enemy"))
{
SceneManager.LoadScene("GameOverScene");
}
}
}
敵も床も Is Trigger オフなので、どちらに当たっても OnCollisionEnter2D が呼ばれます。タグを見て、床なら着地、敵ならゲームオーバーと処理を分けています。
動作確認
GameScene を開いた状態で ▶ を押して再生し、次の5つを確認しましょう。
- 左右キーで動き、スペースでジャンプできる
- 空中ではジャンプできない(連打しても1回だけ)
- プレイヤーが転がらない
- カメラがプレイヤーを横に追いかける
- 敵に当たると GameOverScene(何もない画面)に切り替わる
プログラムの流れ
重要ポイント
横スクロールアクションでは、物理演算と当たり判定の使い分けが重要です。
- 床 … Box Collider 2D だけ(Is Trigger オフ)。動かないので Rigidbody2D は不要
- プレイヤー … Rigidbody2D(Dynamic)で重力とジャンプ。回転を固定し、接地判定で連続ジャンプを防ぐ
- 敵 … Rigidbody2D(Kinematic)で、物理演算の影響を受けずにスクリプトで往復
- 当たり判定 … Is Trigger オフ同士なので OnCollisionEnter2D。タグで床か敵かを見分ける
- シーン切り替え … SceneManager.LoadScene で GameOverScene へ
発展アイデア
- コイン … Trigger のコインを置いて、触れたらスコアを加算する(次の「スコアとゲームオーバー」につながります)
- 落下でゲームオーバー … プレイヤーの y 座標が一定より下になったら GameOverScene へ
- 複数の敵 … Enemy プレハブを並べ、Left Limit / Right Limit をそれぞれ変える
- アニメーション … プレイヤーの歩き、ジャンプのアニメーション








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