Finding closest object to player

I’m working on a cover system for a stealth game. In general it’s working fairly well except for one thing. When the player goes into cover, I have a script (below) that finds the closest cover object (walls, pedestals, etc.). The problem is, with long walls, the transform of the cover object is sometimes farther away than other cover pieces so the detection determines that other cover pieces are closer which is, less than helpful. If anyone has any thoughts on how to efficiently find the closest object based on it’s colliders or vertices or bounds or something else I’m not thinking of, that’d be awesome. Thanks!

void FindNear()
    {
        GameObject[] gos = GameObject.FindGameObjectsWithTag("Cover");
        Vector3 position = transform.position;
        float coverDist = coverDistance;
        foreach (GameObject go in gos)
        {
            Vector3 diff = go.transform.position /*Find Closest point on go*/ - position;
            float curDistance = diff.sqrMagnitude;
            if (curDistance < coverDist)
            {
                closest = go;
                coverDist = curDistance;
            }
        }
    }

Instead of using the models use some cover points. For instance, instead of defining your wall as a cover, place two empty objects at both ends of the wall.

Simply use OverlapSphere to obtain all the colliders (use layermask!) and iterate the list for the closest one based on other.transform.position - transform.position

Edit: @fafase’s suggestion of ClosetPointToBounds makes this solution work well.

Here’s a test class I threw together. Player and Terrain (8/9) are exluded from the OverlapSphere. Press G to fire off the test. It should spit out the closest object at the end.

There is a fair amount of calculation which increases as more objects are involved, rather dramatically. To improve on calculation it doesnt compare all objects together but just against the current closest object. To make this work I specified a Max Search Distance which acts as the threshold for the ‘lastLength’ variable to work against. You can also put this into OverlapSphere’s radius to limit the objects which are entered in to the array, but I haven’t bothered. Its pretty crude but it seems to work.

using UnityEngine;
using System.Collections;

//OverlapSphere Test
public class OSTest : MonoBehaviour {

	float maxSearchDistance = 50.0f;
	Collider thisOne;
	LayerMask lm = ~(1<<8 | 1<<9); //Player and Terrain

	void Update ()
	{
		Collider[] myColArray;
		float lastLength = maxSearchDistance;
		bool flagSet = false;
		
		if(Input.GetKeyDown(KeyCode.G))
		{
			myColArray = Physics.OverlapSphere(transform.position, 50.0f, lm);
			for(int i = 0; i < myColArray.Length; i++)
			{
				Vector3 temp = myColArray*.ClosestPointOnBounds(transform.position);*
  •  		Vector3 tempLength = temp - transform.position;*
    
  •  		float sqrLength = tempLength.sqrMagnitude;*
    

_ if(sqrLength < lastLength*lastLength)_

  •  		{*
    

_ thisOne = myColArray*;_
_
lastLength = tempLength.magnitude;_
_
flagSet = true;_
_
}_
_
}_
_
if(flagSet)_
_
{_
_
Debug.Log(thisOne.gameObject.name);_
_
}_
_
}_
_
}_
_
}_
The Enlongated cube is successfully detected despite the origin being clearly much further away than the other objects.
_
[57354-example.jpg*|57354]*
If this solution works for you make sure to give fafase a +1 on his ClosestBounds comment below this answer.
_*

Hi there

There are varying approaches to this that balance accuracy vs cost.

First up, using OverlapSphere is a good optimisation - it helps find all the objects you actually care about, rather than having to check the entire world. For example, if the player can only ‘find cover’ in objects < 1m away, you want to start with a list of objects within 1m!

Once you have them, the ‘hard core’ approach would be to check every vertex of the mesh, but that’s a little crazy. My gut instinct would be:

  • iterate over all game objects with mesh renderers you find
  • transform your test point (i.e. the player transform) into local space of the renderer
  • clamp it into within the bounds of the mesh
  • transform it back into world space

This little trick will give you the closest bound between your object and the local bounding box of the mesh. Assuming your objects are fairly boxy, testing the distance between the player and this point will be a good approximation.

Note that if you’re happy to drive off colliders instead of the meshes themselves, you could use Collider.ClosestPointOnBounds instead of my clamping process. Either way, the idea is to find the closest point on the target object to the player, then test the distance to it.

Also, use the debug renderer to draw the closest points you find, just in case its not working and you can’t understand why!