Исключение: ArgumentOutOfRangeException

Общие вопросы о Unity3D

Исключение: ArgumentOutOfRangeException

Сообщение BlAzE 28 окт 2022, 11:33

Доброго времени суток, уважаемые коллеги по цеху. Я начинающий разработчик на Unity. Написал из англоязычного источника скрипт на алгоритм А*. Не могу обратиться к стартовому узлу через GetComponent. Выдаёт ошибку: ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
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;
    }
}
 

Это скрипт инициализации сетки и проверки узлов сетки:
Синтаксис:
Используется 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));

            }
        }
    }
}
 

А это сам 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);
            }
        }
    }
}
 

Исключение вылетает на эту строку:
Синтаксис:
Используется csharp
        Seeker.transform.position = Seeker.GetComponent<Pathfinding2D>().GridOwner.GetComponent<Grid2D>().Path[0].WorldPosition;
 

Уже голову сломал, буду рад ответу. Заранее спасибо!
BlAzE
UNец
 
Сообщения: 4
Зарегистрирован: 28 окт 2022, 11:24

Re: Исключение: ArgumentOutOfRangeException

Сообщение Alkos26Rus 28 окт 2022, 11:44

Path[0]

наверно Path пустой
Аватара пользователя
Alkos26Rus
Адепт
 
Сообщения: 1642
Зарегистрирован: 26 ноя 2020, 17:52
Откуда: Москва

Re: Исключение: ArgumentOutOfRangeException

Сообщение BlAzE 28 окт 2022, 23:01

Alkos26Rus писал(а):
Path[0]

наверно Path пустой

Невероятно полный и насыщенный подробностями ответ... Спасибо, Вы мне очень помогли.
BlAzE
UNец
 
Сообщения: 4
Зарегистрирован: 28 окт 2022, 11:24

Re: Исключение: ArgumentOutOfRangeException

Сообщение Alkos26Rus 29 окт 2022, 11:03

BlAzE писал(а):
Alkos26Rus писал(а):
Path[0]

наверно Path пустой

Невероятно полный и насыщенный подробностями ответ... Спасибо, Вы мне очень помогли.

ты же спросил в каком списке элемент выходит из диапазона, дальше твоя задача определить почему Path пустой

я так понял он заполняется в методе FindPath, а ты запрашиваешь его до того как он заполнился, тебе нужно это делать после метода FindPath
Синтаксис:
Используется csharp
        FindPath(Seeker.transform.position, Target.transform.position);
        Seeker.transform.position = Seeker.GetComponent<Pathfinding2D>().GridOwner.GetComponent<Grid2D>().Path[0].WorldPosition;

 
Аватара пользователя
Alkos26Rus
Адепт
 
Сообщения: 1642
Зарегистрирован: 26 ноя 2020, 17:52
Откуда: Москва

Re: Исключение: ArgumentOutOfRangeException

Сообщение BlAzE 29 окт 2022, 15:18

Я исправил код в методе Start():
Синтаксис:
Используется csharp
Grid = GridOwner.GetComponent<Grid2D>();
        Seeker = Seeker.GetComponent<Transform>();
        Target = Target.GetComponent<Transform>();
        Human = Human.GetComponent<Pathfinding2D>();
        FindPath(Seeker.transform.position, Target.transform.position);
        Seeker.transform.position = Human.GetComponent<Pathfinding2D>().GridOwner.GetComponent<Grid2D>().Path[0].WorldPosition;
        Direction = Vector3.down;
        CharacterAnimator = GetComponent<Animator>();

Но unity опять ругается, не может найти экземпляр класса Pathfinding2D (NullReferenceException: Object reference not set to an instance of an object
Pathfinding2D.Start ())
BlAzE
UNец
 
Сообщения: 4
Зарегистрирован: 28 окт 2022, 11:24

Re: Исключение: ArgumentOutOfRangeException

Сообщение Alkos26Rus 29 окт 2022, 15:55

BlAzE писал(а):Я исправил код в методе Start():
Синтаксис:
Используется csharp
Grid = GridOwner.GetComponent<Grid2D>();
        Seeker = Seeker.GetComponent<Transform>();
        Target = Target.GetComponent<Transform>();
        Human = Human.GetComponent<Pathfinding2D>();
        FindPath(Seeker.transform.position, Target.transform.position);
        Seeker.transform.position = Human.GetComponent<Pathfinding2D>().GridOwner.GetComponent<Grid2D>().Path[0].WorldPosition;
        Direction = Vector3.down;
        CharacterAnimator = GetComponent<Animator>();

Но unity опять ругается, не может найти экземпляр класса Pathfinding2D (NullReferenceException: Object reference not set to an instance of an object
Pathfinding2D.Start ())

Значит на Human нет компонента Pathfinding2D, помойму ты не понимаешь вобще что с этим кодом делать, и непонятно куда ты его повесил, иши туториал как его пользовать правильно
Аватара пользователя
Alkos26Rus
Адепт
 
Сообщения: 1642
Зарегистрирован: 26 ноя 2020, 17:52
Откуда: Москва

Re: Исключение: ArgumentOutOfRangeException

Сообщение BlAzE 29 окт 2022, 16:14

Есть он там, скрипт висит на GameObject.Human. Там уже задаются параметры Seeker и Target. Ладно, спасибо.
BlAzE
UNец
 
Сообщения: 4
Зарегистрирован: 28 окт 2022, 11:24


Вернуться в Общие вопросы

Кто сейчас на конференции

Сейчас этот форум просматривают: Google [Bot] и гости: 18