Показать сообщение отдельно
Старый 12.11.2014, 22:36   #652
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,568
Написано 3,006 полезных сообщений
(для 5,323 пользователей)
Ответ: [TrueHorror] - разработка

Сообщение от mr.DIMAS Посмотреть сообщение
Код в студию. Именно код, где на вход дается граф, начало и конец а на выходе массив точек, представляющий путь из начала в конец.
Вообще писал так, чтобы можно было и другие алгоритмы поиска использовать, поэтому есть отдельно Waypoint (на карте) и AStarNode - содержит стоимость пути, эвристику и приоритет точки.

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

// класс вейпоинта
public class Waypoint
{
        public 
Vector3 position;
        public List<
WayPointLink> nearPoints = new List<WayPointLink>(); // связи
        
public int index; // индекс вейпоинта в массиве вейпоинтов
}

// связь с заранее посчитанным расстоянием до соседней точки
public class WayPointLink
{
        public 
Waypoint waypoint;
        public 
float distance;
}


public static class 
AStar
{
    
// разные эвристики

    
public static float DistanceHeuristic(Waypoint wp1, Waypoint wp2)
    {
        return (
wp1.position - wp2.position).magnitude;
    }

    public static 
float Distance2DHeuristic(Waypoint wp1, Waypoint wp2)
    {
        var 
vec = wp1.position - wp2.position;
        var 
vec2 = new Vector2(vec.x, vec.z);
        return 
vec2.magnitude;
    }

    public static 
float SqrDistanceHeuristic(Waypoint wp1, Waypoint wp2)
    {
        return (
wp1.position - wp2.position).sqrMagnitude;
    }

    public static 
float SqrDistance2DHeuristic(Waypoint wp1, Waypoint wp2)
    {
        var 
vec = wp1.position - wp2.position;
        var 
vec2 = new Vector2(vec.x, vec.z);
        return 
vec2.sqrMagnitude;
    }

    public static 
float SumHeuristic(Waypoint wp1, Waypoint wp2)
    {
        var 
vec = wp1.position - wp2.position;
        return 
Mathf.Abs(vec.x) + Mathf.Abs(vec.y) + Mathf.Abs(vec.z);
    }

    public static 
float Sum2DHeuristic(Waypoint wp1, Waypoint wp2)
    {
        var 
vec = wp1.position - wp2.position;
        return 
Mathf.Abs(vec.x) + Mathf.Abs(vec.z);
    }


    
// собственно функция поиска, получает весь массив точек, начальную и конечную точки
    // возвращает путь
    
public static List<Waypoint> FindRoute(List<Waypoint> waypoints, Waypoint start, Waypoint end,
        
Func<Waypoint, Waypoint, float> heuristic = null)
    {
        if(
heuristic == null)
        {
            
heuristic = DistanceHeuristic;
        }

        
// открытые поинты, сортированные по приоритету (с учетом пути и эвристики)
        
var opendSet = new List<AStarNode>();
        
// дополнительная коллекция для быстрого поиска 
        
var opendSetById = new Dictionary<int, AStarNode>();

        
// уже просмотренные точки
        
var closedSet = new Dictionary<int, AStarNode>();

        
// стартовая точка
        
var startNode = new AStarNode(start);
        
startNode.g = 0;
        
startNode.h = heuristic(startNode.waypoint, end);
        
startNode.f = startNode.g + startNode.h;

        
// добавляем ее в список точек для просмотра 
        
opendSet.Add(startNode);
        
opendSetById.Add(startNode.waypoint.index, startNode);

        
// пока есть точки в открытом списке выполняем поиск
        
while (opendSet.Count > 0)
        {
            
// достаем последнюю точку (ближайшую до целевой)
            
var x = opendSet[opendSet.Count - 1];

            
// если это конечная точка, то строим маршрут
            
if (x.waypoint == end)
            {
                return 
ConstructRoute(x);
            }

            
// переносим точку в список просмотренных
            
opendSet.RemoveAt(opendSet.Count - 1);
            
opendSetById.Remove(x.waypoint.index);
            
closedSet.Add(x.waypoint.index, x);

            
// просматриваем все точки, которые рядом с точкой, которую мы обрабатываем 
            
foreach (var yWp in x.waypoint.nearPoints)
            {
                
// если точка уже просмотрена, то пропускаем
                
if (closedSet.ContainsKey(yWp.waypoint.index)) continue;

                
// 
                
var wp = waypoints[yWp.waypoint.index];
                
AStarNode y;

                
//расчетная стоимость пути до этой точки из обрабатываемой
                
var tentativeGScore = x.g + yWp.distance;
                
                
// необходимость изменить родителя просматриваемой соседней точки и пересчитать параметры, 
                // при условии что стоимость пути до нее меньше или при первом добавлении
                
var tentativeIsBetter = false;

                
// если точна не существует в открытом списке, создаем точку и добавляем в список открытых
                
if (!opendSetById.ContainsKey(wp.index))
                {
                    
y = new AStarNode(wp);
                    
добавляем точку в сортированный список по дистанции
                    InsertSorted
(opendSet, y);
                    
opendSetById.Add(y.waypoint.index, y);
                    
tentativeIsBetter = true;
                }
                else 
                {
                    
// точка уже добавлена в открытый список
                    
y = opendSetById[wp.index];
                    
// если стоимость меньше, то меняем родительскую точку и пересчитываем параметры
                    
if (tentativeGScore < y.g) 
                    {
                        
tentativeIsBetter = true;
                    }
                }

                if (!
tentativeIsBetter) continue;


                
// обновление точки
                
y.from = x;
                
y.g = tentativeGScore; // стоимость пути
                
y.h = heuristic(y.waypoint, end); // эвристика данной точки
                
y.f = y.g + y.h; // приоритет 

                // сортировка массива по приоритету
                
UpdateSorted(opendSet, y);
            }

        }
        return 
null;
    }

    
// функция перемещает точку по массиву используя ее приоритет (не сортирует остальные точки)
    // писал давно, не помню точно как работает, но наиболее приортитетные точки в конце списка,
    // из конца дешевле убирать точки, не придется сдвигать весь массив
    
private static void UpdateSorted(List<AStarNode> opendSet, AStarNode aStarNode)
    {
        var 
index = opendSet.IndexOf(aStarNode);
        if (
index >= 0)
        {
            var 
isDown = false;
            for (
int i = index + 1; i < opendSet.Count; ++i)
            {
                if (
opendSet[i].f >= opendSet[i - 1].f)
                {
                    var 
temp = opendSet[i];
                    
opendSet[i] = opendSet[i - 1];
                    
opendSet[i - 1] = temp;
                    
isDown = true;
                }
                else
                {
                    break;
                }
            }

            if (!
isDown)
            {
                for (
int i = index - 1; i > -1; --i)
                {
                    if (
opendSet[i + 1].f < opendSet[i].f)
                    {
                        break;
                    }

                    var 
temp = opendSet[i];
                    
opendSet[i] = opendSet[i + 1];
                    
opendSet[i + 1] = temp;
                }
            }
        }
        else
        {
            
// не существует элемента в списке
        
}
    }

    
// добавляет точку в массив открытых точек в позицию с учетом приоритета
    
private static void InsertSorted(List<AStarNode> opendSet, AStarNode aStarNode)
    {
        for (
int i = 0; i < opendSet.Count; ++i)
        {
            if (!(
aStarNode.f < opendSet[i].f)) continue;

            
opendSet.Insert(i, aStarNode);
            return;
        }

        
opendSet.Add(aStarNode);
    }

    
// функция строит путь
    
private static List<Waypoint> ConstructRoute(AStarNode end)
    {
        var 
route = new List<Waypoint>();

        var 
wp = end;
        
route.Add(wp.waypoint);
        while (
wp.from != null && wp != wp.from)
        {
            
wp = wp.from;
            
route.Add(wp.waypoint);
        }
        if (
route.Count>0)
            
route.RemoveAt(route.Count-1);
        
route.Reverse();

        return 
route;
    }

    public class 
AStarNode
    
{
        public 
readonly Waypoint waypoint; // связь с точкой на карте
        
public float g; // стоимость
        
public float h; // эвристика
        
public float f;  // приоритет

        
public AStarNode from; // откуда в эту точку посчитана стоимость

        
public AStarNode(Waypoint wp)
        {
            
waypoint = wp;
        }
    }
} 
__________________
Blitz3d to Unity Wiki
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
mr.DIMAS (12.11.2014)