C# Unity Поиск объекта в списке с помощью Linq
Вот у меня есть список объектов моего класса. Мне нужно найти один объект из списка, который будет удовлетворять условию. Список это коллайдеры, нужно найти самый ближайший коллайдер к другому объекту на сцене. Это конечно можно написать через цикл, а через Linq это скорее всего будет работать медленнее, но мне интересно. Я почитал что там есть Where, Select, Find. Дошел до такой вещи.
Collider[] colliders = Physics.OverlapSphere(itemHodler.position, sphereRadius, mask);
Collider collider = colliders.ToList().Select(x => Vector3.Distance(itemHodler.position, x.transform.position)).Min();
Но это мне вернет значение типа float самую короткую дистанцию, а мне нужен сам collider. Получается надо как-то скомбинировать эти Where, Select, Find. Но я не знаю как.
Ответы (2 шт):
Если этот код будет выполняться часто, например в Update(), то не соверую юзать Linq, потому что он может оказаться медленным и просаживать производительность игры. Если не часто будет выполняться, то ок.
Что касается запроса из кода, то .ToList() можно и нужно выкинуть. Linq для массивов работает точно так же как и для списков.
Проблему решает простая сортировка.
Collider collider = colliders
.OrderBy(x => Vector3.Distance(itemHodler.position, x.transform.position))
.First();
Вот этот код будет быстрее, вынесите в отдельный метод, если страшно в основном коде кучу циклов держать
private Collider FindClosestCollider(Colliders[] colliders, Vector3 position)
{
Collider result = colliders[0];
float min = Vector3.Distance(position, result.transform.position);
for (int i = 1; i < colliders.Length; i++)
{
float distance = Vector3.Distance(position, colliders[i].transform.position);
if (distance < min)
{
min = distance;
result = colliders[i];
}
}
return result;
}
Самый быстрый вариант поиска ближайшего:
public static class TransformExtension
{
public static Transform FindClosest (this Transform target, Transform[] objects)
{
Vector3 targetPosition = target.position;
int closeIndex = 0;
float minSqrtRange = (objects[0].position-targetPosition).sqrMagnitude;
for (int i = 1; i < objects.Length; i++)
{
float sqrtRange = (objects[i].position-targetPosition).sqrMagnitude;
if (sqrtRange < minSqrtRange)
{
minSqrtRange = sqrtRange;
closeIndex = i;
}
}
return objects[closeIndex];
}
}
Используя sqrMagnitude не придется совершать тяжеловесные операции по вычислению квадратных кореней, как в Vector.Distance. То есть значения расстояния недовычесленны, но что бы их сравнивать это и не нужно.