Unityでカメラがステージからはみ出さないようにする方法

pic01
LINEで送る
Pocket

こんにちは!ポケラボではエンジニアとしてゲームを作っているtomoです。
今回はUnityでゲームを作るとき使っている小技を紹介します。
ゲームを作るときカメラをプレイヤーキャラクターに追随させるようにすることがあると思いますが、
プレイヤーがステージの端まで行ったときに見えてほしくない領域までカメラの範囲に入ってしまうことがあるかと思います。
そんなときにカメラの移動範囲を制限する設定方法を紹介します。

ステージの端が見えてしまっている!

手順としては以下の通りです。

1.ステージの範囲を設定する
2.カメラの移動範囲を制限する

ステージの範囲を設定する

まずはステージの範囲を設定するのにRectクラスを使用します。

Rect – Unity DOCUMENTATION
http://docs.unity3d.com/jp/current/ScriptReference/Rect.html

Rectクラスにはx,y,width,heightのプロパティを持たせることができ、2次元の範囲を定義できるクラスです。
ステージはStageというGameObjectに地面(Floor)と足場(Step)を2つ設置して構成しています。
Stageに以下のスクリプトを設定します。


using UnityEngine;
using System.Collections;

public class StageController : MonoBehaviour {

	private Transform floor;
	public float StageHeight = 10f;//ステージの高さ(任意に設定可)

	public Rect StageRect;//ステージの範囲をRectで設定
	public Vector3 LowerLeft;
	public Vector3 UpperLeft;
	public Vector3 LowerRight;
	public Vector3 UpperRight;

	//ステージ範囲
	void OnDrawGizmos()
	{
		LowerLeft = new Vector3 (StageRect.xMin, StageRect.yMax, 0);
		UpperLeft = new Vector3 (StageRect.xMin, StageRect.yMin, 0);
		LowerRight = new Vector3 (StageRect.xMax, StageRect.yMax, 0);
		UpperRight = new Vector3 (StageRect.xMax, StageRect.yMin, 0);

		Gizmos.color = Color.red;
		Gizmos.DrawLine(LowerLeft, UpperLeft);
		Gizmos.DrawLine(UpperLeft, UpperRight);
		Gizmos.DrawLine(UpperRight, LowerRight);
		Gizmos.DrawLine(LowerRight, LowerLeft);
	}

	void Start()
	{
		//地面を取得
		floor = transform.Find("Floor");
		//地面のColliderを元にRectを設定
		Bounds floorBounds = floor.GetComponent().bounds;
		StageRect.xMin = floorBounds.min.x;
		StageRect.xMax = floorBounds.max.x;
		StageRect.yMin = floorBounds.min.y;
		StageRect.yMax = floorBounds.max.y+StageHeight;
	}

	void Update()
	{

	}

}

これでFloorを基準としたステージ範囲を設定できました。Sceneビュー上にGizmoを表示させてステージ範囲を視覚化させています。

赤いラインがステージ範囲です

カメラの移動範囲を制限する

あとはカメラを以下の条件を満たすように設定します。
・プレイヤーキャラクターを追随する
・カメラの表示領域がステージ範囲を越えないようにする

Main Cameraに以下のスクリプトを設定します。

using UnityEngine;
using System.Collections;

public class CameraController : MonoBehaviour {

	public GameObject target;
	private Vector3 offsetPosition;
	private StageController stageController;
	private float distance = 10f;//プレイヤーキャラまでの距離。固定で設定

	//カメラの表示領域表示用
	private Vector3 cameraBottomLeft;
	private Vector3 cameraTopLeft;
	private Vector3 cameraBottomRight;
	private Vector3 cameraTopRight;
	public float cameraRangeWidth;
	public float cameraRangeHeight;

	//カメラの表示領域を緑ラインで表示
	void OnDrawGizmos()
	{
		Gizmos.color = Color.green;
		Gizmos.DrawLine(cameraBottomLeft, cameraTopLeft);
		Gizmos.DrawLine(cameraTopLeft, cameraTopRight);
		Gizmos.DrawLine(cameraTopRight, cameraBottomRight);
		Gizmos.DrawLine(cameraBottomRight, cameraBottomLeft);
	}

	void Start()
	{
		//プレイヤーキャラを取得
		target = GameObject.FindGameObjectWithTag("Player");
		offsetPosition = transform.position - target.transform.position;
		//ステージコントローラーを取得
		stageController = GameObject.Find("Stage").GetComponent();
	}

	void Update()
	{
		Vector3 newPosition;//カメラの目標座標
		Vector3 limitPosition;//制限されたカメラの目標座標
		float newX = 0f;
		float newY = 0f;
		//プレイヤーキャラの位置にカメラの座標を設定する。キャラのちょっと上にする
		newPosition = target.transform.position + offsetPosition + Vector3.up*3f;
		//ビューポート座標をワールド座標に変換
		cameraBottomLeft = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, distance));
		cameraTopRight = Camera.main.ViewportToWorldPoint(new Vector3(1, 1, distance));
		cameraTopLeft = new Vector3(cameraBottomLeft.x, cameraTopRight.y, cameraBottomLeft.z);
		cameraBottomRight = new Vector3(cameraTopRight.x, cameraBottomLeft.y, cameraTopRight.z);
		cameraRangeWidth = Vector3.Distance(cameraBottomLeft, cameraBottomRight);
		cameraRangeHeight = Vector3.Distance(cameraBottomLeft, cameraTopLeft);
		//カメラの稼働領域をステージ領域に制限
		newX = Mathf.Clamp(newPosition.x, stageController.StageRect.xMin + cameraRangeWidth/2, stageController.StageRect.xMax-cameraRangeWidth/2);
		newY = Mathf.Clamp(newPosition.y, 0, stageController.StageRect.yMax - cameraRangeHeight/2);
		//座標をカメラ位置に設定
		limitPosition = new Vector3(newX,newY,this.transform.position.z);
		transform.position = limitPosition;
	}

}

ポイントはビューポート座標をワールド座標に変換してカメラのワールド上での表示領域を割り出しているところです。
ViewportToWorldPoint()にVector3(0, 0, distance)を渡してカメラの左下の座標を求め、同じようにVector3(1, 1, distance)を渡して右上の座標を求めてそこから左上、右下の座標を出しカメラの表示範囲を割り出します。あとはMathf.Clamp()でx座標とy座標をステージ範囲内に制限しています。

緑のラインがカメラの表示領域です

 これでプレイヤーキャラがステージ端に走って行っても…

カメラがステージ内で止まりました!

シーンビューで確認するとカメラのラインがステージ領域内にちゃんと収まっています。

今回は2D視点横スクロールタイプのステージを例にしていますが、見下ろし視点のゲームでも簡単に応用できると思います。

以上です!