Unityでのプレイヤー画面内制限実装ガイド

Unityで2Dゲームなどを制作する際、プレイヤーが画面外に出ないように位置を制限することが求められます。ここでは、固定値指定動的な境界値計算の2つのアプローチを紹介します。
まずは、画面サイズやプレイヤーサイズが固定の場合に利用できる固定値指定の方法から見ていきます。


1. 固定値指定による実装例

1.1 概要

環境(カメラ位置、画面サイズ、プレイヤーサイズなど)が固定の場合、左右の境界をあらかじめ決めた数値で指定できます。
この方法は実装がシンプルで、以下のようにMathf.Clampを利用してプレイヤーのX座標を直接制限します。

1.2 完全なコード例

using UnityEngine;

public class PlayerControllerFixed : MonoBehaviour
{
    public float moveDistance = 3f;  // 移動する距離

    void Start()
    {
        Application.targetFrameRate = 60;
    }

    void Update()
    {
        // 左右の移動入力処理
        if (Input.GetKeyDown(KeyCode.LeftArrow))
        {
            transform.Translate(-moveDistance, 0, 0);
        }
        if (Input.GetKeyDown(KeyCode.RightArrow))
        {
            transform.Translate(moveDistance, 0, 0);
        }

        // 毎フレーム、X座標を固定値(ここでは-8~8)にClampして画面内に留める
        ClampPosition();
    }

    void ClampPosition()
    {
        Vector3 pos = transform.position;
        pos.x = Mathf.Clamp(pos.x, -8f, 8f);  // 左右の境界を-8~8に固定
        transform.position = pos;
    }
}

1.3 ポイント

  • シンプルな実装:
    環境が一定である場合、直接数値を指定するため、動的計算よりもコードが簡潔になります。
  • 環境の変化には非対応:
    カメラの移動、ズーム、解像度の変更が発生する場合には、固定値指定では対応できないため、別途動的な計算が必要になります。

固定値指定は、例えば以下のような状況では問題が発生します:

  1. カメラが追従移動する場合:
    ゲームによってはカメラがプレイヤーに追随して移動します。固定値で左右の境界を設定していると、これらの境界はワールド座標上の固定の位置になります。
    • 例:
      プレイヤーが移動するとカメラも同じ方向に移動するのに、Clampの境界は固定されているため、実際のカメラの表示範囲(ビューポート)と合わなくなり、プレイヤーが画面内に見えているのに不自然な制限がかかる、または逆に画面外に出てしまう可能性があります。
  2. カメラのズーム(拡大・縮小)が発生する場合:
    カメラがズームインやズームアウトすると、画面上に表示される範囲が変化します。固定値のClampはワールド座標に対してのみ機能するため、ズームによって実際の表示範囲と設定した境界がずれてしまい、プレイヤーが意図せず画面外に出てしまう、または画面内に留まっているにもかかわらず不自然な位置に固定されることがあります。
  3. 解像度や画面サイズの変更:
    ゲームが異なる解像度やウィンドウサイズに対応する必要がある場合、固定値指定では各環境に対して適切な表示範囲を確保できません。環境ごとに実際の画面の境界は変わるため、動的な計算が必要になります。

まとめると、固定値指定はシーンやゲームの設定が常に一定の場合には有効ですが、カメラの移動、ズーム、解像度変更など動的な環境変化があると、ワールド座標上の固定の境界が実際のカメラ表示範囲と一致しなくなり、プレイヤーの表示位置に不整合が生じる可能性があります。こうした場合には、カメラの情報(位置、ズーム、アスペクト比など)に基づいて動的に境界値を計算する方法が推奨されます。


2. 動的な境界値計算を利用した実装例

2.1 概要

正射影カメラを使用している場合、Camera.main.orthographicSize は画面の垂直半分のサイズとなります。
また、画面のアスペクト比(Screen.width / Screen.height)を掛けることで、水平半分サイズ(horizontalExtent)が算出できます。
さらに、プレイヤーの実際のサイズは、SpriteRendererのbounds.extents.xで取得できるため、より正確な境界計算が可能です。

2.2 完全なコード例

using UnityEngine;

public class PlayerControllerDynamic : MonoBehaviour
{
    public float moveDistance = 3f;  // 移動する距離

    void Start()
    {
        Application.targetFrameRate = 60;
    }

    void Update()
    {
        // 左矢印キーで左へ移動
        if (Input.GetKeyDown(KeyCode.LeftArrow))
        {
            transform.Translate(-moveDistance, 0, 0);
        }

        // 右矢印キーで右へ移動
        if (Input.GetKeyDown(KeyCode.RightArrow))
        {
            transform.Translate(moveDistance, 0, 0);
        }

        // 毎フレームClampして画面外に出ないように制限
        ClampPosition();
    }

    void ClampPosition()
    {
        // 現在の位置を取得
        Vector3 pos = transform.position;

        // カメラが正射影の場合、orthographicSizeは画面の垂直半分サイズ
        float cameraHeight = Camera.main.orthographicSize;
        // 画面のアスペクト比から水平半分サイズを算出
        float screenRatio = (float)Screen.width / Screen.height;
        float horizontalExtent = cameraHeight * screenRatio;

        // SpriteRendererから実際の半幅を取得(ピボットが中央の場合)
        float actualHalfWidth = GetComponent<SpriteRenderer>().bounds.extents.x;

        // カメラの中央位置から画面の左右境界を計算(プレイヤーのサイズを考慮)
        float minX = Camera.main.transform.position.x - horizontalExtent + actualHalfWidth;
        float maxX = Camera.main.transform.position.x + horizontalExtent - actualHalfWidth;

        // Mathf.ClampでX座標を境界内に制限
        pos.x = Mathf.Clamp(pos.x, minX, maxX);
        transform.position = pos;
    }
}

2.3 ポイント

  • 動的な境界計算:
    カメラや画面サイズが変化する場合でも、実行時に正しい境界値を計算するため、プレイヤーが常に画面内に収まります。
  • 実際のサイズ利用:
    SpriteRendererのbounds.extentsを用いることで、手動で指定するよりも正確にプレイヤーのサイズに合わせたClampが可能です。

3. まとめ

  • 固定値指定:
    • シンプルな実装で、環境が一定の場合に有効
    • 例:Mathf.Clamp(pos.x, -8f, 8f);
    • 環境の変化(カメラの移動やズーム、解像度変更)には対応できない
  • 動的な境界値計算:
    • カメラのorthographicSizeとアスペクト比から正確な左右の境界を算出
    • SpriteRendererなどから実際のサイズを取得してClampに利用
    • カメラや画面サイズが変動する場合にも柔軟に対応可能

用途や環境に合わせて、最適な実装方法を選択することで、プレイヤーが画面外に出ないように確実に制御できます。

Unity

Posted by hidepon