【Unity】オブジェクトが画面外かどうかを判定する方法(まとめ)
ゲームを作っていると「キャラクターやオブジェクトが画面外か画面内か」で処理を分けたいことがありますが、Unityでその判定をする方法がいくつかあって分かりにくかったので、調べたことを記事にしてまとめておきます。
環境
- Unity 2018.1.0f3
使用アセット
判定方法
Renderer.isVisibleを使う方法
- カメラに表示されているかをtrue, falseで返してくれる
- カメラが複数ある場合、どのカメラにも写っていない場合のみfalseになる
- カメラにはシーンビュー用のカメラも含まれるので注意(ゲームビューに写っていなくてもシーンビューに写っていればtrueになる)
- オブジェクトの後ろに隠れていてカメラに写っていなくても、カメラの描画範囲内にあればtrueが返ってくる
- 「画面外で生成される場合はNG」という情報をたまに見かけましたが、テストした限りでは画面外で生成してもちゃんと動いてました

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class IsVisible : MonoBehaviour { Renderer targetRenderer; // 判定したいオブジェクトのrendererへの参照 void Start () { targetRenderer = GetComponent<Renderer>(); } void Update () { if(targetRenderer.isVisible) { // 表示されている場合の処理 ShowText("画面に表示されてるよ"); } else { // 表示されていない場合の処理 ShowText("画面から消えたよ"); } } // 以下はサンプルのUI表示用 [SerializeField] Text uiText; void ShowText(string message) { uiText.text = message; } } |
- Renderer.isVisibleの参考リンク
- 公式スクリプトリファレンス(日本語版最新)
OnBecameVisible、OnBecameInVisibleを使う方法
- OnBecameVisibleはカメラに表示されるようになった時、OnBecameInVisibleはカメラに表示されなくなった時に一度だけ呼ばれる
- RendererがアタッチされているオブジェクトにスクリプトをつけておけばOK(GetComponentで参照を取得しなくてもいい)
- カメラが複数ある場合、どのカメラにも映っていない場合のみfalseになる
- カメラにはシーンビュー用のカメラも含まれるので注意(ゲームビューに映っていなくてもシーンビューに映っていればtrueになる)
- オブジェクトの後ろに隠れていてカメラに写っていなくても、カメラの描画範囲内にあればtrueが返ってくる

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class BecameVisible : MonoBehaviour { void Start() { } void OnBecameVisible() { // 表示されるようになった時の処理 ShowText("表示されるようになったよ"); } void OnBecameInvisible() { // 表示されなくなった時の処理 ShowText("表示されなくなったよ"); } // 以下はサンプルのUI表示用 [SerializeField] Text uiText; void ShowText(string message) { uiText.text = message; } } |
- OnBecameVisible、OnBecameInVisibleの参考リンク
- 公式スクリプトリファレンス(日本語版最新)
- 【Unity】オブジェクトがカメラに写っているかと注意点 2017.02.13
- カメラに写っている間のみ処理を行う 2013.10.07
OnWillRenderObjectを使う方法
- カメラの表示対象になっている場合、各カメラ毎に呼ばれる
- Camera.currentで映っているカメラが取得できる
- シーンビュー用のカメラも対象になるが、カメラ毎に処理を切り替え可能なのでゲームビューのみを対象にすることもできる
- RendererがアタッチされているオブジェクトにスクリプトをつけておけばOK(GetComponentで参照を取得しなくてもいい)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class WillRenderObj : MonoBehaviour { void Start () { } void Update() { ShowText(cameraNum); cameraNum = 0; } void OnWillRenderObject() { if(Camera.current.name == "SceneCamera") cameraNum++; if(Camera.current.name == "Main Camera") cameraNum += 2; } // 以下はサンプルのUI表示用 [SerializeField] Text uiText; int cameraNum; void ShowText(int num) { switch(num) { case 0: uiText.text = "映ってないよ"; break; case 1: uiText.text = "シーンカメラに映ってるよ"; break; case 2: uiText.text = "メインカメラに映ってるよ"; break; case 3: uiText.text = "両方のカメラに映ってるよ"; break; } } } |
- OnWillRenderObjectの参考リンク
- 公式スクリプトリファレンス(日本語版最新)
- 特定のカメラに映ってるかの判定【Unity】 2014.12.29
- カメラに写っている間のみ処理を行う 2013.10.07
Camera.ViewportPointToRayを使う方法
- カメラの四隅にRayを飛ばすことができる(四隅以外にも飛ばせます)
- Rayを飛ばす位置の指定はビューポート座標系(左下は(0, 0)、右上は(1, 1))
- Unityの座標系について、詳しくはこちらの記事(UnityのCameraが使う3つの座標系)を参照してください
- Rayが当たったポイントの座標から画面外かどうかを判定することができる
- Rayが何かに当たる前提なので、何もない可能性がある場合は使えない

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class ViewPortToRay : MonoBehaviour { Camera targetCamera; // 映っているか判定するカメラへの参照 [SerializeField] Transform targetObj; // 映っているか判定する対象への参照。inspectorで指定する Vector3 lb; // 左下の座標 Vector3 rt; // 右上の座標 // Use this for initialization void Start () { targetCamera = GetComponent<Camera>(); lb = GetRayhitPos(new Vector3(0, 0, 0)); // 左下 rt = GetRayhitPos(new Vector3(1, 1, 0)); // 右上 } void Update() { if(CheckInScreen()) ShowText("画面内にいるよ"); else ShowText("画面外だよ"); } // カメラからRayを飛ばしてhitした位置を返す Vector3 GetRayhitPos(Vector3 targetPos) { Ray ray = targetCamera.ViewportPointToRay(targetPos); RaycastHit hit; if (Physics.Raycast(ray, out hit)) return hit.point; return Vector3.zero; // 何もヒットしなければ(0, 0, 0)を返す } // 対象のオブジェクトの位置から画面内かどうか判定して返す bool CheckInScreen() { // テスト環境のカメラが変な方向を向いてたので少しおかしな判定文になっています // ここの条件式は適宜、修正してください if(targetObj.position.x < rt.x || lb.x < targetObj.position.x || targetObj.position.z < lb.z || rt.z < targetObj.position.z) return false; return true; } // 以下はサンプルのUI表示用 [SerializeField] Text uiText; void ShowText(string message) { uiText.text = message; } } |
- Camera.ViewportPointToRayの参考リンク
- 公式スクリプトリファレンス(日本語版最新)
- 公式マニュアル カメラからのRay
Camera.ViewportToWorldPointを使う方法
- カメラのビューポート空間の座標をワールド空間の座標に変換してくれる
- Unityの座標系について、詳しくはこちらの記事(UnityのCameraが使う3つの座標系)を参照してください
- 一つ前のCamera.ViewportPointToRayと似ているが、こちらは「カメラと水平な面の座標」しか取れない(後で画像で説明してます)
- 引数として渡すVector3.zの値でカメラと面の間の距離を指定できます
- 画面外判定で使えるのは2Dの場合のみだと思います。なので例も2D表示にしてます

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class ViewPortTo : MonoBehaviour { Camera targetCamera; // 映っているか判定するカメラへの参照 [SerializeField] Transform targetObj; // 映っているか判定する対象への参照。inspectorで指定する Vector3 lb; // 左下の座標 Vector3 rt; // 右上の座標 // Use this for initialization void Start () { targetCamera = GetComponent<Camera>(); lb = GetViewportPos(new Vector3(0, 0, 0)); // 左下 rt = GetViewportPos(new Vector3(1, 1, 0)); // 右上 } void Update() { if(CheckInScreen()) ShowText("画面内にいるよ"); else ShowText("画面外だよ"); } // カメラのビューポート座標をワールド座標に変換して返す Vector3 GetViewportPos(Vector3 targetPos) { return targetCamera.ViewportToWorldPoint(targetPos); } // 対象のオブジェクトの位置から画面内かどうか判定して返す bool CheckInScreen() { // テスト環境のカメラが変な方向を向いてたので少しおかしな判定文になっています // ここの条件式は適宜、修正してください if(targetObj.position.x < rt.x || lb.x < targetObj.position.x || targetObj.position.z < lb.z || rt.z < targetObj.position.z) return false; return true; } // 以下はサンプルのUI表示用 [SerializeField] Text uiText; void ShowText(string message) { uiText.text = message; } } |
参考:ViewportToWorldPointが3Dだと使いにくい理由
前述した通り「カメラと水平な面の座標」しか取れないのが理由なのですが、文章だと分かりにくいので画像を貼っておきます。

画面外判定するのに知りたいのは「床面がどこまでカメラに映っているか」という情報なわけですが、ViewportToWorldPointを使用した場合に取得できるのは、上の画像で黄色い球が表示されているポイントになります。
ゲームのステージとは関係なく、「カメラから一定距離離れた地点でカメラに映る範囲」が取得されてしまうので、「カメラとステージが水平で、ステージが真っ平らな場合」でないと画面外判定に使うのは難しいです。
2Dの場合は「カメラとステージが水平で、ステージが真っ平らな場合」という条件に合っているので問題なく使えます。
- Camera.ViewportToWorldPointの参考リンク
- 公式スクリプトリファレンス(日本語版最新)
- 2Dと3Dの座標変換【Unity】 2015.06.29
Camera.WorldToViewportPointを使う方法
- ワールド空間の座標をカメラのビューポート空間の座標に変換してくれる
- Unityの座標系について、詳しくはこちらの記事(UnityのCameraが使う3つの座標系)を参照してください
- ターゲットとなるオブジェクトの位置をカメラのビューポート座標に変換し、x座標・y座標が(0,0)から(1,1)の間にあれば画面内にあると判定できる

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class WorldTo : MonoBehaviour { Camera targetCamera; // 映っているか判定するカメラへの参照 [SerializeField] Transform targetObj; // 映っているか判定する対象への参照。inspectorで指定する Rect rect = new Rect(0, 0, 1, 1); // 画面内か判定するためのRect void Start () { targetCamera = GetComponent<Camera>(); } void Update () { var viewportPos = targetCamera.WorldToViewportPoint(targetObj.position); if(rect.Contains(viewportPos)) ShowText("画面内にいるよ"); else { ShowText("画面外だよ"); } } // 以下はサンプルのUI表示用 [SerializeField] Text uiText; void ShowText(string message) { uiText.text = message; } } |
- Camera.WorldToViewportPointの参考リンク
- 公式スクリプトリファレンス(日本語版最新)
- Rect.Containsの公式スクリプトリファレンス(日本語版最新)
- Unity】画面外のターゲットを追跡するカーソル 2016.04.26
Colliderを使う方法
- 画面の周囲を判定用のColliderで囲んでおいて、OnTriggerEnterとかで判定する方法
- 画面全体を覆うColliderを作っておいて、OntriggerExitで判定する方法もあります(Unity公式チュートリアルの2Dシューティングはこの方法で弾を削除してます)
- 実装方法は関連リンクにUnity公式チュートリアルの2Dシューティングの該当箇所へのリンクを貼ってますので、そちらをご覧ください
- スマホアプリだと端末によって画面に映る範囲が変わってしまうので、他の方法の方が使い勝手がいいと思います
- Colliderの参考リンク
- 公式チュートリアル 2Dシューティング 当たり判定とアニメーションイベントとレイヤー