Я сделал 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
13.5k12 золотых знаков43 серебряных знака72 бронзовых знака
задан 31 июл 2017 в 15:36
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
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 TriumphovIvan Triumphov
1,1081 золотой знак14 серебряных знаков37 бронзовых знаков
Идеологически можно подойти к решению 2 способами:
- Один раз найти ближайшую цель, запомнить ее и двигаться в ее направлении до тех пор, пока она не будет уничтожена
- Каждый раз при вызове Update() пересчитывать расстояния и каждый раз искать ближайшую цель. Это может быть точнее, чем первый вариант, потому что будет учитывать скорость перемещения целей (если они движутся, и если они движутся с разной скоростью), однако требует пересчета при каждом вызове Update, в силу чего менее оптимально, чем первый вариант.
Однако, стоит заметить, что вам посоветовали верный способ поиска ближайшей цели (использование magnitude) Вы также можете попробовать Vector3.Distance, но судя по документации, этот метод работает через magnitude. Или можете попробовать Vector3.sqrMagnitude, он схож по идее работы с magnitude, но работает чуть быстрее, за счет того, что не выполняется операция вычисления квадратного корня, однако в силу этого дает менее точный результат. Вы можете почитать обсуждения на тему методов поиска ближайшего объекта здесь и здесь.
Вам может казаться результат вычисления ближайшей цели неверным, если работаете с вычислениями через Vector3, так как он работает с 3 измерениями (А скорее всего речь идет именно о трехмерной игре, так как вы используете навигацию Unity). Например, если взять игру с видом сверху, то учет разности высот при измерении расстояния может казаться визуально неверным, но точным математически. Таким образом вы можете попробовать считать расстояния с помощью тех же методов magnitude, sqrMagnitude или Distance, но используя Vector2. Тогда при вычислении расстояния учитываться будут только x и y координаты объектов. Такой подход может дать более “верное” решение задачи с точки зрения визуальной части.
ответ дан 31 июл 2017 в 19:29
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 будет ближайший
-
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# | ||
|
Для поста, наверно, многовато кода, но я думаю чтобы понять полностью, этого предостаточно.
Буду благодарен за любую помощь, но мне бы код…
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.
Hope you get an idea about finding closest enemy in unity 3D. Post your comments for queries and feedback. Thanks for reading.
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.