I'm trying to make an AI for companions that have to follow the player and to get positioned in the closest position not yet occupied by another companion
i have a script in the companion:
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);
}
}
and a trigger in the positions:
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;
}
}
Acutally they reach the closest postion but they don't care if the position is occupied. (I also should try to make the code work properly for the "firstLine" gameObject array that now has not sense to exist) Sorry for the bad english...
-----------------EDIT--------------- I changed the way to choose the position to reach, working on the formation script:
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);
}
}
}
}
}
and edited the companions script:
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);
}
}
The problem is that now when a position is chosed they dont change target anymore...
In order to solve this, the companions should call a function on the positions that make them occupied instead of using onTrigger. Code for the positions to replace both OnTrigger functions:
public void SetEmptyOrNot(bool empty){
isEmpty = empty;
}
Code for first if statement companions:
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);
Do the same with the second if statement except use firstLine[1] instead.
The next step is to make the positions closed when the companions are not close to the positions.
For making the companions stop when near the position(no need for the position colliders):
void LateUpdate(){
if(Vector3.Distance(transform.position, target)>amountToStay)//amount to stay is to equal what
{
lineToGoTo.GetComponent<Postition>().SetEmptyOrNot(true);
}
}
Code for continuing to use colliders:
void LateUpdate(){
if(target.collider.bounds.Contains(transform.position))//amount to stay is to equal what
{
lineToGoTo.GetComponent<Postition>().SetEmptyOrNot(true);
}
}
What the code should look like at the end inside the Companion script:
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);
}
}
}
For the position script:
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;
}
}
This should solve the problem. The first one to start the update function will choose the closest position. If both companions have the same closest position, the first one to update will be the first one to pick it.
Improvements you may want to make:
There are two ways to improve this depending on what you intend to do so they cooperate with each other.
The 1st way is to let the companion with the closest distance to the position to take it.
The 2nd way is to make it so the farther companion from the closest point to occupy that point so that it catches up