Не знаю насколько это решение разумно, но суть такова - от объекта до цели создается сетка из триггеров, которые "сканируют" местность на присутствие (или отсутствие ) препятствия. Затем каждый триггер передает данные на карту состоящею из объектов Cell (кладем просто аккуратно в папку со всеми скриптами):
Синтаксис:
Используется csharp
public class Cell
{
public bool passable; //Проходима ли клетка
public int F; // Значение в клетке
public bool begin; // начало пути
public bool back; // конец пути
public int G; // цена пути от начала
private Cell parent;
public int x, y;
public Cell()
{
passable = true;
G = 0;
}
public Cell GetParrent()
{
if (parent == null)
return null;
return parent;
}
public void SetParent(Cell cell)
{
parent = cell;
}
public void SetLocale(int x, int y) // установим местонахождение на карте
{
this.x = x;
this.y = y;
}
}
{
public bool passable; //Проходима ли клетка
public int F; // Значение в клетке
public bool begin; // начало пути
public bool back; // конец пути
public int G; // цена пути от начала
private Cell parent;
public int x, y;
public Cell()
{
passable = true;
G = 0;
}
public Cell GetParrent()
{
if (parent == null)
return null;
return parent;
}
public void SetParent(Cell cell)
{
parent = cell;
}
public void SetLocale(int x, int y) // установим местонахождение на карте
{
this.x = x;
this.y = y;
}
}
В последствий эта карта обрабатывается по упрощенному мною алгоритму А*. Так как этот скрипт решает задачу "Прогулки", то есть животное просто бегает по лесу на определенной территории - я не усложняю алгоритм поиска.
Сам скрипт (его мы и крепим на моба):
Синтаксис:
Используется csharp
using UnityEngine;
using System;
using System.Collections.Generic;
public class PathBuilding : MonoBehaviour
{
Transform Aim; // сюда нужно прийти
public GameObject ScannerPrefab; // Префаб клетки
public int size; // Размер ячеек size*size
public int FiledExtendet; // насколько ячеек расширить поле
GameObject[,] MapForTrassing; // карта для трассировки
int x, y;// размер карты Cell
float delay; // задержка для того чтобы FieldPrefab's смогли определить проходима ли клетка
GameObject parent;
Cell[,] map;
bool begin, back; // у нас есть начало и конец, если true
// Переменные описывающие размеры основного поля
// нумерация по часовой стрелки от AI
Vector3 vertex1; // AI
Vector3 vertex2; // правый нижний угол относитeльно AI
Vector3 vertex3; // AIM
Vector3 vertex4;
bool step1 = true, step2 = true;
bool pathComplete; // если true, то есть готовый путь
bool error;
void Start()
{
ScannerPrefab.GetComponent<BoxCollider>().size = new Vector3(size, ScannerPrefab.GetComponent<BoxCollider>().size.y, size);
FiledExtendet *= size;
}
void Update()
{
if (delay <= 0)
{
if (step1 == false) // создание объектов для сбора данных
{
error = false;
begin = back = false;
x = y = 0;
// Заполняем массив MapForTrassing
vertexRectDeterm();
step1 = true;
}
else if (step2 == false)
{
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
{
map[i, k].SetLocale(i, k);
if (MapForTrassing[i, k] == null)
{
Error();
return;
}
map[i, k].passable = MapForTrassing[i, k].GetComponent<Scanner>().GetPassable();
// Поиск начала и конца пути
if (begin == false && Vector3.Distance(this.gameObject.transform.position, MapForTrassing[i, k].gameObject.transform.position) <= size && map[i, k].passable == true)
{
map[i, k].begin = true;
begin = true;
}
else if (back == false && Vector3.Distance(Aim.transform.position, MapForTrassing[i, k].gameObject.transform.position) <= size && map[i, k].passable == true)
{
map[i, k].back = true;
back = true;
}
}
Seacrh(); // сам поиск пути
step2 = true;
}
}
else delay -= Time.deltaTime;
}
public bool GetError() // если true, то была ошибка
{
return error;
}
public bool GetComplete() // возвращает true, если путь найден
{
return pathComplete;
}
public Transform[] GetPath()
{
Cell cell = backPath; // получили точку конечного пути*/
List<Transform> mass = new List<Transform>();
while (true)
{
if (MapForTrassing[cell.x, cell.y] != null && Vector3.Distance(MapForTrassing[cell.x, cell.y].transform.position, this.transform.position) > size)
{
// Добавляем Scanner's в путь
MapForTrassing[cell.x, cell.y].GetComponent<Scanner>().Activated();
mass.Add(MapForTrassing[cell.x, cell.y].transform);
}
if (cell.GetParrent() == null)
break;
cell = cell.GetParrent();
}
pathComplete = false;
foreach (GameObject element in MapForTrassing)
if (element.GetComponent<Scanner>().GetON() == false)
GameObject.Destroy(element);
if (mass == null)
Error();
if (Aim != null)
GameObject.Destroy(Aim.gameObject);
return mass.ToArray();
}
public void CtreatePath(Transform Aim)
{
step1 = step2 = false;
this.Aim = Aim;
}
void getKorrdinate(Cell cell, out int _x, out int _y)
{
_x = cell.x;
_y = cell.y;
}
void Error() // Вставить туда где выходят ошибки!
{
error = true;
step1 = step2 = true;
pathComplete = false;
}
#region Сам алгоритм построения пути
List<Cell> queue; // очередь
List<Cell> closeCells; // ячейки которые отработаны и больше не нужны
Cell backPath; // конец пути
void Seacrh()
{
if (back == false || begin == false)
Error();
else
{
queue = new List<Cell>();
closeCells = new List<Cell>();
queue.Add(GetBeginCell()); // добавляем начальную точку в очередь
backPath = GetBackCell();
Generation();
pathComplete = true;
}
}
void Generation() // А*, запускает алгоритм
{
while (true)
{
Cell cell;
if (queue.Count > 0)
{
cell = GetValidationCell();
closeCells.Add(cell);
queue.Remove(cell);
}
else
{
Error();
break;
}
List<Cell> wave = new List<Cell>();
if (cell.x + 1 < x && map[cell.x + 1, cell.y].passable == true && CloseCell(map[cell.x + 1, cell.y]) == false)
{
map[cell.x + 1, cell.y].SetParent(cell);
map[cell.x + 1, cell.y].F = GetExresticalH(map[cell.x + 1, cell.y]);
wave.Add(map[cell.x + 1, cell.y]);
}
if (cell.x - 1 > -1 && map[cell.x - 1, cell.y].passable == true && CloseCell(map[cell.x - 1, cell.y]) == false)
{
map[cell.x - 1, cell.y].SetParent(cell);
map[cell.x - 1, cell.y].F = GetExresticalH(map[cell.x - 1, cell.y]);
wave.Add(map[cell.x - 1, cell.y]);
}
if (cell.y + 1 < y && map[cell.x, cell.y + 1].passable == true && CloseCell(map[cell.x, cell.y + 1]) == false)
{
map[cell.x, cell.y + 1].SetParent(cell);
map[cell.x, cell.y + 1].F = GetExresticalH(map[cell.x, cell.y + 1]);
wave.Add(map[cell.x, cell.y + 1]);
}
if (cell.y - 1 > -1 && map[cell.x, cell.y - 1].passable == true && CloseCell(map[cell.x, cell.y - 1]) == false)
{
map[cell.x, cell.y - 1].SetParent(cell);
map[cell.x, cell.y - 1].F = GetExresticalH(map[cell.x, cell.y - 1]);
wave.Add(map[cell.x, cell.y - 1]);
}
bool exitFlag = false;
foreach (Cell elements in wave)
{
if (elements == backPath)
exitFlag = true;
else if (OpenCell(elements) == false) // ячейка не находиться в открытом списке
queue.Add(elements);
}
if (exitFlag == true)
break;
}
}
Cell[,] getMap()
{
return map;
}
Cell GetValidationCell() // получает самую перспективную ячеку из Queue
{
Cell max = queue[queue.Count - 1];
foreach (Cell element in queue)
if (element.F < max.F)
max = element;
return max;
}
int GetExresticalH(Cell cell)
{
int H = 0;
int xDistance = Math.Abs(cell.x - backPath.x);
int yDistance = Math.Abs(cell.y - backPath.y);
if (xDistance > yDistance)
H = 14 * yDistance + 10 * (xDistance - yDistance);
else
H = 14 * xDistance + 10 * (yDistance - xDistance);
return H;
}
bool OpenCell(Cell cell)
{
foreach (Cell element in queue)
if (element == cell)
return true;
return false;
}
bool CloseCell(Cell cell) // есть ли эта клетка в закрытом списке
{
foreach (Cell element in closeCells)
if (element == cell)
return true;
return false;
}
Cell GetBeginCell() // получить начальную точку
{
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
if (map[i, k].begin == true)
return map[i, k];
return null;
}
Cell GetBackCell() // получить конечную точку
{
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
if (map[i, k].back == true)
return map[i, k];
return null;
}
#endregion
#region Приводит сетку для заполнения
void vertexRectDeterm() // задаем вершины поля
{
// наши координаты
float x1 = this.transform.position.x;
float z1 = this.transform.position.z;
// координаты объекта конца пути
float x2 = Aim.transform.position.x;
float z2 = Aim.transform.position.z;
float height = Aim.transform.position.y;
if (x1 < x2 && z1 < z2) // 1
{
vertex1 = new Vector3((int)x1 - FiledExtendet, height, (int)z1 - FiledExtendet);
vertex2 = new Vector3((int)x2 + FiledExtendet, height, (int)z1 - FiledExtendet);
vertex3 = new Vector3((int)x2 + FiledExtendet, height, (int)z2 + FiledExtendet);
// дальше расширяем границы, так чтобы ячеки размером size на size корректно встали
while (true)
if ((int)vertex3.x % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x + 1, height, (int)vertex3.z);
while (true)
if ((int)vertex3.z % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x, height, (int)vertex3.z + 1);
vertex4 = new Vector3((int)x1 - FiledExtendet, height, (int)z2 + FiledExtendet);
parent = (GameObject)Instantiate(ScannerPrefab, this.transform.position, new Quaternion(0, 0, 0, 0));
MapForTrassing = new GameObject[x = (((int)vertex4.z - (int)vertex1.z) / size), y = (((int)vertex2.x - (int)vertex1.x) / size)]; // массив точек путей столбцы - строки
map = new Cell[x, y];
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
map[i, k] = new Cell();
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
{
MapForTrassing[i, k] = (GameObject)Instantiate(ScannerPrefab, new Vector3(vertex1.x + k * size + 0.5f, height, vertex1.z + i * size + 0.5f), new Quaternion(0, 0, 0, 0)); // создаем ячейку
MapForTrassing[i, k].transform.parent = parent.transform; // отдaем ее родителю, общему для группы ячеек
MapForTrassing[i, k].GetComponent<Scanner>().SetAI(this.gameObject);
}
}
else if (x1 > x2 && z1 > z2)// 2
{
vertex1 = new Vector3((int)x1 + FiledExtendet, height, (int)z1 + FiledExtendet);
vertex2 = new Vector3((int)x2 - FiledExtendet, height, (int)z1 + FiledExtendet);
vertex3 = new Vector3((int)x2 - FiledExtendet, height, (int)z2 - FiledExtendet);
// дальше расширяем границы, так чтобы ячеки размером size на size корректно встали
while (true)
if ((int)vertex3.x % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x - 1, height, (int)vertex3.z);
while (true)
if ((int)vertex3.z % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x, height, (int)vertex3.z - 1);
vertex4 = new Vector3((int)x1 + FiledExtendet, height, (int)z2 - FiledExtendet);
parent = (GameObject)Instantiate(ScannerPrefab, this.transform.position, new Quaternion(0, 0, 0, 0));
MapForTrassing = new GameObject[x = (((int)vertex2.z - (int)vertex3.z) / size), y = ((int)vertex4.x - (int)vertex3.x) / size]; // массив точек путей столбцы - строки
map = new Cell[x, y];
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
map[i, k] = new Cell();
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
{
MapForTrassing[i, k] = (GameObject)Instantiate(ScannerPrefab, new Vector3(vertex3.x + k * size + 0.5f, height, vertex3.z + i * size + 0.5f), new Quaternion(0, 0, 0, 0));
MapForTrassing[i, k].transform.parent = parent.transform;
MapForTrassing[i, k].GetComponent<Scanner>().SetAI(this.gameObject);
}
}
else if (x1 > x2 && z1 < z2)// 3
{
vertex1 = new Vector3((int)x1 + FiledExtendet, height, (int)z1 - FiledExtendet);
vertex2 = new Vector3((int)x1 + FiledExtendet, height, (int)z2 + FiledExtendet);
vertex3 = new Vector3((int)x2 - FiledExtendet, height, (int)z2 + FiledExtendet);
// дальше расширяем границы, так чтобы ячеки размером size на size корректно встали
while (true)
if ((int)vertex3.x % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x - 1, height, (int)vertex3.z);
while (true)
if ((int)vertex3.z % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x, height, (int)vertex3.z + 1);
vertex4 = new Vector3((int)x2 - FiledExtendet, height, (int)z1 - FiledExtendet);
parent = (GameObject)Instantiate(ScannerPrefab, this.transform.position, new Quaternion(0, 0, 0, 0));
MapForTrassing = new GameObject[x = (((int)vertex3.z - (int)vertex4.z) / size), y = ((int)vertex1.x - (int)vertex4.x) / size]; // массив точек путей столбцы - строки
map = new Cell[x, y];
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
map[i, k] = new Cell();
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
{
MapForTrassing[i, k] = (GameObject)Instantiate(ScannerPrefab, new Vector3(vertex4.x + k * size + 0.5f, height, vertex4.z + i * size + 0.5f), new Quaternion(0, 0, 0, 0));
MapForTrassing[i, k].transform.parent = parent.transform;
MapForTrassing[i, k].GetComponent<Scanner>().SetAI(this.gameObject);
}
}
else if (x1 < x2 && z1 > z2)// 4
{
vertex1 = new Vector3((int)x1 - FiledExtendet, height, (int)z1 + FiledExtendet);
vertex2 = new Vector3((int)x1 - FiledExtendet, height, (int)z2 - FiledExtendet);
vertex3 = new Vector3((int)x2 + FiledExtendet, height, (int)z2 - FiledExtendet);
// дальше расширяем границы, так чтобы ячеки размером size на size корректно встали
while (true)
if ((int)vertex3.x % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x + 1, height, (int)vertex3.z);
while (true)
if ((int)vertex3.z % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x, height, (int)vertex3.z - 1);
vertex4 = new Vector3((int)x2 + FiledExtendet, height, (int)z1 + FiledExtendet);
parent = (GameObject)Instantiate(ScannerPrefab, this.transform.position, new Quaternion(0, 0, 0, 0));
MapForTrassing = new GameObject[x = (((int)vertex1.z - (int)vertex2.z) / size), y = ((int)vertex3.x - (int)vertex2.x) / size]; // массив точек путей, [столбцы, строки]
map = new Cell[x, y];
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
map[i, k] = new Cell();
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
{
MapForTrassing[i, k] = (GameObject)Instantiate(ScannerPrefab, new Vector3(vertex2.x + k * size + 0.5f, height, vertex2.z + i * size + 0.5f), new Quaternion(0, 0, 0, 0));
MapForTrassing[i, k].transform.parent = parent.transform;
MapForTrassing[i, k].GetComponent<Scanner>().SetAI(this.gameObject);
}
}
delay = Time.deltaTime * 2;
}
public void DestroyMapPath()
{
GameObject.Destroy(parent);
}
#endregion
}
using System;
using System.Collections.Generic;
public class PathBuilding : MonoBehaviour
{
Transform Aim; // сюда нужно прийти
public GameObject ScannerPrefab; // Префаб клетки
public int size; // Размер ячеек size*size
public int FiledExtendet; // насколько ячеек расширить поле
GameObject[,] MapForTrassing; // карта для трассировки
int x, y;// размер карты Cell
float delay; // задержка для того чтобы FieldPrefab's смогли определить проходима ли клетка
GameObject parent;
Cell[,] map;
bool begin, back; // у нас есть начало и конец, если true
// Переменные описывающие размеры основного поля
// нумерация по часовой стрелки от AI
Vector3 vertex1; // AI
Vector3 vertex2; // правый нижний угол относитeльно AI
Vector3 vertex3; // AIM
Vector3 vertex4;
bool step1 = true, step2 = true;
bool pathComplete; // если true, то есть готовый путь
bool error;
void Start()
{
ScannerPrefab.GetComponent<BoxCollider>().size = new Vector3(size, ScannerPrefab.GetComponent<BoxCollider>().size.y, size);
FiledExtendet *= size;
}
void Update()
{
if (delay <= 0)
{
if (step1 == false) // создание объектов для сбора данных
{
error = false;
begin = back = false;
x = y = 0;
// Заполняем массив MapForTrassing
vertexRectDeterm();
step1 = true;
}
else if (step2 == false)
{
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
{
map[i, k].SetLocale(i, k);
if (MapForTrassing[i, k] == null)
{
Error();
return;
}
map[i, k].passable = MapForTrassing[i, k].GetComponent<Scanner>().GetPassable();
// Поиск начала и конца пути
if (begin == false && Vector3.Distance(this.gameObject.transform.position, MapForTrassing[i, k].gameObject.transform.position) <= size && map[i, k].passable == true)
{
map[i, k].begin = true;
begin = true;
}
else if (back == false && Vector3.Distance(Aim.transform.position, MapForTrassing[i, k].gameObject.transform.position) <= size && map[i, k].passable == true)
{
map[i, k].back = true;
back = true;
}
}
Seacrh(); // сам поиск пути
step2 = true;
}
}
else delay -= Time.deltaTime;
}
public bool GetError() // если true, то была ошибка
{
return error;
}
public bool GetComplete() // возвращает true, если путь найден
{
return pathComplete;
}
public Transform[] GetPath()
{
Cell cell = backPath; // получили точку конечного пути*/
List<Transform> mass = new List<Transform>();
while (true)
{
if (MapForTrassing[cell.x, cell.y] != null && Vector3.Distance(MapForTrassing[cell.x, cell.y].transform.position, this.transform.position) > size)
{
// Добавляем Scanner's в путь
MapForTrassing[cell.x, cell.y].GetComponent<Scanner>().Activated();
mass.Add(MapForTrassing[cell.x, cell.y].transform);
}
if (cell.GetParrent() == null)
break;
cell = cell.GetParrent();
}
pathComplete = false;
foreach (GameObject element in MapForTrassing)
if (element.GetComponent<Scanner>().GetON() == false)
GameObject.Destroy(element);
if (mass == null)
Error();
if (Aim != null)
GameObject.Destroy(Aim.gameObject);
return mass.ToArray();
}
public void CtreatePath(Transform Aim)
{
step1 = step2 = false;
this.Aim = Aim;
}
void getKorrdinate(Cell cell, out int _x, out int _y)
{
_x = cell.x;
_y = cell.y;
}
void Error() // Вставить туда где выходят ошибки!
{
error = true;
step1 = step2 = true;
pathComplete = false;
}
#region Сам алгоритм построения пути
List<Cell> queue; // очередь
List<Cell> closeCells; // ячейки которые отработаны и больше не нужны
Cell backPath; // конец пути
void Seacrh()
{
if (back == false || begin == false)
Error();
else
{
queue = new List<Cell>();
closeCells = new List<Cell>();
queue.Add(GetBeginCell()); // добавляем начальную точку в очередь
backPath = GetBackCell();
Generation();
pathComplete = true;
}
}
void Generation() // А*, запускает алгоритм
{
while (true)
{
Cell cell;
if (queue.Count > 0)
{
cell = GetValidationCell();
closeCells.Add(cell);
queue.Remove(cell);
}
else
{
Error();
break;
}
List<Cell> wave = new List<Cell>();
if (cell.x + 1 < x && map[cell.x + 1, cell.y].passable == true && CloseCell(map[cell.x + 1, cell.y]) == false)
{
map[cell.x + 1, cell.y].SetParent(cell);
map[cell.x + 1, cell.y].F = GetExresticalH(map[cell.x + 1, cell.y]);
wave.Add(map[cell.x + 1, cell.y]);
}
if (cell.x - 1 > -1 && map[cell.x - 1, cell.y].passable == true && CloseCell(map[cell.x - 1, cell.y]) == false)
{
map[cell.x - 1, cell.y].SetParent(cell);
map[cell.x - 1, cell.y].F = GetExresticalH(map[cell.x - 1, cell.y]);
wave.Add(map[cell.x - 1, cell.y]);
}
if (cell.y + 1 < y && map[cell.x, cell.y + 1].passable == true && CloseCell(map[cell.x, cell.y + 1]) == false)
{
map[cell.x, cell.y + 1].SetParent(cell);
map[cell.x, cell.y + 1].F = GetExresticalH(map[cell.x, cell.y + 1]);
wave.Add(map[cell.x, cell.y + 1]);
}
if (cell.y - 1 > -1 && map[cell.x, cell.y - 1].passable == true && CloseCell(map[cell.x, cell.y - 1]) == false)
{
map[cell.x, cell.y - 1].SetParent(cell);
map[cell.x, cell.y - 1].F = GetExresticalH(map[cell.x, cell.y - 1]);
wave.Add(map[cell.x, cell.y - 1]);
}
bool exitFlag = false;
foreach (Cell elements in wave)
{
if (elements == backPath)
exitFlag = true;
else if (OpenCell(elements) == false) // ячейка не находиться в открытом списке
queue.Add(elements);
}
if (exitFlag == true)
break;
}
}
Cell[,] getMap()
{
return map;
}
Cell GetValidationCell() // получает самую перспективную ячеку из Queue
{
Cell max = queue[queue.Count - 1];
foreach (Cell element in queue)
if (element.F < max.F)
max = element;
return max;
}
int GetExresticalH(Cell cell)
{
int H = 0;
int xDistance = Math.Abs(cell.x - backPath.x);
int yDistance = Math.Abs(cell.y - backPath.y);
if (xDistance > yDistance)
H = 14 * yDistance + 10 * (xDistance - yDistance);
else
H = 14 * xDistance + 10 * (yDistance - xDistance);
return H;
}
bool OpenCell(Cell cell)
{
foreach (Cell element in queue)
if (element == cell)
return true;
return false;
}
bool CloseCell(Cell cell) // есть ли эта клетка в закрытом списке
{
foreach (Cell element in closeCells)
if (element == cell)
return true;
return false;
}
Cell GetBeginCell() // получить начальную точку
{
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
if (map[i, k].begin == true)
return map[i, k];
return null;
}
Cell GetBackCell() // получить конечную точку
{
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
if (map[i, k].back == true)
return map[i, k];
return null;
}
#endregion
#region Приводит сетку для заполнения
void vertexRectDeterm() // задаем вершины поля
{
// наши координаты
float x1 = this.transform.position.x;
float z1 = this.transform.position.z;
// координаты объекта конца пути
float x2 = Aim.transform.position.x;
float z2 = Aim.transform.position.z;
float height = Aim.transform.position.y;
if (x1 < x2 && z1 < z2) // 1
{
vertex1 = new Vector3((int)x1 - FiledExtendet, height, (int)z1 - FiledExtendet);
vertex2 = new Vector3((int)x2 + FiledExtendet, height, (int)z1 - FiledExtendet);
vertex3 = new Vector3((int)x2 + FiledExtendet, height, (int)z2 + FiledExtendet);
// дальше расширяем границы, так чтобы ячеки размером size на size корректно встали
while (true)
if ((int)vertex3.x % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x + 1, height, (int)vertex3.z);
while (true)
if ((int)vertex3.z % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x, height, (int)vertex3.z + 1);
vertex4 = new Vector3((int)x1 - FiledExtendet, height, (int)z2 + FiledExtendet);
parent = (GameObject)Instantiate(ScannerPrefab, this.transform.position, new Quaternion(0, 0, 0, 0));
MapForTrassing = new GameObject[x = (((int)vertex4.z - (int)vertex1.z) / size), y = (((int)vertex2.x - (int)vertex1.x) / size)]; // массив точек путей столбцы - строки
map = new Cell[x, y];
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
map[i, k] = new Cell();
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
{
MapForTrassing[i, k] = (GameObject)Instantiate(ScannerPrefab, new Vector3(vertex1.x + k * size + 0.5f, height, vertex1.z + i * size + 0.5f), new Quaternion(0, 0, 0, 0)); // создаем ячейку
MapForTrassing[i, k].transform.parent = parent.transform; // отдaем ее родителю, общему для группы ячеек
MapForTrassing[i, k].GetComponent<Scanner>().SetAI(this.gameObject);
}
}
else if (x1 > x2 && z1 > z2)// 2
{
vertex1 = new Vector3((int)x1 + FiledExtendet, height, (int)z1 + FiledExtendet);
vertex2 = new Vector3((int)x2 - FiledExtendet, height, (int)z1 + FiledExtendet);
vertex3 = new Vector3((int)x2 - FiledExtendet, height, (int)z2 - FiledExtendet);
// дальше расширяем границы, так чтобы ячеки размером size на size корректно встали
while (true)
if ((int)vertex3.x % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x - 1, height, (int)vertex3.z);
while (true)
if ((int)vertex3.z % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x, height, (int)vertex3.z - 1);
vertex4 = new Vector3((int)x1 + FiledExtendet, height, (int)z2 - FiledExtendet);
parent = (GameObject)Instantiate(ScannerPrefab, this.transform.position, new Quaternion(0, 0, 0, 0));
MapForTrassing = new GameObject[x = (((int)vertex2.z - (int)vertex3.z) / size), y = ((int)vertex4.x - (int)vertex3.x) / size]; // массив точек путей столбцы - строки
map = new Cell[x, y];
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
map[i, k] = new Cell();
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
{
MapForTrassing[i, k] = (GameObject)Instantiate(ScannerPrefab, new Vector3(vertex3.x + k * size + 0.5f, height, vertex3.z + i * size + 0.5f), new Quaternion(0, 0, 0, 0));
MapForTrassing[i, k].transform.parent = parent.transform;
MapForTrassing[i, k].GetComponent<Scanner>().SetAI(this.gameObject);
}
}
else if (x1 > x2 && z1 < z2)// 3
{
vertex1 = new Vector3((int)x1 + FiledExtendet, height, (int)z1 - FiledExtendet);
vertex2 = new Vector3((int)x1 + FiledExtendet, height, (int)z2 + FiledExtendet);
vertex3 = new Vector3((int)x2 - FiledExtendet, height, (int)z2 + FiledExtendet);
// дальше расширяем границы, так чтобы ячеки размером size на size корректно встали
while (true)
if ((int)vertex3.x % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x - 1, height, (int)vertex3.z);
while (true)
if ((int)vertex3.z % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x, height, (int)vertex3.z + 1);
vertex4 = new Vector3((int)x2 - FiledExtendet, height, (int)z1 - FiledExtendet);
parent = (GameObject)Instantiate(ScannerPrefab, this.transform.position, new Quaternion(0, 0, 0, 0));
MapForTrassing = new GameObject[x = (((int)vertex3.z - (int)vertex4.z) / size), y = ((int)vertex1.x - (int)vertex4.x) / size]; // массив точек путей столбцы - строки
map = new Cell[x, y];
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
map[i, k] = new Cell();
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
{
MapForTrassing[i, k] = (GameObject)Instantiate(ScannerPrefab, new Vector3(vertex4.x + k * size + 0.5f, height, vertex4.z + i * size + 0.5f), new Quaternion(0, 0, 0, 0));
MapForTrassing[i, k].transform.parent = parent.transform;
MapForTrassing[i, k].GetComponent<Scanner>().SetAI(this.gameObject);
}
}
else if (x1 < x2 && z1 > z2)// 4
{
vertex1 = new Vector3((int)x1 - FiledExtendet, height, (int)z1 + FiledExtendet);
vertex2 = new Vector3((int)x1 - FiledExtendet, height, (int)z2 - FiledExtendet);
vertex3 = new Vector3((int)x2 + FiledExtendet, height, (int)z2 - FiledExtendet);
// дальше расширяем границы, так чтобы ячеки размером size на size корректно встали
while (true)
if ((int)vertex3.x % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x + 1, height, (int)vertex3.z);
while (true)
if ((int)vertex3.z % size == 0)
break;
else
vertex3 = new Vector3((int)vertex3.x, height, (int)vertex3.z - 1);
vertex4 = new Vector3((int)x2 + FiledExtendet, height, (int)z1 + FiledExtendet);
parent = (GameObject)Instantiate(ScannerPrefab, this.transform.position, new Quaternion(0, 0, 0, 0));
MapForTrassing = new GameObject[x = (((int)vertex1.z - (int)vertex2.z) / size), y = ((int)vertex3.x - (int)vertex2.x) / size]; // массив точек путей, [столбцы, строки]
map = new Cell[x, y];
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
map[i, k] = new Cell();
for (int i = 0; i < x; i++)
for (int k = 0; k < y; k++)
{
MapForTrassing[i, k] = (GameObject)Instantiate(ScannerPrefab, new Vector3(vertex2.x + k * size + 0.5f, height, vertex2.z + i * size + 0.5f), new Quaternion(0, 0, 0, 0));
MapForTrassing[i, k].transform.parent = parent.transform;
MapForTrassing[i, k].GetComponent<Scanner>().SetAI(this.gameObject);
}
}
delay = Time.deltaTime * 2;
}
public void DestroyMapPath()
{
GameObject.Destroy(parent);
}
#endregion
}
Для работы этого скрипта необходимо в инспекторе заполнить три поля, а именно ScannerPrefab, Size (рекомендую ставить число полученное от округления размера коллайдера моба по х)и FiledExtendet(4-7 - достаточно(СИЛЬНО ВЛИЯЕТ НА ПРОИЗВОДИТЕЛЬНОСТЬ) увеличение необходимо просто для того, чтобы находить путь когда цель находиться прямо по отношению к нам)
Как заполнить целочисленные значения - это понятно =), а ScannerPrefab должен быть заполнен префабом, который можно создать так:
1. Создаем пустой объект назовем его "ScannerPrefab";
2. Надеваем на него BoxCollider;
3. Задаем размеры коллайдеру по x и z ровно такие же как мы указывали в поле Size выше, а y от > 20;
4. BoxCollider переводим в режим триггера;
5. Надеваем Rigidbody, отключаем гравитацию на нем;
6. Ставим скрипт Scanner на него;
7. Ну и наконец заливаем его в префаб и отправляем в поле ScannerPrefab у нашего моба=)
У ScannerPrefab в скрипте Scanner в поле TagsIgnore пишем те теги, которые мы не хотим считать за препятствие, например тег "Terrain"=) Мы же не хотим, чтобы террэин был для нас одним большим препятствием. Самому сканеру тоже дадим тэг, например "Ambience"=) И так же занесем его массив TagsIgnore.
Сам скрипт Scanner (крепим на ScannerPrefab):
Синтаксис:
Используется csharp
using UnityEngine;
using System.Collections;
public class Scanner : MonoBehaviour {
public string[] tagsIgnore; // теги объектов, которые будут игнорироваться при поисик припятствий
public GameObject AI; // объект породивший этот префаб
public bool passable;
public bool on;
void Start()
{
passable = true;
}
void OnTriggerStay(Collider c)
{
if (!getLisitnse(c.tag) && c.gameObject != AI)
passable = false;
}
public void SetAI(GameObject AI)
{
this.AI = AI;
}
public void Activated()
{
on = true;
}
public bool GetON()
{
return on;
}
public bool GetPassable()
{
return passable;
}
void OnTriggerEnter(Collider c)
{
if (c.gameObject == AI && on == true)
{
AI.GetComponent<AI>().NextIndexPath();
}
}
bool getLisitnse(string tag) // Получить разрешение на запрет "passable = true"
{
for (int i = 0; i < tagsIgnore.Length; i++)
if (tagsIgnore[i] == tag)
return true;
return false;
}
}
using System.Collections;
public class Scanner : MonoBehaviour {
public string[] tagsIgnore; // теги объектов, которые будут игнорироваться при поисик припятствий
public GameObject AI; // объект породивший этот префаб
public bool passable;
public bool on;
void Start()
{
passable = true;
}
void OnTriggerStay(Collider c)
{
if (!getLisitnse(c.tag) && c.gameObject != AI)
passable = false;
}
public void SetAI(GameObject AI)
{
this.AI = AI;
}
public void Activated()
{
on = true;
}
public bool GetON()
{
return on;
}
public bool GetPassable()
{
return passable;
}
void OnTriggerEnter(Collider c)
{
if (c.gameObject == AI && on == true)
{
AI.GetComponent<AI>().NextIndexPath();
}
}
bool getLisitnse(string tag) // Получить разрешение на запрет "passable = true"
{
for (int i = 0; i < tagsIgnore.Length; i++)
if (tagsIgnore[i] == tag)
return true;
return false;
}
}
Дальше создадим небольшой мозг для проверки скрипта, банально назовем его "AI". Конечно же этот скрипт часть моба, ему тоже даем Regidbody=)
Синтаксис:
Используется csharp
using UnityEngine;
using System.Collections;
public class AI : MonoBehaviour {
public GameObject aim;
public Transform[] path; // объекты для передвижения
public int indexPath;
void Start () {
// передадим объект для поиска пути
this.GetComponent<PathBuilding>().CtreatePath(aim.transform);
indexPath = -1;
}
void Update()
{
if (this.GetComponent<PathBuilding>().GetError() == true)
{
NextIndexPath();
}
else if (this.GetComponent<PathBuilding>().GetComplete() == true)
{
path = this.GetComponent<PathBuilding>().GetPath();
if (path == null)
NextIndexPath();
else
indexPath = path.Length - 1;
}
else if (indexPath > -1)
{
Movent(2);
Rotation(path[indexPath].gameObject, 2);
}
}
public void NextIndexPath()
{
if (indexPath <= 0)
{
this.GetComponent<PathBuilding>().DestroyMapPath();
GameObject.Destroy(aim);
}
else indexPath--;
}
private void Rotation(GameObject objectRotation, float speedRotation)
{
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, Quaternion.LookRotation(objectRotation.transform.position - this.transform.position), speedRotation * Time.deltaTime);
}
private void Movent(float speed)
{
this.rigidbody.velocity = transform.TransformDirection(new Vector3(0, 0, (float)speed));
}
}
using System.Collections;
public class AI : MonoBehaviour {
public GameObject aim;
public Transform[] path; // объекты для передвижения
public int indexPath;
void Start () {
// передадим объект для поиска пути
this.GetComponent<PathBuilding>().CtreatePath(aim.transform);
indexPath = -1;
}
void Update()
{
if (this.GetComponent<PathBuilding>().GetError() == true)
{
NextIndexPath();
}
else if (this.GetComponent<PathBuilding>().GetComplete() == true)
{
path = this.GetComponent<PathBuilding>().GetPath();
if (path == null)
NextIndexPath();
else
indexPath = path.Length - 1;
}
else if (indexPath > -1)
{
Movent(2);
Rotation(path[indexPath].gameObject, 2);
}
}
public void NextIndexPath()
{
if (indexPath <= 0)
{
this.GetComponent<PathBuilding>().DestroyMapPath();
GameObject.Destroy(aim);
}
else indexPath--;
}
private void Rotation(GameObject objectRotation, float speedRotation)
{
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, Quaternion.LookRotation(objectRotation.transform.position - this.transform.position), speedRotation * Time.deltaTime);
}
private void Movent(float speed)
{
this.rigidbody.velocity = transform.TransformDirection(new Vector3(0, 0, (float)speed));
}
}
Так же у скрипта который использует данный поиск пути должен быть открытый метод public void NextIndexPath(), в первое условие при выполнении можно поставить функцию которая будет перезапускать процесс спавна цели до которой мы будем идти. Так же в Scanner's в методе OnTriggerEnter(Collider c) поставим вместо AI название скрипта откуда запускаем процесс поиска.
Хотел спросить:
1. Как можно определить точки касания префаба ScannerPrefab с Terrain'ом, чтобы выровнять их по высоте? Вообщем аккуратно расположить найденные точки по Terrain'у=)
И еще сильно - не пинайте, это мой первый простинький ИИ