オブジェクトがCameraの描画外にいるか判定する方法

By:
April 26, 2022

画面外に出たオブジェクトに対して破棄だったりあるいは2DシューティングでみられるScreenWrappingなどの実装方法。

↓昔の2Dシューティングゲームとかである画面外に出たら反対側から出現するみたいな処理で画面外にいたら~な判定をとるコード。

このゲームでは隕石やプレイヤーの弾、プレイヤー自身が画面外に出たら反対側に出現するようにデザインされている。

以下サンプルコード

using UnityEngine;

/// <summary>
/// 自身が画面外に出たら自動でラッピング( 画面の反対側へ移動 )処理をする.
/// </summary>
public class ScreenWrapper : MonoBehaviour
{
	private const float WrappingMargin = 0.01f;
	
	private Camera camera;
	private bool isInCameraViewPort;
	
	
	[SerializeField] 
	private float horizontalOffset = 0f;
	private Vector3 HorizontalOffsetVector3 => new Vector3(horizontalOffset, 0f, 0f);
	
	[SerializeField] 
	private float verticalOffset = 0f;
	private Vector3 VerticalOffsetVector3 => new Vector3(0f, verticalOffset, 0f);

	
	private void Start()
	{
		camera = Camera.main;
	}
	

	private void Update()
	{
		Vector3 position = transform.position;
		bool leftOver = camera.WorldToViewportPoint(position + HorizontalOffsetVector3).x <= 0f;
		bool rightOver = camera.WorldToViewportPoint(position - HorizontalOffsetVector3).x >= 1f;
		bool topOver = camera.WorldToViewportPoint(position - VerticalOffsetVector3).y >= 1f;
		bool bottomOver = camera.WorldToViewportPoint(position + VerticalOffsetVector3).y <= 0f;
		if (!leftOver && !rightOver && !bottomOver && !topOver) return;
		
		// オブジェクトが画面外なのでラップ処理.
		float horizontal = position.x;
		if (leftOver) horizontal = -position.x - horizontalOffset - WrappingMargin;
		if (rightOver) horizontal = -position.x + horizontalOffset + WrappingMargin;

		float vertical = position.y;
		if (topOver) vertical = -position.y + verticalOffset + WrappingMargin;
		if (bottomOver) vertical = -position.y - verticalOffset - WrappingMargin;
		transform.position = new Vector3(horizontal, vertical, 0f);
	}
}

ViewPortはX軸Y軸ともに0 ~ 1なのでWorld座標をViewPort座標に変換しそれぞれが画面外判定になったら反対側の座標に移動する。

An image from Notion

インスペクタはこんな感じ、オブジェクトが完全に画面外に出る前にラップするナイーブラッピングを防止するために水平、垂直それぞれにオフセットを設定しておく。

挙動確認