Unity: как заставить gameObject достичь ближайшей свободной позиции - PullRequest
0 голосов
/ 03 августа 2020

Я пытаюсь создать ИИ для компаньонов, которые должны следовать за игроком и занимать ближайшую позицию, еще не занятую другим компаньоном введите описание изображения здесь

у меня есть скрипт в сопутствующем:

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

public class ReachPosition : MonoBehaviour
{
 public GameObject[] firstLine = new GameObject[2];

 Transform target;
 // Start is called before the first frame update
 void Start()
 {

 }

 // Update is called once per frame
 void Update()
 {
     //calculate distance from each position
     float dist = Vector3.Distance(transform.position, firstLine[0].transform.position);
     float dist1 = Vector3.Distance(transform.position, firstLine[1].transform.position);

     if (dist < dist1 && firstLine[0].GetComponent<Position>().isEmpty)
     {
         target = firstLine[0].transform;

         Debug.Log(0);
     }
     else if (dist > dist1 && firstLine[1].GetComponent<Position>().isEmpty)
     {
         target = firstLine[1].transform;

         Debug.Log(1);
     }

     //reach target
     transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(target.position - transform.position), 1 * Time.deltaTime);
     transform.position = Vector3.MoveTowards(transform.position, target.position, .01f);


 }

}

и триггер в позициях:

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

 public class Position : MonoBehaviour
 {
 public bool isEmpty;
 // Start is called before the first frame update
 void Start()
 {
     isEmpty = true;
 }

 private void OnTriggerEnter(Collider other)
 {
     isEmpty = false;
 }
 private void OnTriggerExit(Collider other)
 {
     isEmpty = true;
 }

}

Фактически они достигают ближайшей позиции, но им все равно, занята ли позиция. (Я также должен попытаться заставить код работать правильно для массива gameObject "firstLine", который теперь не имеет смысла существовать) Извините за плохой engli sh ...

------- ---------- РЕДАКТИРОВАТЬ --------------- Я изменил способ выбора позиции для достижения, работая над скриптом формирования:

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

public class Formation : MonoBehaviour
{
public List<GameObject> firstLine = new List<GameObject>();
public List<GameObject> Companions = new List<GameObject>();
// Start is called before the first frame update
void Start()
{
    
}

// Update is called once per frame
void Update()
{
    for (int i = 0; i < Companions.Count; i++)
    {
        //set the position to reach to the first non occupied position
        int a = 0;
        while (!firstLine[a].GetComponent<Position>().isEmpty)
        {
            a++;
        }
        Companions[i].GetComponent<Companion>().objectToReach = firstLine[a];
        Companions[i].GetComponent<Companion>().distanceFromTarget = Vector3.Distance(Companions[i].transform.position, firstLine[a].transform.position);
        firstLine[a].GetComponent<Position>().SetEmptyOrNot(false);

        //set the position to reach to the closest non occupied position
        for (int j = 0; j < firstLine.Count; j++)
        {
            float dist = Vector3.Distance(Companions[i].transform.position, firstLine[j].transform.position);
            if (dist < Companions[i].GetComponent<Companion>().distanceFromTarget && firstLine[j].GetComponent<Position>().isEmpty)
            {
                Companions[i].GetComponent<Companion>().objectToReach.GetComponent<Position>().SetEmptyOrNot(true);
                Companions[i].GetComponent<Companion>().objectToReach = firstLine[j];
                Companions[i].GetComponent<Companion>().distanceFromTarget = dist;

                firstLine[j].GetComponent<Position>().SetEmptyOrNot(false);
                
            }
        }
    }
}

}

и отредактировал скрипт компаньонов:

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

public class Companion : MonoBehaviour
{
public GameObject objectToReach;
public float distanceFromTarget;
Transform target;

// Start function not needed

// Update is called once per frame
void Update()
{
    target = objectToReach.transform;
    //reach target
    transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(target.position - transform.position), 1 * Time.deltaTime);
    transform.position = Vector3.MoveTowards(transform.position, target.position, .01f);
}

}

Проблема в том, что теперь, когда позиция выбрана, они больше не меняют цель ...

1 Ответ

0 голосов
/ 03 августа 2020

Чтобы решить эту проблему, компаньоны должны вызывать функцию на позициях, которые делают их занятыми, вместо использования onTrigger. Код для позиций, заменяющий обе функции OnTrigger:

public void SetEmptyOrNot(bool empty){
     isEmpty = empty;
}

Код для первого сопутствующего оператора if:

lineToGoTo = firstLine[0];//needed for future step
//Position is the scripts name that is to be accessed
lineToGoTo.GetComponent<Postition>().SetEmptyOrNot(false);//In the future try putting the firstLine[0].GetComponent<Postition>() in a variable for increased performance
target = lineToGoTo.transform;
Debug.Log(0);

Сделайте то же самое со вторым оператором if, но вместо этого используйте firstLine [1].

следующий шаг - закрыть позиции, когда компаньоны не находятся рядом с позициями.

Чтобы заставить компаньонов останавливаться, когда они рядом с позицией (нет необходимости в коллайдерах позиций):

void LateUpdate(){
    if(Vector3.Distance(transform.position, target)>amountToStay)//amount to stay is to equal what 
    {
         lineToGoTo.GetComponent<Postition>().SetEmptyOrNot(true);
    }
}

Код для продолжения использования коллайдеров:

void LateUpdate(){
if(target.collider.bounds.Contains(transform.position))//amount to stay is to equal what 
{
    lineToGoTo.GetComponent<Postition>().SetEmptyOrNot(true);
}
}

Как должен выглядеть код в конце внутри сценария Companion:

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

public class ReachPosition : MonoBehaviour
{
 public GameObject[] firstLine = new GameObject[2];
 public float amountToStay;
 private GameObject lineToGoTo;
 Transform target;
 // Start function not needed

 // Update is called once per frame
 void Update()
 {
     //calculate distance from each position
     float dist = Vector3.Distance(transform.position, firstLine[0].transform.position);
     float dist1 = Vector3.Distance(transform.position, firstLine[1].transform.position);

     if (dist < dist1 && firstLine[0].GetComponent<Position>().isEmpty)
     {
         lineToGoTo = firstLine[0];//needed for future step
         //Position is the scripts name that is to be accessed
         lineToGoTo.GetComponent<Postition>().SetEmptyOrNot(false);//In the future try putting the lineTo.GetComponent<Postition>() in a variable for increased performance
         target = lineToGoTo.transform;
         Debug.Log(0);
     }
     else if (dist > dist1 && firstLine[1].GetComponent<Position>().isEmpty)
     {
         lineToGoTo = firstLine[1];//needed for future step
         //Position is the scripts name that is to be accessed
         lineToGoTo.GetComponent<Postition>().SetEmptyOrNot(false);//In the future try putting the lineTo.GetComponent<Postition>() in a variable for increased performance
         target = lineToGoTo.transform;
         Debug.Log(1);
     }

     //reach target
     transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(target.position - transform.position), 1 * Time.deltaTime);
     transform.position = Vector3.MoveTowards(transform.position, target.position, .01f);
 }
 void LateUpdate(){//use this or the collider option given above
        if(Vector3.Distance(transform.position, lineToGoTo.transform.position)>amountToStay)//amount to stay is to equal what 
        {
            lineToGoTo.GetComponent<Postition>().SetEmptyOrNot(true);
        }
 }
 }

Для сценария позиции:

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

 public class Position : MonoBehaviour
 {
 public bool isEmpty;
 // Start is called before the first frame update
 void Start()
 {
     isEmpty = true;
 }

 public void SetEmptyOrNot(bool empty){
       isEmpty = empty;
 }
}

Это должно решить проблему. Первый, кто запустит функцию обновления, выберет ближайшую позицию. Если оба спутника имеют одинаковую ближайшую позицию, то обновленный первым будет первым, кто выберет его.

Возможные улучшения:

Есть два способа улучшить это в зависимости от того, что вы собираетесь делать, чтобы они взаимодействовали друг с другом.

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

Второй способ - сделать так, чтобы спутник, находящийся дальше от ближайшей точки, занимал эту точку и догонял

...