【Unity】A*寻路算法


一、前言

本文为a*寻路算法的简单实现,在此记录。
本文对应项目:GitHub
说明:
1、Unity版本为-2019.4.27
2、文章中的Tile可替换为Point

参考文章:A*算法的C#实现

二、完整代码

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

public class AStar
{
    private AStar() { }
    public static AStar Instance { get; } = new AStar();

    #region 结构 
    // 二维坐标点  
    public struct Point
    {
        public int X, Y;
        public Point(int _x, int _y)
        {
            X = _x;
            Y = _y;
        }
    }
 
    // A*的每个节点 
    public class ANode
    {
        public Point point;
        public ANode parent;
        public int fn, gn, hn;
    }
    #endregion

    #region 字段
    private int[,] Map = null;
    private Dictionary<Point, ANode> OpenList = null;
    private HashSet<Point> ClosedList = null;
    private Point EndPoint;
    private int ReachableVal;
    #endregion

    #region 方法
    // 执行算法
    public List<Tile> Execute(int[,] map, int startX, int startY, int endX, int endY, int reachableVal = 0, bool allowDiagonal = false)
    {
        InitData(map, endX, endY, reachableVal);
        AddNodeToOpen(startX, startY);
        List<Tile>path = FindPath(allowDiagonal);

        return path;
    }

    //初始化所需数据
    private void InitData(int[,] map, int endX, int endY, int reachableVal)
    {
        this.OpenList = new Dictionary<Point, ANode>();
        this.ClosedList = new HashSet<Point>();
        this.Map = map;
        this.EndPoint = new Point(endX, endY);
        this.ReachableVal = reachableVal;
    }

    //查找由起点到终点的路径
    private List<Tile> FindPath(bool allowDiagonal)
    {
        while (OpenList.Count > 0)
        {
            //从open列表中找到f(n)最小的结点
            ANode minFn = FindMinFn(OpenList);
            Point point = minFn.point;
            //判断是否到达终点
            if (point.X == EndPoint.X && point.Y == EndPoint.Y)
            {
                return DisplayPath(minFn);
            }
            else
            {
                UpdateCloseAndOpen(minFn, point, allowDiagonal);
            }
        }

        Debug.Log("没找到路径");
        return null;
    }
   
    // 从open列表中获取最小f(n)的节点  
    private ANode FindMinFn(Dictionary<Point, ANode> aNodes)
    {
        ANode minANode = null;
        foreach (var e in aNodes)
        {
            if (minANode == null || e.Value.fn < minANode.fn)
            {
                minANode = e.Value;
            }
        }
        return minANode;
    }

    // 输出相对最短路径
    private List<Tile> DisplayPath(ANode aNode)
    {      
        List<Tile> tiles = new List<Tile>();

        while (aNode != null)
        {          
            Tile tile = new Tile(aNode.point.X, aNode.point.Y);         
            tiles.Insert(0, tile);
            aNode = aNode.parent;
        }

        return tiles;
    }

    //更新列表
    private void UpdateCloseAndOpen(ANode nowNode, Point nowpoint,bool allowDiagonal)
    {
        //去除nowNode,加入到closed列表中
        OpenList.Remove(nowNode.point);
        ClosedList.Add(nowNode.point);

        //将nowNode周围的节点加入到open列表中
        AddPointToOpen(new Point(nowpoint.X - 1, nowpoint.Y), nowNode); //左
        AddPointToOpen(new Point(nowpoint.X + 1, nowpoint.Y), nowNode); //右
        AddPointToOpen(new Point(nowpoint.X, nowpoint.Y - 1), nowNode); //上
        AddPointToOpen(new Point(nowpoint.X, nowpoint.Y + 1), nowNode); //下
        if (allowDiagonal)
        {
            AddPointToOpen(new Point(nowpoint.X - 1, nowpoint.Y - 1), nowNode); //左上
            AddPointToOpen(new Point(nowpoint.X + 1, nowpoint.Y - 1), nowNode); //右上
            AddPointToOpen(new Point(nowpoint.X - 1, nowpoint.Y + 1), nowNode); //左下
            AddPointToOpen(new Point(nowpoint.X + 1, nowpoint.Y + 1), nowNode); //右下
        }
    }

    // 判断节点是否可达,可达则将节点加入到open列表中 
    private void AddPointToOpen(Point point, ANode parent)
    {
        if (point.X > (Map.GetLength(0) - 1) || point.X < 0)//X越界
        {
            return;
        }

        if (point.Y > (Map.GetLength(1) - 1) || point.Y < 0)//Y越界
        {
            return;
        }

        if (IsReachable(point) && !ClosedList.Contains(point))//该点可到达,且没有被使用过
        {
            AddNodeToOpen(point.X, point.Y, parent);
        }
    }
  
    // 判定该点是否可达
    private bool IsReachable(Point point)
    {
        return Map[point.X, point.Y] == this.ReachableVal;
    }

    //初始化节点并判断是否加入到open列表中
    private void AddNodeToOpen(int x, int y, ANode parent = null)
    {
        ANode aNode = new ANode();
        aNode.point = new Point(x, y);
        aNode.parent = parent;
        aNode.gn = (parent == null) ? 0 : (parent.gn + 1);
        aNode.hn = ManHattan(aNode.point, EndPoint);
        aNode.fn = aNode.gn + aNode.hn;

        if (OpenList.ContainsKey(aNode.point))//判断 是否已包含
        {
            if (aNode.fn < OpenList[aNode.point].fn)//更新为fn更小的节点
            {
                OpenList[aNode.point] = aNode;
            }
        }
        else//新增节点
        {
            OpenList.Add(aNode.point, aNode);
        }
    }
  
    // 计算两个点之间的曼哈顿距离
    private int ManHattan(Point a, Point b)
    {
        return Mathf.Abs(a.X - b.X) + Mathf.Abs(a.Y - b.Y);
    } 
    #endregion
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值