Как найти ближайший объект юнити

Я сделал NavMeshAgent в Unity. И через for задал ему несколько целей. Уничтожает один объект, затем другой. Но, как я понял, он определяет не ближний объект, а по возрастанию (по индексу массива).

Как сделать так, чтобы персонаж определял ближний объект, затем уничтожил его, а потом переходил к другому ближнему?

Один человек посоветовал мне через magnitude, но работает некорректно.

Вот мой код:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Unit : MonoBehaviour {

private NavMeshAgent agent;
private GameObject[] target;
private Animator anim;
private float distance;

public Transform start;

void Start () {
    agent = GetComponent<NavMeshAgent> ();
    anim = GetComponent<Animator> ();
}

public void Attack(int index){
    switch (index) {
    case 0:
        {
            for (int i = 0; i < target.Length; i++) {
                distance = Vector3.Distance (transform.position, agent.steeringTarget);
                Vector3 move = target [i].transform.position;
                transform.LookAt (new Vector3 (target [i].transform.position.x, 0, target [i].transform.position.z));
                agent.SetDestination (move);
                agent.updateRotation = true;
            }
        }
        break;
    case 1:
        {
            anim.Play ("Idle Firing");
            RaycastHit hit;
            if (Physics.Raycast (start.position, start.forward, out hit, 100.0f)) {
                Debug.Log (hit.point);
                if (hit.collider.tag == "Target") {
                    hit.collider.gameObject.GetComponent<Building> ().health -= 1;
                    if (hit.collider.gameObject.GetComponent<Building> ().health <= 0) {
                        Destroy (hit.collider.gameObject);
                        anim.Play ("Idle");
                    }
                }
            } else {
                Debug.LogError ("Error");
            }
        }
        break;
    case 3:
        {
            anim.Play ("Idle");
        }
        break;
    }
}

public void Navigate(){
    for (int i = 0; i < target.Length; i++) {
        distance = Vector3.Distance (transform.position, agent.steeringTarget);
        if (distance > agent.stoppingDistance) {
            anim.Play ("Run_Forwards");
        }
    }
}

void Shoot(){
    Attack (0);
    if (distance > agent.stoppingDistance) {
        Navigate ();
    } else if (distance <= agent.stoppingDistance) {
        Attack (1);
    } else {
        anim.Play ("Idle");
    }
}

void Update () {
    target = GameObject.FindGameObjectsWithTag ("Target");
    Shoot ();
}

}

Kromster's user avatar

Kromster

13.5k12 золотых знаков43 серебряных знака72 бронзовых знака

задан 31 июл 2017 в 15:36

General2001's user avatar

1

Здравствуйте!

Немного теории.

Допустим есть ваш персонаж и две цели куда идти. Первая цель target1 находится на расстоянии в 2 метра от персонажа, а вторая target2 в 5 метрах. Но есть одно условие, первая цель target1 стоит за длинной стеной, а вторая target2 в зоне прямой видимости от персонажа. Если применять ваши методы сравнения Vector3.Distance или Vector3.Magnitude, то будет выдаваться первая цель target1, но она за стеной и идти до нее 8 метров!

Мораль сей басни такова: вам нужно проверять не расстояние, а длину пути до объекта. Пример кода :

IEnumerator GetClosestTarget() {
    float tmpDist = float.MaxValue;
    GameObject currentTarget = null;
    for (int i = 0; i < targets.Length; i++) {
        if (agent.SetDestination(targets[i].transform.position)) {
            //ждем пока вычислится путь до цели
            while (agent.pathPending) {
                yield return null;
            }
            Debug.Log(agent.pathStatus.ToString());
            // проверяем, можно ли дойти до цели
            if (agent.pathStatus != NavMeshPathStatus.PathInvalid) {
                float pathDistance = 0;
                //вычисляем длину пути
                pathDistance += Vector3.Distance(transform.position, agent.path.corners[0]);
                for (int j = 1; j < agent.path.corners.Length; j++) {
                    pathDistance += Vector3.Distance(agent.path.corners[j - 1], agent.path.corners[j]);
                }

                if (tmpDist > pathDistance) { 
                    tmpDist = pathDistance;
                    currentTarget = targets[i];
                    agent.ResetPath();
                }
            } else {
                Debug.Log("невозможно дойти до "+ targets[i].name);
            }

        }

    }
    if (currentTarget != null) {
        agent.SetDestination(currentTarget.transform.position);
        //... дальше ваша логика движения к цели
    }
}

Вызов этого метода производится так: StartCoroutine(GetClosestTarget());

ответ дан 4 авг 2017 в 13:57

nipercop's user avatar

nipercopnipercop

5212 серебряных знака8 бронзовых знаков

Найти ближний объект можно так:

using System.Collections;
using UnityEngine;
using System.Collections.Generic;

public class Nearest : MonoBehaviour {

    GameObject [] enemy;
    GameObject closest;

    public string nearest;

    void Start() {
        enemy = GameObject.FindGameObjectsWithTag("Enemy");
    }

    GameObject FindClosestEnemy() {
        float distance = Mathf.Infinity;
        Vector3 position = transform.position;
        foreach (GameObject go in enemy) {
            Vector3 diff = go.transform.position - position;
            float curDistance = diff.sqrMagnitude;
            if(curDistance< distance) {
                closest = go;
                distance = curDistance;
            }
        }
        return closest;
    }

    void Update() {
        nearest =FindClosestEnemy().name;
    }
}

Тут:

GameObject [] enemy; – список объектов врагов, среди которых будем искать.

FindClosestEnemy() – ищет ближайший GameObject к объекту на который повешен скрипт.

Надо отметить врагов Tag ("Enemy"). Для формирования списка в котором будем искать ближайший объект, находящийся относительно нашего.

Не стал заморачиваться, чтоб переделывать ваш код. Так как вопрос старый. Решил поделиться кодом который у меня хорошо работает.

ответ дан 15 авг 2019 в 9:34

Ivan Triumphov's user avatar

Ivan TriumphovIvan Triumphov

1,1081 золотой знак14 серебряных знаков37 бронзовых знаков

Идеологически можно подойти к решению 2 способами:

  1. Один раз найти ближайшую цель, запомнить ее и двигаться в ее направлении до тех пор, пока она не будет уничтожена
  2. Каждый раз при вызове Update() пересчитывать расстояния и каждый раз искать ближайшую цель. Это может быть точнее, чем первый вариант, потому что будет учитывать скорость перемещения целей (если они движутся, и если они движутся с разной скоростью), однако требует пересчета при каждом вызове Update, в силу чего менее оптимально, чем первый вариант.

Однако, стоит заметить, что вам посоветовали верный способ поиска ближайшей цели (использование magnitude) Вы также можете попробовать Vector3.Distance, но судя по документации, этот метод работает через magnitude. Или можете попробовать Vector3.sqrMagnitude, он схож по идее работы с magnitude, но работает чуть быстрее, за счет того, что не выполняется операция вычисления квадратного корня, однако в силу этого дает менее точный результат. Вы можете почитать обсуждения на тему методов поиска ближайшего объекта здесь и здесь.

Вам может казаться результат вычисления ближайшей цели неверным, если работаете с вычислениями через Vector3, так как он работает с 3 измерениями (А скорее всего речь идет именно о трехмерной игре, так как вы используете навигацию Unity). Например, если взять игру с видом сверху, то учет разности высот при измерении расстояния может казаться визуально неверным, но точным математически. Таким образом вы можете попробовать считать расстояния с помощью тех же методов magnitude, sqrMagnitude или Distance, но используя Vector2. Тогда при вычислении расстояния учитываться будут только x и y координаты объектов. Такой подход может дать более “верное” решение задачи с точки зрения визуальной части.

ответ дан 31 июл 2017 в 19:29

vmchar's user avatar

vmcharvmchar

4,43117 серебряных знаков25 бронзовых знаков

Вот я написал такой код, но он во-первых ресурсозатратный, а во-вторых – находит расстояние только до ближайшего обьекта.
Добавил обьекты в массив:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rasstoyanie : MonoBehaviour
{
    GameObject[] enemy;
    GameObject player;
    public string nearest;

    private void Start()
    {
        enemy = GameObject.FindGameObjectsWithTag("Enemy");
    }
}

и вот в этой части надо найти вместо 1 обьекта, 3 ближайших:

GameObject Find()
    {
        float dist = Mathf.Infinity;
        Vector3 position = transform.position;
        foreach(GameObject go in enemy)
        {
            Vector3 diff = go.transform.position;
            float currdist = diff.sqrMagnitude;
            if(currdist < dist)
            {
                player = go;
                dist = currdist;
            }        
        }
        return player;  
    }

можете поправить и подсказать, как найти ближайшие 3 обьекта? Просто первый раз с массивами столкнулся.

Поиск по тегам, как найти ближний объект?

Поиск по тегам, как найти ближний объект?

Мне нужно найти объект с тегом Enemy, который находится ближе всего к объекту со скриптом. Я использовать такой код:

Используется csharp

targetEnemy = GameObject.FindWithTag (“Enemy”);
 

Но он находит мне 1-ый Enemy в списке объектов на сцене (во вкладке Hierarchy), по случаю этот объект дальше всех остальных. Подскажите, как мне найти БЛИЖАЙШЕЙ объект с тегом Enemy?

elfinik
UNIверсал
 
Сообщения: 390
Зарегистрирован: 24 фев 2013, 20:03

Re: Поиск по тегам, как найти ближний объект?

Сообщение trololoid 11 авг 2013, 16:18

private float temp=9999;
private GameObject nearest=null;

{
//Получаем любым удобным способом все объекты с тегом и запихиваем в массивлист List;
}

foreach(GameObject go in List){
float tmp2=Vector3.Distance(transform.position,go.position);
if(tmp2<temp){
temp=tmp2;
nearest=go;
}
}

На выходе в nearest будет ближайший :ymparty:

Аватара пользователя
trololoid
Старожил
 
Сообщения: 712
Зарегистрирован: 15 сен 2011, 19:18
Откуда: Туапсе, Краснодарский край, Россия

Re: Поиск по тегам, как найти ближний объект?

Сообщение elfinik 11 авг 2013, 16:23

Сейчас попробую, но такой вопрос… Не сильно ли это затратно? Допустим у меня есть башни, они должны каждый кадр искать таким образом врагов… Есть ли какой то более удобный вариант для этой задачи?

P.S. Враги ходят не по заданной траектории, а по большему плоскому пространству, а башни должны их отстреливать.

elfinik
UNIверсал
 
Сообщения: 390
Зарегистрирован: 24 фев 2013, 20:03

Re: Поиск по тегам, как найти ближний объект?

Сообщение BornFoRdeatH 11 авг 2013, 16:28

Поставить на башню тригер

Не бойся, если ты один, бойся, если ты ноль.

BornFoRdeatH
Адепт
 
Сообщения: 2377
Зарегистрирован: 22 окт 2011, 23:41
Откуда: Украина
Skype: bornfordeath

Re: Поиск по тегам, как найти ближний объект?

Сообщение elfinik 11 авг 2013, 16:33

Ну триггер то это можно, но мне еще и к юнитам нужно это применять, а там с триггером врятли прокатит.

Эх, я не умею создавать массивы и листы, а так же искать ВСЕ теги, только 1 умею искать) В Help’е другого не нашел(

elfinik
UNIверсал
 
Сообщения: 390
Зарегистрирован: 24 фев 2013, 20:03

Re: Поиск по тегам, как найти ближний объект?

Сообщение Левш@ 11 авг 2013, 16:47

Почему не прокатит ? Каждый входящий в триггер враг будет сам себя заносить в списокмассив целей.
Остается только башне обработать список целей и выбрать ближайшего.
Смотри класс Mathf у него есть метод который поможет в отборе (не помню как его звать…).

Аватара пользователя
Левш@
Адепт
 
Сообщения: 4073
Зарегистрирован: 14 окт 2009, 16:34
Откуда: IBERIA
Skype: bars_levsha
  • Сайт

Re: Поиск по тегам, как найти ближний объект?

Сообщение elfinik 11 авг 2013, 16:55

Я полазал, и нашел по массивам только это:

Используется csharp

public GameObject  cam;
        GameObject[] List = new GameObject[100];
List[1] = cam;

Хоть это правильно? Если я так же засуну все объекты в List, все правильно будет?

elfinik
UNIверсал
 
Сообщения: 390
Зарегистрирован: 24 фев 2013, 20:03

Re: Поиск по тегам, как найти ближний объект?

Сообщение Zaic 11 авг 2013, 17:20

Аватара пользователя
Zaic
Старожил
 
Сообщения: 758
Зарегистрирован: 18 июл 2013, 23:13
Откуда: Отсюда
Skype: У меня нет скайпа
  • Сайт


Вернуться в Почемучка

Кто сейчас на конференции

Сейчас этот форум просматривают: Google [Bot], Yandex [Bot] и гости: 18



Студворк — интернет-сервис помощи студентам

Скорее всего, данный вопрос задавался много раз, но мне не помогали решения, которые я нашел в интернете… Вместо того, чтобы идти к ближайшему, персонаж-бактерия направляется к самому новому.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
private Transform target;
    public float speed = 2f;
private GameObject targett;
    GameObject[] targets;
    GameObject iol;
    public GameObject Eda;
    public string nearest;
void Update()
    {
        Puk();
        transform.position += (target.position - transform.position).normalized * speed * Time.deltaTime;
        transform.up = Vector3.Lerp(transform.up, (target.position - transform.position), 1.5f * Time.deltaTime);
        if ((target.position - transform.position).sqrMagnitude < 0.1f)
        {
            Spawn();
            Destroy(targett);
            nutrition = nutrition + 1f;
            Puk();
        }
        
    }
 
    void Puk()
    {
        targets = GameObject.FindGameObjectsWithTag("Food");
        targett = GameObject.Find(Find().name);
        target = targett.transform;
    }
 
    GameObject Find()
    {
        float dist = Mathf.Infinity;
        Vector3 position = transform.position;
        foreach (GameObject go in targets)
        {
            Vector3 diff = go.transform.position;
            float currdist = diff.sqrMagnitude;
            if (currdist < dist)
            {
                iol = go;
                dist = currdist;
            }
        }
        return iol;
    }
 void Spawn()
    {
        int x = Random.Range(-10, 10);
        int y = Random.Range(-3, 3);
        int op = Random.Range(1, 100000);
        string ap = op + "";
        GameObject neweda = Instantiate(Eda);
        neweda.transform.position = new Vector3(x, y, 0);
        neweda.name = ap;
 
    }

Для поста, наверно, многовато кода, но я думаю чтобы понять полностью, этого предостаточно.

Буду благодарен за любую помощь, но мне бы код…

In this tutorial, we will learn how to find closest enemy in Unity 3D. Finding closest enemy or object is one of common requirement in developing games.

We will use following ways to calculate closest enemy.

  • Loop through enemy list
  • KDTree
  • Physics Sphere Overlap

Let’s try them one by one.

Find Closest Enemy in Unity 3D

Loop through Enemy List

This method is useful when you do not have many enemies in your application. Use the below script to find the closest enemy/ object in unity 3D.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class ClosestEnemyBehaviour : MonoBehaviour

{

    public Transform Player;

    public List<Transform> EnemyList;

    private Transform nearestEnemy;

    void Update()

    {

        float minimumDistance = Mathf.Infinity;

        if(nearestEnemy!=null)

        {

            nearestEnemy.GetComponent<MeshRenderer>().material.color = Color.green;

        }

        nearestEnemy = null;

        foreach(Transform enemy in EnemyList)

        {

            float distance = Vector3.Distance(Player.position, enemy.position);

            if ( distance < minimumDistance)

            {

                minimumDistance = distance;

                nearestEnemy = enemy;

            }

        }

        nearestEnemy.GetComponent<MeshRenderer>().material.color = Color.red;

        Debug.Log(“Nearest Enemy: “ + nearestEnemy + “; Distance: “ + minimumDistance);

    }

}

KDTree

If the enemy list is large or we want get the minimum distance between multiple group of objects then above method will take more time to calculate and will result in low FPS.

KDTree data structure is helps us to do these calculations very fast as it stores data in K-Dimensional space. Using KDTree to find closest object will improve the performance.

You can find many scripts on KDTree algorithm for Unity3D over internet. I will list couple of them here and we will use one for the example.

  • https://github.com/orifmilod/KdTree-Unity3D : Used in this example
  • https://github.com/viliwonka/KDTree
  • https://gist.github.com/ditzel/194ec800053ce7083b73faa1be9101b0

Example script using KdTree is given below. You can get the KdTree script on this link.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class ClosestEnemyKdTree : MonoBehaviour

{

    public Transform Player;

    //First add your enemy transform in C# List

    public List<Transform> EnemyList;

    private KdTree<Transform> enemyKdTree = new KdTree<Transform>();

    private Transform nearestEnemy;

    void Start()

    {

        // Update EnemyKdTree

        enemyKdTree.AddAll(EnemyList);

    }

    void Update()

    {

        if (nearestEnemy != null)

        {

            nearestEnemy.GetComponent<MeshRenderer>().material.color = Color.green;

        }

        // get closest object

        nearestEnemy = enemyKdTree.FindClosest(Player.position);

        nearestEnemy.GetComponent<MeshRenderer>().material.color = Color.red;

        float distance = Vector3.Distance(Player.position, nearestEnemy.position);

        Debug.Log(“Nearest Enemy: “ + nearestEnemy + “; Distance: “ + distance);

    }

}

Physics Sphere Overlap

We can also use physics OverlapSphere method to optimize the closest enemy calculation.

This will provide the collection of enemies which is inside the radius of the sphere. The sphere center will be player position. We will find the closest enemy only inside that sphere.

Below script will allows us to get the closest enemy using physics sphere overlap method. A layer is assigned to all enemy objects, so it will return only the enemy objects. To know more about layer masks, please take a quick look to this tutorial.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

using System.Collections;

using UnityEngine;

public class ClosestEnemyColliderOverlap : MonoBehaviour

{

    public Transform Player;

    public float OverlapRadius = 10.0f;

    private Transform nearestEnemy;

    private int enemyLayer;

    private void Start()

    {

        enemyLayer = LayerMask.NameToLayer(“Enemy”);

        Debug.Log(enemyLayer);

    }

    void Update()

    {

        if(nearestEnemy!=null)

        {

            nearestEnemy.GetComponent<MeshRenderer>().material.color = Color.green;

        }

        Collider[] hitColliders = Physics.OverlapSphere(Player.position, OverlapRadius, 1 << enemyLayer);

        float minimumDistance = Mathf.Infinity;

        foreach(Collider collider in hitColliders)

        {

            float distance = Vector3.Distance(Player.position, collider.transform.position);

            if (distance < minimumDistance)

            {

                minimumDistance = distance;

                nearestEnemy = collider.transform;

            }

        }

        if(nearestEnemy!=null)

        {

            nearestEnemy.GetComponent<MeshRenderer>().material.color = Color.red;

            Debug.Log(“Nearest Enemy: “ + nearestEnemy + “; Distance: “ + minimumDistance);

        }

        else

        {

            Debug.Log(“There is no enemy in the given radius”);

        }

    }

}

Output: The capsule object is player and Cubes are enemies. The nearest enemy will be turned to red.

Closest Enemy in Unity 3D

Hope you get an idea about finding closest enemy in unity 3D. Post your comments for queries and feedback. Thanks for reading.

Share Button

The following two tabs change content below.

  • Bio
  • Latest Posts

Gyanendu Shekhar is a technology enthusiast. He loves to learn new technologies. His area of interest includes Microsoft technologies, Augmented reality, Virtual reality, unity3d and android development.

Gyanendu Shekhar

Gyanendu Shekhar is a technology enthusiast. He loves to learn new technologies. His area of interest includes Microsoft technologies, Augmented reality, Virtual reality, unity3d and android development.

Добавить комментарий