【学習】横スクロールアクション(ミニゲーム実践②)

2026年11月2日

広告

これまでの学習では、

  • 落ち物キャッチゲームで、プレイヤー・生成・当たり判定・UI を組み合わせた
  • 当たり判定で、Trigger と Collision の違いを学んだ

という形でゲームの基本を押さえてきました。

しかし、物理演算で床に立ち、ジャンプして敵を避けるゲームはまだ作っていません。

今回は横スクロールアクションの基本を作ります。床の上を左右に移動・ジャンプし、敵に当たるとゲームオーバーになるゲームです。

この記事の進め方

今日作るもの

  • プレイヤーが左右に動き、スペースでジャンプする(空中ではジャンプできない)
  • 床に立って、落下しない
  • カメラがプレイヤーを横に追いかける
  • 敵が左右に往復する
  • 敵に当たるとゲームオーバー(シーン切り替え)

必要なオブジェクト

オブジェクト役割
Ground床。Box Collider 2D のみ(Rigidbody2D なし)
Player左右移動+ジャンプ。Rigidbody2D(回転を固定)、Box Collider 2D
Enemy(プレハブ)左右に往復。Rigidbody2D(Kinematic)、Box Collider 2D
Main Cameraプレイヤーを横に追いかける(CameraFollow)
横スクロールアクションの構成Ground(床)PlayerジャンプleftLimitrightLimitEnemy敵にぶつかるとOnCollisionEnter2D→ GameOverScene
床の上でジャンプして敵を避ける。敵は左右に往復する

準備:プロジェクトとシーンを用意する

  1. Unity Hub で新しいプロジェクトを作成する(プロジェクト名:ActionGame、テンプレート:Universal 2D)
  2. Project ウィンドウの Assets → Scenes にある SampleScene を右クリック → Rename で、名前を GameScene に変える
  3. File → New Scene で新しいシーンを作り、Assets → Scenes に GameOverScene という名前で保存する(中身は次の第18回で作ります)
  4. File → Build Profiles を開き、Scene List に GameScene と GameOverScene を追加する(Project からドラッグ)。詳しくは「Build Profiles と Scene List」を参照
  5. Project の GameScene をダブルクリックして開き直す。ここから先は GameScene で作業します

Scene List に GameOverScene が入っていないと、敵に当たったときにエラーになります。最初に登録しておきましょう。


手順1:床を作る

  1. Hierarchy の「+」→ 2D Object → Sprites → Square で四角形を作成し、名前を Ground にする
  2. Transform の Position を (0, -4, 0)、Scale を (20, 1, 1) にして、横長の床にする
  3. Add Component で Box Collider 2D を追加する(Is Trigger はオフのまま)。動かない床なので Rigidbody2D は追加しません
  4. Inspector 上部の Tag → Add Tag… を選び、「+」で Ground というタグを作る。Ground オブジェクトを選び直して、Tag を Ground にする

手順2:プレイヤーを作る

  1. Hierarchy の「+」→ 2D Object → Sprites → Square で四角形を作成し、名前を Player にする。Position は (0, -2, 0)
  2. Add Component で Rigidbody2D を追加し、Gravity Scale を 3 にする。Constraints → Freeze Rotation Z をオンにする(ぶつかったときに転がらないようにするため)
  3. Add Component で Box Collider 2D を追加する(Is Trigger はオフのまま。床と物理的にぶつかるため)
  4. Tag を Player にする(最初から用意されているタグです)
  5. Add Component で Player Input を追加し、Actions に InputSystem_Actions、Default Map に Player を設定する。Behavior は Send Messages のままにする
  6. 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:カメラをプレイヤーに追従させる

このままだとカメラが動かず、プレイヤーが画面の外へ出てしまいます。カメラがプレイヤーを横に追いかけるようにします。

  1. CameraFollow スクリプトを作成して次のコードを書き、Main Camera にアタッチする
  2. 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:敵を作る

  1. Hierarchy の「+」→ 2D Object → Sprites → Square で四角形を作成し、名前を Enemy にする。Position を (3, -3, 0) にして床の上に置き、Sprite Renderer の Color を赤などに変える
  2. Add Component で Rigidbody2D を追加し、Body Type を Kinematic にする(重力で落ちず、スクリプトで動かすため)
  3. Add Component で Box Collider 2D を追加する(Is Trigger はオフのまま)
  4. Tag → Add Tag… で Enemy というタグを作り、Enemy の Tag を Enemy にする
  5. EnemyController スクリプトを作成して次のコードを書き、Enemy にアタッチする
  6. 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(何もない画面)に切り替わる

プログラムの流れ

流れ
プレイヤーが左右+ジャンプで移動
        ↓
床(Ground)と物理衝突して立つ
        ↓
敵が左右に往復
        ↓
プレイヤーが敵にぶつかると OnCollisionEnter2D
        ↓
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 をそれぞれ変える
  • アニメーション … プレイヤーの歩き、ジャンプのアニメーション
訪問数 109 回, 今日の訪問数 7回

広告

Unity

Posted by hidepon