Determining GameObjects in Focus

I’m trying to design a configurable script that produces a list of objects in the player’s focus, and optionally a specific object that is “most” in focus. This is being used for a FPS like game, but could be used for a number of other genres.

Things it should take into account are a configurable field of focus (might be smaller than the camera FOV), object distance from the camera, and distance from the camera’s center focus point.

Any suggestions on implementation?

Currently, I’ve written this code:

    public bool requireFieldOfView = true;
    public GameObject Camera;
    public float verticalFieldOfView = 15;
    public float horizontalFieldOfView = 30;


   public ArrayList findInteractiveObjects()
    {
        Vector3 distance, adj, vHyp, hHyp;
        float hAngle, vAngle;
        ArrayList interactiveObjects = new ArrayList();

        // Determine Camera View
        Transform FOV;
        if (Camera != null)
            FOV = Camera.transform;
        else FOV = transform;

        // Find current colliders
        Collider[] proximityObjects = Physics.OverlapSphere(FOV.position, maximumInteractiveDistance, interactiveLayers.value);
        foreach (Collider col in proximityObjects)
        {
            distance = col.transform.position - FOV.position;
            adj = Vector3.Dot(distance, FOV.forward) * FOV.forward;

            vHyp = distance - (Vector3.Dot(distance, FOV.right) * FOV.right);
            vAngle = Mathf.Rad2Deg * Mathf.Acos(adj.magnitude / vHyp.magnitude);

            hHyp = distance - (Vector3.Dot(distance, FOV.up) * FOV.up); ;
            hAngle = Mathf.Rad2Deg * Mathf.Acos(adj.magnitude / hHyp.magnitude); ;

            //Ensure they are in the Field of View
            if ((hAngle <= horizontalFieldOfView  vAngle <= verticalFieldOfView) || !requireFieldOfView)
            {
                Interactive interactiveObj = col.gameObject.GetComponent<Interactive>();
                if (interactiveObj != null)
                    interactiveObjects.Add(interactiveObj);
            }
        }

        return interactiveObjects;
    }

Some of the problems I’m having are:

  1. This code requires that the object’s local origin to be within the field of view, not necessarily any part of the object
  2. Objects behind the player but within that field of view are also detected
  3. Doesn’t yet determine which object is “most in focus”. Determining this will probably have be some trade off between distance from the camera and distance from the center of the Field of View
  4. it would be nice to have some sort of gizmo to graphically alter the FOV, or at least a display mode to show which area of the camera will detect objects

I’m wondering if it might be easier to just create a 4 sided frustrum mesh and check for collisions. Maybe use scaling to configure the desired parameters? Would that method be considerably more expensive, computationally?

Any approaches that I haven’t thought of?

Thanks!

1 Like

I will just throw an idea here:
I think you can set up a custom camera (with custom FOV and far/near clipping planes) that doesn’t render anything but still culls the scene. You can check which objects are within the camera’s frustum/frustrum using functions like OnBecameVisible, OnBecameInvisible isVisible, OnWillRenderObject and keep a list of all objects that are visible and sort them out somehow to determine which one is ‘most’ in focus. This might not be at all cheap though if you have a lot of items that can be in focus…

The problem with using another camera is that Renderer.isVisible is true if any camera sees the object. So if the object is on the screen, but out of my focus area, it still says its visible.

The above code works for objects who’s center point within the view, and for now, I was able to eliminate objects behind the player using the isVisible property (though that might not work if I add a camera based minimap).

To detect objects that are in view, but their center point is not, I can use use pyramidal mesh w/ kinematic rigidbody, set as a trigger, to do collision detection. Feels like a bad idea, but it works. However, that doesn’t cover objects completely within the mesh and not touching the any edges.

So, a combination of the two should detect all the objects of interest. The issue becomes keeping track of them all. The first method is done based on periodic updates. The second method is based on message handling.

So, if we are maintaining a list containing objects of interest, we could add to the list when they cross the mesh or if they are found to be within the mesh. And remove them when they exit the mesh (checking if they are contained inside will add them back in, if they exit the trigger into the mesh). We can maintain the list, but its only guaranteed to be correct after the update function runs.

I guess technically it wont catch objects whose collider is completely within the view mesh, but whose origin is outside the mesh… but I can’t think of any use cases where that is a desired art asset design.