Unity-How do I check if an object is seen by the Main Camera

20,702

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

Share:
20,702

Related videos on Youtube

user8048595
Author by

user8048595

Updated on September 23, 2020

Comments

  • user8048595
    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
      Izukani over 6 years
      Are 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
      user8048595 over 6 years
      for now, just whatever's on screen
    • mrogal.ski
      mrogal.ski over 6 years
      You 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
      user8048595 over 6 years
      the main camera in hierarchy I made a script for
    • Izukani
      Izukani over 6 years
      @user8048595 As said, change Camera cam into UnityEngine.Camera cam to make it works. Or change the name of your Camera script. Because right now your script is trying to find WorldToViewportPoint into your script, and not Unity's one.
  • Programmer
    Programmer over 6 years
    But another object can block the vie with this and the camera will still "see" it
  • user8048595
    user8048595 over 6 years
    There's an error saying Camera does not contain a definition for WorldToViewportPoint.
  • Izukani
    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
    Programmer over 6 years
    Remove ",moveObject" that is placed after MonoBehaviour I can't even tell why you have that there. Also, do not name your script Camera. Rename it both in the Editor and in Visual Studio.
  • user8048595
    user8048595 over 6 years
    I was practicing strategy pattern. It's not important right now
  • user8048595
    user8048595 over 6 years
    I 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
    Izukani over 6 years
    @user8048595 Just set your cam variable to Camera.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
    user8048595 over 6 years
    Ok. 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
    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 set isMoving to false if that's the case. Then just put an if condition testing if isMoving is true, and if that's the case, call your Move function.
  • user8048595
    user8048595 over 6 years
    if(ismoving==true) move(). Do I set that in update() at the end? Because it only works once.
  • Izukani
    Izukani over 6 years
    @user8048595 I edited my answer to show you the idea.
  • user8048595
    user8048595 over 6 years
    Sorry 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
    Hellium over 6 years
    Another big problem with the WorldToViewportPoint is that the object may be partially visible by your camera and WorldToViewportPoint will indicate the object is outside the viewport....
  • Izukani
    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.