OnBecameVisible() triggers to early and OnBecameInvisible() to late

OnBecameVisible() triggers to early (before the gamobject is seen on the camera) and OnBecameInvisible() doesn’t triggers accurate, it doesn’t triggers even the gameobject is outside the camera view. It is seen on my video, the enemies are firing bullets/lasers before they are seen on the camera.

The enemy ships have a 2d sprite renderer, I’m using URP.

My code:

    void OnBecameVisible()
    {
        if (gameObject.transform.root.gameObject.CompareTag("Enemy"))
        {
            isInScreen = true;
            wasInScreen = true;
            allowShooting = true;
            allowActive = true;
            Debug.Log("OnBecameVisible " + this.gameObject.transform.root.gameObject.name);
        }
    }

    void OnBecameInvisible()
    {
        if (gameObject.transform.root.gameObject.CompareTag("PlayerShooting"))
        {
            this.gameObject.SetActive(false);
        }
        else if (gameObject.transform.root.gameObject.CompareTag("EnemyShooting"))
        {
            this.gameObject.SetActive(false);
        }
        else if (wasInScreen && gameObject.transform.root.gameObject.CompareTag("Enemy"))
        {
            isInScreen = false;
            allowShooting = false;
            allowActive = false;
            Debug.Log("Destroyed OnBecameInvisible " + this.gameObject.transform.root.gameObject.name);
            isDestroyed = true;
            Destroy(this.gameObject.transform.root.gameObject,0.1f);
        }
    }

Likely because those viz callbacks are based on AABB rects, eg, the Renderer.bounds and clipping that to the frustum.

Go look up what an AABB rect is, but it will almost always be much larger the involved object, unless it is truly an axis-aligned cube. :slight_smile:

2 Likes

As mentioned, it’ll be keyed on the AABB but also know that is this a core renderer feature and nothing specifically to do with SpriteRenderer or 2D at all.

You can tell this because the callback originates here: Unity - Scripting API: Renderer.OnBecameVisible()

1 Like

Ok, thanks for the info. Is there a easy way to fix it? I want that the enemy get only active when they are seen on the camera.

I could try to check the x-y-distance to the camera or something like position on the camera screen with WorldToScreenPoint but is there a more easier way?

I always do the work myself (usually world to screen, then screen check) since almost always it has to take into account the size of the enemy: big enemies are visible sooner than little ones, assuming a central position.

Sometimes you want special game-nice features like “let the enemy be fully onscreen for one second before they open fire,” which often feels better to the player, not so cheap and cheat-y.

2 Likes

There is a new problem, because of the directional light there is the shadow seen, even the gameobject self isn’t seen on the camera. Is there a easy way to solve this problem?

OnWillRenderObject seems to ignore the shadow and is called repeatedly while the object is on screen. So you could place the shooting code in that method instead of setting a flag.

1 Like

This “problem” is a little vague, what do you mean by this?

All I can do is point you to the docs:

“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.”

1 Like

Thank you, this is more accurate than OnBecameVisible().

I have tested OnBecameInvisible(), it triggers to late, even if the shadow isn’t seen this function will trigger much later. Can be a audiosource-range the reason for the late triggering?