一:使用Unity自带的回调函数
当物体身上的Render组件是否被渲染时调用OnBecameVisible和OnBecameInVisible,类似于OnTriggerEnter和OnTriggerExit
- 物体自身上必须有相关Render组件
- 它所谓的渲染与不渲染包括Game视图和Scene视图两个,在电脑上测试会出现Game视图中已经不显示物体但是Scene视图中物体还被显示出来的问题,移动端不用考虑这个问题
- 下面的脚本挂载到被检测的物体身上
using UnityEngine;
public class Test : MonoBehaviour
{
private bool isInView; //是否在视线范围内
private void Update()
{
if (isInView)
{
print("yes");
}
else
{
print("no");
}
}
private void OnBecameVisible()
{
isInView = true;
}
private void OnBecameInvisible()
{
isInView = false;
}
}
但是测试后发现,这种自带的API有两种问题(还有一个叫OnWillRenderObject的API与这种方法类似)
——游戏运行一开始会执行no两次,这是因为物体被渲染出来可能需要几帧的时间,而在这个几帧的间隔里就会执OnBecameInvisible方法,解决方法是可以在游戏运行后1秒再去判定Update中的isInView,不过这样显得很奇怪
——所有的相机只要有一个渲染了此物体都会执行OnBecameVisible,无法指定相机
推荐以下的方法
二:世界坐标转视口坐标
using UnityEngine;
public class Test : MonoBehaviour
{
private void Update()
{
if (IsInView(transform.position))
{
print("yes");
}
else
{
print("no");
}
}
/// <summary>
/// 是否在指定相机的视野范围内
/// </summary>
/// <param name="worldPos">物体世界坐标</param>
/// <returns>是否在相机视野范围内</returns>
public bool IsInView(Vector3 worldPos)
{
Transform camTransform = Camera.main.transform;
Vector2 viewPos = Camera.main.WorldToViewportPoint(worldPos);
//判断物体是否在相机前面
Vector3 dir = (worldPos - camTransform.position).normalized;
float dot = Vector3.Dot(camTransform.forward, dir);
if (dot > 0 && viewPos.x >= 0 && viewPos.x <= 1 && viewPos.y >= 0 && viewPos.y <= 1)
{
return true;
}
else
{
return false;
}
}
}