What is the best way to return all objects within a 3D area?

Viewed 52

I'm currently working on an FPS and trying to create a "Gamemode" controller that gives the team with the most team members within a defined 3d area a point for their team. My first attempt looks something like:

public class TDM : Gamemode
{
    private Collider pointArea;
    private Dictionary<Player, int> teamA; 
    private Dictionary<Player, int> teamB;

    private int methodNumberOfPlayersOnPoint(List<Player> players)
    {
        int count = 0;
        
        foreach (Player player in players)
        {
            if (pointArea.bounds.Contains(player.transform.position))
            {
                count++;
            }
        }

        return count;
    }
}

is there a better way to do this?

3 Answers

You can call OverlapSphere which gets all the elements within a sphere. Or a box using OverlapBox or capsule using OverlapCapsule.

void GettAll(Vector3 center, float radius)
{
     Collider[] objectsHit = Physics.OverlapSphere(center, radius);
     foreach (var obj in objectsHit )
     {
         // do something for each object
     }
}

I don't have much experience in Unity yet so I'll let someone else provide code, but I believe what you're looking for are Trigger Colliders. You basically want to let the engine itself do the heavy lifting for checking for intersection of your player with the zone.

Then in the method that gets called when the player enters the area you can add them to the count that is being used by your scorekeeping component for calculating point accrual. When the player leaves you remove them from the count.

If the position alone (ignoring any shape of objects) is enough precise then this is probably already a good approach.

You could/should cache the pointArea.bounds though before the loop so it is fetched only once.

And you could use Linq to simplify the code like

private int methodNumberOfPlayersOnPoint(List<Player> players)
{
    var bounds = pointArea.bounds;
    return players.Count(player => bounds.Contains(player.transform.position));
}

Note though that the Bounds in Unity are always world axis aligned! That means if your area is rotated in any way the bounds are not accurate.

In such case you might wan to rather use a PhysicsOverlapBox which allows you to check a specific rotation as well. This requires all your objects having colliders of course

private static int methodNumberOfPlayersOnPoint(BoxCollider area)
{
    return Physics.OverlapBox(area.transform.TransformPoint(area.center), area.size / 2f, area.transform.rotation).Length;
}

Note for now this returns ALL objects within this area. you might want to filter for a certain LayerMask and include a filter for the Team (e.g. tag or some ID stored in Player)

Related