I'm making a game and I need to find the 2 GameObject
s 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 GameObject
s 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 GameObject
s, 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