Unity-How do I check if an object is seen by the Main Camera
Solution 1
You can use the Camera.WorldToViewport function, using your Main Camera to call it and giving in parameter the position of the object your are checking. If the x and y coordinates values of the result vector are between 0 and 1 and the z value is superior to 0, it means the center of your object is seen by your camera.
UnityEngine.Camera cam;
bool isMoving;
void Start()
{
cam = UnityEngine.Camera.main;
isMoving = true;
}
void Update()
{
Vector3 viewPos = cam.WorldToViewportPoint(checkedObject.position);
if (viewPos.x >= 0 && viewPos.x <= 1 && viewPos.y >= 0 && viewPos.y <= 1 && viewPos.z > 0)
{
// Your object is in the range of the camera, you can apply your behaviour
isMoving = false;
}
else
isMoving = true;
if(isMoving)
Move();
}
Solution 2
Methods suggested does not work in your case because you've made a class called Camera
which has exactly the same name as Unity
's Camera
component.
I would suggest you use the approach from the community wiki. It's simple enough to understand and lets you check visibility for each camera.
You can create an extension class for the Camera
component :
public static class CameraEx
{
public static bool IsObjectVisible(this UnityEngine.Camera @this, Renderer renderer)
{
return GeometryUtility.TestPlanesAABB(GeometryUtility.CalculateFrustumPlanes(@this), renderer.bounds);
}
}
The you can call that from each object separately like such :
// UnityEngine.Camera cam1;
// UnityEngine.Camera cam2;
void Update()
{
bool isVisibleForCamera1 = cam1.IsObjectVisible(GetComponent<MeshRenderer>());
bool isVisibleForCamera2 = cam2.IsObjectVisible(GetCoponent<SpriteRenderer>());
}
Here you can find whole documentation about GeometryUtility
Related videos on Youtube
user8048595
Updated on September 23, 2020Comments
-
user8048595 over 3 years
I'm doing this thing where an object is moving on a plane, and the camera is in the center of it. I got it so the camera rotates along with the mouse, and when the main camera sees the game object, it stops. So I was using the onbecamevisible() and onbecameinvisible() functions, but that applies to any camera, including the scene view. How do I make it so the objects stops when seen only by the main game camera?
using System.Collections; using System.Collections.Generic; using UnityEngine; public class cubeMove : MonoBehaviour,moveObject { Camera cam; public Transform checkedObject; void Start() { cam = GetComponent<Camera>(); } void Update() { Vector3 viewPos = cam.WorldToViewportPoint(checkedObject.position); if () move(); } public void move() { transform.Rotate(new Vector3(15, 30, 45) * Time.deltaTime); }
}
Here's my Camera Script
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Camera : MonoBehaviour { public enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 } public RotationAxes axes = RotationAxes.MouseXAndY; public float sensitivityX = 15F; public float sensitivityY = 15F; public float minimumX = -360F; public float maximumX = 360F; public float minimumY = -60F; public float maximumY = 60F; float rotationX = 0F; float rotationY = 0F; private List<float> rotArrayX = new List<float>(); float rotAverageX = 0F; private List<float> rotArrayY = new List<float>(); float rotAverageY = 0F; public float frameCounter = 20; Quaternion originalRotation; void Update() { if (axes == RotationAxes.MouseXAndY) { rotAverageY = 0f; rotAverageX = 0f; rotationY += Input.GetAxis("Mouse Y") * sensitivityY; rotationX += Input.GetAxis("Mouse X") * sensitivityX; rotArrayY.Add(rotationY); rotArrayX.Add(rotationX); if (rotArrayY.Count >= frameCounter) { rotArrayY.RemoveAt(0); } if (rotArrayX.Count >= frameCounter) { rotArrayX.RemoveAt(0); } for (int j = 0; j < rotArrayY.Count; j++) { rotAverageY += rotArrayY[j]; } for (int i = 0; i < rotArrayX.Count; i++) { rotAverageX += rotArrayX[i]; } rotAverageY /= rotArrayY.Count; rotAverageX /= rotArrayX.Count; rotAverageY = ClampAngle(rotAverageY, minimumY, maximumY); rotAverageX = ClampAngle(rotAverageX, minimumX, maximumX); Quaternion yQuaternion = Quaternion.AngleAxis(rotAverageY, Vector3.left); Quaternion xQuaternion = Quaternion.AngleAxis(rotAverageX, Vector3.up); transform.localRotation = originalRotation * xQuaternion * yQuaternion; } else if (axes == RotationAxes.MouseX) { rotAverageX = 0f; rotationX += Input.GetAxis("Mouse X") * sensitivityX; rotArrayX.Add(rotationX); if (rotArrayX.Count >= frameCounter) { rotArrayX.RemoveAt(0); } for (int i = 0; i < rotArrayX.Count; i++) { rotAverageX += rotArrayX[i]; } rotAverageX /= rotArrayX.Count; rotAverageX = ClampAngle(rotAverageX, minimumX, maximumX); Quaternion xQuaternion = Quaternion.AngleAxis(rotAverageX, Vector3.up); transform.localRotation = originalRotation * xQuaternion; } else { rotAverageY = 0f; rotationY += Input.GetAxis("Mouse Y") * sensitivityY; rotArrayY.Add(rotationY); if (rotArrayY.Count >= frameCounter) { rotArrayY.RemoveAt(0); } for (int j = 0; j < rotArrayY.Count; j++) { rotAverageY += rotArrayY[j]; } rotAverageY /= rotArrayY.Count; rotAverageY = ClampAngle(rotAverageY, minimumY, maximumY); Quaternion yQuaternion = Quaternion.AngleAxis(rotAverageY, Vector3.left); transform.localRotation = originalRotation * yQuaternion; } } void Start() { Rigidbody rb = GetComponent<Rigidbody>(); if (rb) rb.freezeRotation = true; originalRotation = transform.localRotation; } public static float ClampAngle(float angle, float min, float max) { angle = angle % 360; if ((angle >= -360F) && (angle <= 360F)) { if (angle < -360F) { angle += 360F; } if (angle > 360F) { angle -= 360F; } } return Mathf.Clamp(angle, min, max); }
}
SphereMove
public class sphereMove : MonoBehaviour,moveObject { public float delta = 1.5f; public float speed = 2.0f; private Vector3 startPos; public UnityEngine.Camera cam; public Transform checkedObject; bool isMoving; void Start() { isMoving = true; cam = Camera.main; } void Update() { Vector3 viewPos = cam.WorldToViewportPoint(checkedObject.position); if (viewPos.x > 0 && viewPos.x <= 1 && viewPos.y >= 0 && viewPos.y <= 1 && viewPos.z > 0) isMoving = false; else isMoving = true; if (isMoving) move(); } public void move() { Vector3 v = startPos; v.x += delta * Mathf.Sin(Time.time * speed); transform.position = v; }
}
-
Izukani over 6 yearsAre you considering your object is "seen" by the camera when in it is in the range of the camera or it is actually seen on the screen (that means not hidden by any other item)?
-
user8048595 over 6 yearsfor now, just whatever's on screen
-
mrogal.ski over 6 yearsYou want to do something when an object is in the view of a camera, but which camera? The one you've made a class or the built-in Unity's Camera component?
-
user8048595 over 6 yearsthe main camera in hierarchy I made a script for
-
Izukani over 6 years@user8048595 As said, change
Camera cam
intoUnityEngine.Camera cam
to make it works. Or change the name of your Camera script. Because right now your script is trying to findWorldToViewportPoint
into your script, and not Unity's one.
-
-
Programmer over 6 yearsBut another object can block the vie with this and the camera will still "see" it
-
user8048595 over 6 yearsThere's an error saying Camera does not contain a definition for WorldToViewportPoint.
-
Izukani over 6 years@user8048595 Do you have a script called Camera? If that's the case, your program will use that script instead of the UnityEngine.Camera one, and thus you won't be able to find the function. If that's the case, just declare Camera as UnityEngine.Camera.
-
Programmer over 6 yearsRemove ",moveObject" that is placed after
MonoBehaviour
I can't even tell why you have that there. Also, do not name your scriptCamera
. Rename it both in the Editor and in Visual Studio. -
user8048595 over 6 yearsI was practicing strategy pattern. It's not important right now
-
user8048595 over 6 yearsI added the script, it says I need to add a camera component. Do I have to add a new camera for every moving object I want to stop?
-
Izukani over 6 years@user8048595 Just set your
cam
variable toCamera.main
in your Start function. You don't need to add a new camera nor anything, it will work with your main one as you asked. -
user8048595 over 6 yearsOk. I think I got something. It moves when the camera sees it. I want to stop it when seen. Do I set move to Start() and then stop move in the if statement? How would I do that?
-
Izukani over 6 years@user8048595 Add a boolean to your class, something like
isMoving
, and set it to true in your Start function. Then in your Update, check for the condition I did put in my answer, and setisMoving
to false if that's the case. Then just put an if condition testing ifisMoving
is true, and if that's the case, call yourMove
function. -
user8048595 over 6 yearsif(ismoving==true) move(). Do I set that in update() at the end? Because it only works once.
-
Izukani over 6 years@user8048595 I edited my answer to show you the idea.
-
user8048595 over 6 yearsSorry One more thing. I'm applying the same rules to a sphere thats move left to right, but I play and look away, the sphere ends up in the center of the plane where the camera is and starts moving when it should be against the wall moving. I think the code I applied to the cube has to do with that.
-
Hellium over 6 yearsAnother big problem with the
WorldToViewportPoint
is that the object may be partially visible by your camera andWorldToViewportPoint
will indicate the object is outside the viewport.... -
Izukani over 6 years@Hellium That's why I precised this solution would only work if the center of the object is seen by the camera.