Pierce Thompson
Pierce Thompson

Reputation: 21

Check if a gameobject is visble on the current camera

I've been searching stack overflow and the internet for a bit trying to search for a solution to this issue, everytime i try to use OnBecameVisible() or OnBecameInvisible() or TestPlanesAABB to check if the object is not visible through the wall, the camera can still see the object through a solid wall.

Video of the problem here: https://youtu.be/3HiEugm6On8

As you can see, if i'm looking at him he stops moving, if i turn around and he "unloads" or "becomes invisible" he moves closer, but if i go around a corner still looking in his direction he stops moving as if i can see him and there is no wall there, this is what i'm looking to solve

it's an enemy that roams around, and i want him to only move if i cannot see him, which i thought would be fairly simple, but alas it seems not to be as simple as i thought

my current code is basic:

public bool IsSeen = false;

    public void OnBecameVisible()
    {
        Debug.Log("I can now see you");
        IsSeen = true;
    }

    public void OnBecameInvisible()
    {
        Debug.Log("I can't see you");
        IsSeen = false;
    }

this is attatched to the object that i wish to detect / not detect through walls, which i do believe checks if the object is viewable by the camera that i choose.

does anyone have any ideas as to how i can fix / achieve this?

Upvotes: 2

Views: 4278

Answers (1)

derHugo
derHugo

Reputation: 90852

As already mentioned the general "problem" with Renderer.OnBecameVisible is

Note that object is considered visible when it needs to be rendered in the Scene. It might not be actually visible by any camera, but still need to be rendered for shadows for example. Also, when running in the editor, the Scene view cameras will also cause this function to be called.

So its not really usable for you.

is there a simple way to raycast the whole screen?

Unfortunately not really :/

You can a bit avoid this using GeometryUtility.CalculateFrustumPlanes in order to get the four planes of the camera frustrum. Then you can check whether the object is actually inside the frustrum using GeometryUtility.TestPlanesAABB

var cam = Camera.main;
var planes = GeometryUtility.CalculateFrustumPlanes(cam);
var objCollider =  GetComponent<Collider>();

if (GeometryUtility.TestPlanesAABB(planes, objCollider.bounds))
{
    Debug.Log("I am inside the camera frustrum!");
}
else
{
    Debug.Log("I am out of sight...");
}

However, this still does not cover any other object being in front of the target object and therefore actually covering it.

You would need to define exactly what visible means (e.g. any portion of the mesh? Is the center of the object enough to test? etc).

For e.g. testing only the center of the object you could use a Physics.Linecast like e.g.

if (GeometryUtility.TestPlanesAABB(planes, objCollider.bounds))
{
    Debug.Log("I am inside the camera frustrum!");

    if(Physics.LineCast(cam.transform.position, objCollider.GetComponentInChildren<Renderer>().bounds.center, out var hit)
    {
        if(hit.gameObject != objCollider.gameObject)
        {
            Debug.Log("..but something else is in the way..");
        }
        else
        {
            Debug.Log("Now you see me, muhaha!");
        }
    }
}

If you want it to be more precise and track of any part of the mesh is visible then it actually gets tricky. You would need to raycast multiple key points of the bounding box (e.g. each corner, centers of the edges etc) depending on your needs.

Upvotes: 2

Related Questions