Parameter name: index
Не могу понять в каком списке элемент выходит за его диапазон. Подскажите пожалуйста.
Это скрипт узла, тут вроде всё понятно:
Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Node2D : MonoBehaviour
{
public int gCost;
public int hCost;
public int fCost { get { return gCost + hCost; } }
public bool obstacle;
public int gridX;
public int gridY;
public Node2D ParentNode;
public Vector3 WorldPosition;
public Node2D(bool obstacle, Vector3 WorldPosition, int gridX, int gridY)
{
this.obstacle = obstacle;
this.WorldPosition = WorldPosition;
this.gridX = gridX;
this.gridY = gridY;
}
//Метод возврата проходимости узла
public void SetObstacle(bool obstacle)
{
this.obstacle = obstacle;
}
}
using System.Collections.Generic;
using UnityEngine;
public class Node2D : MonoBehaviour
{
public int gCost;
public int hCost;
public int fCost { get { return gCost + hCost; } }
public bool obstacle;
public int gridX;
public int gridY;
public Node2D ParentNode;
public Vector3 WorldPosition;
public Node2D(bool obstacle, Vector3 WorldPosition, int gridX, int gridY)
{
this.obstacle = obstacle;
this.WorldPosition = WorldPosition;
this.gridX = gridX;
this.gridY = gridY;
}
//Метод возврата проходимости узла
public void SetObstacle(bool obstacle)
{
this.obstacle = obstacle;
}
}
Это скрипт инициализации сетки и проверки узлов сетки:
Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class Grid2D : MonoBehaviour
{
public Vector3 GridWorldSize;
public float nodeRadius;
public Node2D[,] Grid;
public Tilemap ObstacleMap;
public List<Node2D> Path;
public Vector3 WorldBottomLeft;
public float nodeDiameter;
public int gridSizeX;
public int gridSizeY;
//Метод задания приведённых размеров сетки
private void Awake()
{
nodeDiameter = nodeRadius * 2;
gridSizeX = Mathf.RoundToInt(GridWorldSize.x / nodeDiameter);
gridSizeY = Mathf.RoundToInt(GridWorldSize.y / nodeDiameter);
CreateGrid();
}
//Метод, который создаёт зону видимости узлов и саму сетку, а также определяет проходимые узлы и препятствия
public void CreateGrid()
{
Grid = new Node2D[gridSizeX, gridSizeY];
WorldBottomLeft = transform.position + Vector3.left * GridWorldSize.x / 2 + Vector3.down * GridWorldSize.y / 2;
for (int gridX = 0; gridX < gridSizeX; gridX++)
{
for (int gridY = 0; gridY < gridSizeY; gridY++)
{
Vector3 WorldPoint = WorldBottomLeft + Vector3.right * (gridX * nodeDiameter + nodeRadius) + Vector3.up * (gridY * nodeDiameter + nodeRadius);
Grid[gridX, gridY] = new Node2D(false, WorldPoint, gridX, gridY);
if (ObstacleMap.HasTile(ObstacleMap.WorldToCell(Grid[gridX, gridY].WorldPosition)))
Grid[gridX, gridY].SetObstacle(true);
else
Grid[gridX, gridY].SetObstacle(false);
}
}
}
//Метод, который добавляет все узлы в зоне видимости владельца сетки (объекта, к которому привязана сетка) в динамический массив (List<Node2D>)
public List<Node2D> GetNeighbours(Node2D node)
{
List<Node2D> Neighbours = new List<Node2D>();
if (node.gridX >= 0 && node.gridX < gridSizeX && node.gridY + 1 >= 0 && node.gridY + 1 < gridSizeY)
{
Neighbours.Add(Grid[node.gridX, node.gridY]);
}
else if (node.gridX >= 0 && node.gridX < gridSizeX && node.gridY - 1 >= 0 && node.gridY - 1 < gridSizeY)
{
Neighbours.Add(Grid[node.gridX, node.gridY]);
}
else if (node.gridX + 1 >= 0 && node.gridX + 1 < gridSizeX && node.gridY >= 0 && node.gridY < gridSizeY)
{
Neighbours.Add(Grid[node.gridX, node.gridY]);
}
else if (node.gridX - 1 >= 0 && node.gridX - 1 < gridSizeX && node.gridY >= 0 && node.gridY < gridSizeY)
{
Neighbours.Add(Grid[node.gridX, node.gridY]);
}
return Neighbours;
}
//Метод, который переводит мировые координаты узла в относительные
public Node2D NodeFromWorldPoint(Vector3 WorldPosition)
{
int gridX = Mathf.RoundToInt(WorldPosition.x);
int gridY = Mathf.RoundToInt(WorldPosition.y);
return Grid[gridX, gridY];
}
//Метод визуализации свойств узлов
void OnDrawGizmos()
{
Gizmos.DrawWireCube(transform.position, new Vector3(GridWorldSize.x, GridWorldSize.y, 1));
if (Grid != null)
{
foreach (Node2D n in Grid)
{
if (n.obstacle)
Gizmos.color = Color.red;
else
Gizmos.color = Color.white;
if (Path != null && Path.Contains(n))
Gizmos.color = Color.black;
else Gizmos.DrawCube(n.WorldPosition, Vector3.one * (nodeRadius));
}
}
}
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class Grid2D : MonoBehaviour
{
public Vector3 GridWorldSize;
public float nodeRadius;
public Node2D[,] Grid;
public Tilemap ObstacleMap;
public List<Node2D> Path;
public Vector3 WorldBottomLeft;
public float nodeDiameter;
public int gridSizeX;
public int gridSizeY;
//Метод задания приведённых размеров сетки
private void Awake()
{
nodeDiameter = nodeRadius * 2;
gridSizeX = Mathf.RoundToInt(GridWorldSize.x / nodeDiameter);
gridSizeY = Mathf.RoundToInt(GridWorldSize.y / nodeDiameter);
CreateGrid();
}
//Метод, который создаёт зону видимости узлов и саму сетку, а также определяет проходимые узлы и препятствия
public void CreateGrid()
{
Grid = new Node2D[gridSizeX, gridSizeY];
WorldBottomLeft = transform.position + Vector3.left * GridWorldSize.x / 2 + Vector3.down * GridWorldSize.y / 2;
for (int gridX = 0; gridX < gridSizeX; gridX++)
{
for (int gridY = 0; gridY < gridSizeY; gridY++)
{
Vector3 WorldPoint = WorldBottomLeft + Vector3.right * (gridX * nodeDiameter + nodeRadius) + Vector3.up * (gridY * nodeDiameter + nodeRadius);
Grid[gridX, gridY] = new Node2D(false, WorldPoint, gridX, gridY);
if (ObstacleMap.HasTile(ObstacleMap.WorldToCell(Grid[gridX, gridY].WorldPosition)))
Grid[gridX, gridY].SetObstacle(true);
else
Grid[gridX, gridY].SetObstacle(false);
}
}
}
//Метод, который добавляет все узлы в зоне видимости владельца сетки (объекта, к которому привязана сетка) в динамический массив (List<Node2D>)
public List<Node2D> GetNeighbours(Node2D node)
{
List<Node2D> Neighbours = new List<Node2D>();
if (node.gridX >= 0 && node.gridX < gridSizeX && node.gridY + 1 >= 0 && node.gridY + 1 < gridSizeY)
{
Neighbours.Add(Grid[node.gridX, node.gridY]);
}
else if (node.gridX >= 0 && node.gridX < gridSizeX && node.gridY - 1 >= 0 && node.gridY - 1 < gridSizeY)
{
Neighbours.Add(Grid[node.gridX, node.gridY]);
}
else if (node.gridX + 1 >= 0 && node.gridX + 1 < gridSizeX && node.gridY >= 0 && node.gridY < gridSizeY)
{
Neighbours.Add(Grid[node.gridX, node.gridY]);
}
else if (node.gridX - 1 >= 0 && node.gridX - 1 < gridSizeX && node.gridY >= 0 && node.gridY < gridSizeY)
{
Neighbours.Add(Grid[node.gridX, node.gridY]);
}
return Neighbours;
}
//Метод, который переводит мировые координаты узла в относительные
public Node2D NodeFromWorldPoint(Vector3 WorldPosition)
{
int gridX = Mathf.RoundToInt(WorldPosition.x);
int gridY = Mathf.RoundToInt(WorldPosition.y);
return Grid[gridX, gridY];
}
//Метод визуализации свойств узлов
void OnDrawGizmos()
{
Gizmos.DrawWireCube(transform.position, new Vector3(GridWorldSize.x, GridWorldSize.y, 1));
if (Grid != null)
{
foreach (Node2D n in Grid)
{
if (n.obstacle)
Gizmos.color = Color.red;
else
Gizmos.color = Color.white;
if (Path != null && Path.Contains(n))
Gizmos.color = Color.black;
else Gizmos.DrawCube(n.WorldPosition, Vector3.one * (nodeRadius));
}
}
}
}
А это сам Pathfinding скрипт поиска пути:
Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Bindings;
public class Pathfinding2D : MonoBehaviour
{
public float speed = 3.0f;
public bool isMoving = false;
public Transform Seeker;
public Transform Target;
public Vector3 SeekerPosition;
public Vector3 TargetPosition;
public Vector3 Direction;
public Grid2D Grid;
public Node2D SeekerNode;
public Node2D TargetNode;
public GameObject GridOwner;
public Animator CharacterAnimator;
void Start()
{
Debug.Log("Target.transform.positin = " + Target.transform.position);
Debug.Log("Seeker.transform.positin = " + Seeker.transform.position);
SeekerPosition = transform.position;
SeekerPosition.z = 0;
Direction = Vector3.down;
CharacterAnimator = GetComponent<Animator>();
Seeker.transform.position = Seeker.GetComponent<Pathfinding2D>().GridOwner.GetComponent<Grid2D>().Path[0].WorldPosition;
Debug.Log("Seeker.transform.positin = " + Seeker.transform.position);
Debug.Log(Seeker.GetComponent<Pathfinding2D>().GridOwner.GetComponent<Grid2D>().Path[0].WorldPosition);
Debug.Log("Target.transform.positin = " + Target.transform.position);
SeekerPosition = Seeker.transform.position;
TargetPosition = Target.transform.position;
FindPath(Seeker.transform.position, Target.transform.position);
}
void Update()
{
SetAnimation(Direction);
if (Input.GetMouseButtonUp(0))
{
Debug.Log("Seeker.transform.position = " + Seeker.transform.position);
Debug.Log("Target.transform.position = " + Target.transform.position);
}
}
//Метод реализации пути передвижения (здесь поподробнее)
public void FindPath(Vector3 StartPosition, Vector3 TargetPosition)//Метод принимает стартовую и целевую позиции
{
//Переводим позиции с узлы
SeekerNode = Grid.NodeFromWorldPoint(StartPosition);
TargetNode = Grid.NodeFromWorldPoint(TargetPosition);
//Создаём 2 списка. Открытый, в котором проверяется стоимость узла, и закрытый, в который добавляем самые дешёвые узлы
List<Node2D> openSet = new List<Node2D>();
HashSet<Node2D> closedSet = new HashSet<Node2D>();
openSet.Add(SeekerNode);//Начинаем с добавления в открытый список изначального узла, так как отсчёт будет вестись от него
//Пока в открытом списке имеются узлы для проверки, задаём первый узел с индексом 0. Затем в цикле проходим по всем узлам открытого списка. При проверке их стоимости (в условиях)
//присваиваем первому узлу более дешёвый с индексом i
while (openSet.Count > 0)
{
Node2D Node = openSet[0];
for (int i = 0; i < openSet.Count; i++)
{
if (openSet[i].fCost <= Node.fCost)
{
if (openSet[i].hCost < Node.hCost)
Node = openSet[i];
}
}
//Затем удаляем проверенный узел из открытого списка и добавляем во закрытое множество
openSet.Remove(Node);
closedSet.Add(Node);
//Если проверяемый узел совпадает с целевым узлом, возвращаемся по проложенным до цели узлам
if (Node == TargetNode)
{
RetracePath(SeekerNode, TargetNode);
return;
}
//Переносим из сетки все доступные для обработки узлы и проверяем каждый на:
foreach (Node2D Neighbour in Grid.GetNeighbours(Node))
{
//проходимость и нахождение узла в закрытом множестве
if (Neighbour.obstacle || closedSet.Contains(Neighbour))
{
continue;
}
int newCostToNeighbour = Node.gCost + GetDistance(Node, Neighbour);
if (newCostToNeighbour < Neighbour.gCost || !openSet.Contains(Neighbour))
{
if (newCostToNeighbour < Neighbour.gCost || !openSet.Contains(Neighbour))
{
Neighbour.gCost = newCostToNeighbour;
Neighbour.hCost = GetDistance(Neighbour, TargetNode);
Neighbour.ParentNode = Node;
if (!openSet.Contains(Neighbour))
openSet.Add(Neighbour);
}
}
}
}
}
//Метод обратного пути от конечного узла до стартового
private void RetracePath(Node2D StartNode, Node2D TargetNode)
{
List<Node2D> Path = new List<Node2D>();
Node2D CurrentNode = TargetNode;
while (CurrentNode != StartNode)
{
Path.Add(CurrentNode);
CurrentNode = CurrentNode.ParentNode;
}
Path.Reverse();
Grid.Path = Path;
}
//Метод, определящий дистанцию в узлах от стартового до конечного узлов
private int GetDistance(Node2D NodeA, Node2D NodeB)
{
int distX = Mathf.Abs(NodeA.gridX - NodeB.gridX);
int distY = Mathf.Abs(NodeA.gridY - NodeB.gridY);
if (distX > distY) return 14 * distY + 10 * (distX - distY);
return 14 * distX + 10 * (distY - distX);
}
private void SetAnimation(Vector3 Direction)
{
this.Direction = Direction;
if (isMoving == true)
{
CharacterAnimator.SetBool("isMoving", true);
if (Direction == Vector3.down)
{
CharacterAnimator.SetFloat("xPos", 0);
CharacterAnimator.SetFloat("yPos", -1);
}
else if (Direction == Vector3.up)
{
CharacterAnimator.SetFloat("xPos", 0);
CharacterAnimator.SetFloat("yPos", 1);
}
else if (Direction == Vector3.left)
{
CharacterAnimator.SetFloat("xPos", -1);
CharacterAnimator.SetFloat("yPos", 0);
}
else if (Direction == Vector3.right)
{
CharacterAnimator.SetFloat("xPos", 1);
CharacterAnimator.SetFloat("yPos", 0);
}
}
else
{
CharacterAnimator.SetBool("isMoving", false);
if (Direction == Vector3.down)
{
CharacterAnimator.SetFloat("xPos", 0);
CharacterAnimator.SetFloat("yPos", -1);
}
else if (Direction == Vector3.up)
{
CharacterAnimator.SetFloat("xPos", 0);
CharacterAnimator.SetFloat("yPos", 1);
}
else if (Direction == Vector3.left)
{
CharacterAnimator.SetFloat("xPos", -1);
CharacterAnimator.SetFloat("yPos", 0);
}
else if (Direction == Vector3.right)
{
CharacterAnimator.SetFloat("xPos", 1);
CharacterAnimator.SetFloat("yPos", 0);
}
}
}
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Bindings;
public class Pathfinding2D : MonoBehaviour
{
public float speed = 3.0f;
public bool isMoving = false;
public Transform Seeker;
public Transform Target;
public Vector3 SeekerPosition;
public Vector3 TargetPosition;
public Vector3 Direction;
public Grid2D Grid;
public Node2D SeekerNode;
public Node2D TargetNode;
public GameObject GridOwner;
public Animator CharacterAnimator;
void Start()
{
Debug.Log("Target.transform.positin = " + Target.transform.position);
Debug.Log("Seeker.transform.positin = " + Seeker.transform.position);
SeekerPosition = transform.position;
SeekerPosition.z = 0;
Direction = Vector3.down;
CharacterAnimator = GetComponent<Animator>();
Seeker.transform.position = Seeker.GetComponent<Pathfinding2D>().GridOwner.GetComponent<Grid2D>().Path[0].WorldPosition;
Debug.Log("Seeker.transform.positin = " + Seeker.transform.position);
Debug.Log(Seeker.GetComponent<Pathfinding2D>().GridOwner.GetComponent<Grid2D>().Path[0].WorldPosition);
Debug.Log("Target.transform.positin = " + Target.transform.position);
SeekerPosition = Seeker.transform.position;
TargetPosition = Target.transform.position;
FindPath(Seeker.transform.position, Target.transform.position);
}
void Update()
{
SetAnimation(Direction);
if (Input.GetMouseButtonUp(0))
{
Debug.Log("Seeker.transform.position = " + Seeker.transform.position);
Debug.Log("Target.transform.position = " + Target.transform.position);
}
}
//Метод реализации пути передвижения (здесь поподробнее)
public void FindPath(Vector3 StartPosition, Vector3 TargetPosition)//Метод принимает стартовую и целевую позиции
{
//Переводим позиции с узлы
SeekerNode = Grid.NodeFromWorldPoint(StartPosition);
TargetNode = Grid.NodeFromWorldPoint(TargetPosition);
//Создаём 2 списка. Открытый, в котором проверяется стоимость узла, и закрытый, в который добавляем самые дешёвые узлы
List<Node2D> openSet = new List<Node2D>();
HashSet<Node2D> closedSet = new HashSet<Node2D>();
openSet.Add(SeekerNode);//Начинаем с добавления в открытый список изначального узла, так как отсчёт будет вестись от него
//Пока в открытом списке имеются узлы для проверки, задаём первый узел с индексом 0. Затем в цикле проходим по всем узлам открытого списка. При проверке их стоимости (в условиях)
//присваиваем первому узлу более дешёвый с индексом i
while (openSet.Count > 0)
{
Node2D Node = openSet[0];
for (int i = 0; i < openSet.Count; i++)
{
if (openSet[i].fCost <= Node.fCost)
{
if (openSet[i].hCost < Node.hCost)
Node = openSet[i];
}
}
//Затем удаляем проверенный узел из открытого списка и добавляем во закрытое множество
openSet.Remove(Node);
closedSet.Add(Node);
//Если проверяемый узел совпадает с целевым узлом, возвращаемся по проложенным до цели узлам
if (Node == TargetNode)
{
RetracePath(SeekerNode, TargetNode);
return;
}
//Переносим из сетки все доступные для обработки узлы и проверяем каждый на:
foreach (Node2D Neighbour in Grid.GetNeighbours(Node))
{
//проходимость и нахождение узла в закрытом множестве
if (Neighbour.obstacle || closedSet.Contains(Neighbour))
{
continue;
}
int newCostToNeighbour = Node.gCost + GetDistance(Node, Neighbour);
if (newCostToNeighbour < Neighbour.gCost || !openSet.Contains(Neighbour))
{
if (newCostToNeighbour < Neighbour.gCost || !openSet.Contains(Neighbour))
{
Neighbour.gCost = newCostToNeighbour;
Neighbour.hCost = GetDistance(Neighbour, TargetNode);
Neighbour.ParentNode = Node;
if (!openSet.Contains(Neighbour))
openSet.Add(Neighbour);
}
}
}
}
}
//Метод обратного пути от конечного узла до стартового
private void RetracePath(Node2D StartNode, Node2D TargetNode)
{
List<Node2D> Path = new List<Node2D>();
Node2D CurrentNode = TargetNode;
while (CurrentNode != StartNode)
{
Path.Add(CurrentNode);
CurrentNode = CurrentNode.ParentNode;
}
Path.Reverse();
Grid.Path = Path;
}
//Метод, определящий дистанцию в узлах от стартового до конечного узлов
private int GetDistance(Node2D NodeA, Node2D NodeB)
{
int distX = Mathf.Abs(NodeA.gridX - NodeB.gridX);
int distY = Mathf.Abs(NodeA.gridY - NodeB.gridY);
if (distX > distY) return 14 * distY + 10 * (distX - distY);
return 14 * distX + 10 * (distY - distX);
}
private void SetAnimation(Vector3 Direction)
{
this.Direction = Direction;
if (isMoving == true)
{
CharacterAnimator.SetBool("isMoving", true);
if (Direction == Vector3.down)
{
CharacterAnimator.SetFloat("xPos", 0);
CharacterAnimator.SetFloat("yPos", -1);
}
else if (Direction == Vector3.up)
{
CharacterAnimator.SetFloat("xPos", 0);
CharacterAnimator.SetFloat("yPos", 1);
}
else if (Direction == Vector3.left)
{
CharacterAnimator.SetFloat("xPos", -1);
CharacterAnimator.SetFloat("yPos", 0);
}
else if (Direction == Vector3.right)
{
CharacterAnimator.SetFloat("xPos", 1);
CharacterAnimator.SetFloat("yPos", 0);
}
}
else
{
CharacterAnimator.SetBool("isMoving", false);
if (Direction == Vector3.down)
{
CharacterAnimator.SetFloat("xPos", 0);
CharacterAnimator.SetFloat("yPos", -1);
}
else if (Direction == Vector3.up)
{
CharacterAnimator.SetFloat("xPos", 0);
CharacterAnimator.SetFloat("yPos", 1);
}
else if (Direction == Vector3.left)
{
CharacterAnimator.SetFloat("xPos", -1);
CharacterAnimator.SetFloat("yPos", 0);
}
else if (Direction == Vector3.right)
{
CharacterAnimator.SetFloat("xPos", 1);
CharacterAnimator.SetFloat("yPos", 0);
}
}
}
}
Исключение вылетает на эту строку:
Синтаксис:
Используется csharp
Seeker.transform.position = Seeker.GetComponent<Pathfinding2D>().GridOwner.GetComponent<Grid2D>().Path[0].WorldPosition;
Уже голову сломал, буду рад ответу. Заранее спасибо!