Home > Mobile >  How to find the closest GameObject in an array with another GameObject also in that array?
How to find the closest GameObject in an array with another GameObject also in that array?

Time:11-17

I'm making a game and I need to find the 2 GameObjects from a list closest to another GameObject which is also in the same list but I don't want it to return more than 2 GameObjects or return the GameObject that I'm checking against.

Here's what I want to input:

GameObject[] objects, GameObject currentObject

And I want it to output:

GameObject[] closestObjects, GameObject currentObject

I tried:

GameObject [ ] GetClosestPaths ( GameObject [ ] paths, GameObject pathToTest )
{
    GameObject[] bestTargets = new GameObject[2];
    float closestDistanceSqr = Mathf.Infinity;
    Vector3 currentPosition = pathToTest.transform.position;
    Transform[] pathTransforms = new Transform[paths.Length];

    for ( int i = 0; i < paths.Length; i   )
    {
        pathTransforms [ i ] = paths [ i ].transform;
    }

    for ( int i = 0; i < pathTransforms.Length; i   )
    {
        if ( pathTransforms [ i ].position != currentPosition && paths [ i ] != pathToTest )
        {
            Transform potentialTarget = pathTransforms[i];
            Vector3 directionToTarget = potentialTarget.position - currentPosition;
            float dSqrToTarget = directionToTarget.sqrMagnitude;
            if ( dSqrToTarget < closestDistanceSqr )
            {
                if ( bestTargets [ 0 ] == null )
                {
                    bestTargets [ 0 ] = paths [ i ];
                }
                closestDistanceSqr = dSqrToTarget;
                if ( paths [ i ].transform.position != bestTargets [ 0 ].transform.position )
                {
                    bestTargets [ 0 ] = paths [ i ];
                }
                else
                {
                    bestTargets [ 1 ] = paths [ i ];
                }
            }
        }
    }

    return bestTargets;
}

paths being the GameObjects, pathToTest being currentObject and bestTargets being closestObjects.

I got this on Stackoverflow and this did not work at all. I'm hoping someone can help otherwise this goes to the infinite pile of unfinished projects.

CodePudding user response:

A simple check for two objects, in an array, excluding the test object, could start with an empty array, and then fill it, and shuffle the values (for ordering) as each new object in the array is tested. The resulting code could look something like this:

public static GameObject[] GetClosestPaths ( GameObject [ ] paths, GameObject pathToTest )
{
    var results = new GameObject[2];
    var resultsDistance = new float[] { float.MaxValue, float.MaxValue };
        
    for ( int i = 0; i < paths.Length; i   )
    {
        // Unity has its mechanism to determine if two objects are equal.
        if ( paths [ i ] == pathToTest ) continue;

        // Grab the sqrDistance between the test object and the current array object.
        var d = (paths[i].transform.position - pathToTest.transform.position).sqrMagnitude;
        if ( d < resultsDistance [ 0 ] )
        {
            // Shuffle down the result if needed.
            if ( results [ 0 ] != null )
            {
                results [ 1 ] = results [ 0 ];
                resultsDistance [ 1 ] = resultsDistance [ 0 ];
            }
            results [ 0 ] = paths [ i ];
            resultsDistance [ 0 ] = d;
        }
        else if ( d < resultsDistance [ 1 ] )
        {
            results [ 1 ] = paths [ i ];
            resultsDistance [ 1 ] = d;
        }
    }

    // You can remove this debug statement once you're happy with the results.
    Debug.Log ( $"{results[0].name} - {resultsDistance[0]}\n"  
        $"{results [ 1 ].name} - {resultsDistance [ 1 ]}" );
    return results;
}

CodePudding user response:

If I understand correctly you have as input the

  • GameObject pathToTest
  • GameObject[] paths

And what you want is

  • From paths return those 2 items that
  • are not pathToTest
  • are the two closest objects to pathToTest

So using Linq you could simply use

using System;
using System.Linq;
using UnityEngine;

public static class GameObjectExtensions
{
    public static GameObject[] GetClosestPaths (this GameObject pathToTest, GameObject[] paths, int maxAmount = 2)
    {
        // just cache the position once
        var positionToTest = pathToTest.transform.position;
        
        // Go through the paths
        return paths
            // Skip the "pathToTest"
            .Where(p => p != pathToTest)
            // Order them by distance to "pathToTest"
            .OrderBy(p => (p.transform.position - positionToTest).sqrMagnitude)
            // Take only up to 2 items
            .Take(maxAmount)
            // finally store the results in an array
            .ToArray();
    }

    public static GameObject[] GetClosestPaths (this GameObject[] paths, GameObject pathToTest, int maxAmount = 2)
    {
        return pathToTest.GetClosestPaths(paths, maxAmount);
    }
}

This will return a GameObject[] with up to 2 (by default) items (if there are enough items) that are the two closest objects to pathToTest without being pathToTest itself.

As it is an extension method now you can simply call it like

var closestTwoPaths = yourTargetObject.GetClosestPaths(paths);
// or also
var closestFourPaths = paths.GetClosestPaths(yourTargetObject, 4);

Since it can still return less then the given amount you should still always check the length of the returned array

See

  • Related