unity利用A*算法进行2D寻路

找了份新工作之后,忙的要死,都没时间鼓捣博客了,深深的感受到资本家的剥削,端午节连粽子都没有,每天下班累得跟条咸鱼一样(可能就是)。

刚好最近忙里偷闲,就来写写unity在2D下的AStar寻路算法。

地图用untiy的tilemap来贴。

大概的效果,没有去找好看的图片,将就弄点颜色表示:

黑色表示障碍,绿色表示路径,开头和结尾也是用的绿色,好懒o(╥﹏╥)o

原理和详细解释,还是参考的这位国外的大神:

https://www.redblobgames.com/pathfinding/a-star/introduction.html

解说如下:

A*算法其实可以理解为是贪心算法和广度优先搜索算法的结合体。

广度优先搜索算法,每次都可以找到最短的路径,每走一步都会记下起点到当前点的步数,优点是绝对能找到最短的路径,缺点就是地图越大计算量会变得很巨大。

贪心算法,每次都是走当前点距离终点最近的格子,在没有障碍的情况下效率很高,但是如果有障碍的话,就很绕路。

A*算法结合两者,计算当前走过的步数 与 当前点到终点的距离 之和作为走格子的依据,优点就是当有障碍物时,能找到最短距离并且计算量没有广度优先搜索大,没有障碍物时,效率和贪心算法一样高。

其实代码量没多少,直接贴出来了,具体也不解释,看注释吧,我好懒。

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

public class MapBehaviour : MonoBehaviour
{
    public Vector2Int mapSize;//地图尺寸

    public Tilemap tilemap;
    public Tile normalTile;//白色tile
    public Tile obstacleTile;//黑色tile
    public Tile pathTile;//绿色tile

    public int obstacleCount;//要生成的障碍物数量

    public Vector3Int startPos;//起点
    public Vector3Int endPos;//终点

    private bool hasStartPosSet;//是否设置了起点
    private bool hasEndPosSet;//是否设置了终点

    private Dictionary<Vector3Int, int> search = new Dictionary<Vector3Int, int>();//要进行的查找任务
    private Dictionary<Vector3Int, int> cost = new Dictionary<Vector3Int, int>();//起点到当前点的消耗
    private Dictionary<Vector3Int, Vector3Int> pathSave = new Dictionary<Vector3Int, Vector3Int>();//保存回溯路径
    private List<Vector3Int> hadSearch = new List<Vector3Int>();//已经查找过的坐标

    private List<Vector3Int> obstacle = new List<Vector3Int>();//障碍物坐标

    private void Start()
    {
        CreateNormalTiles();
        CreateObstacleTiles();
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            if (!hasStartPosSet)//第一次点击设置起点
            {
                startPos = tilemap.WorldToCell(Camera.main.ScreenToWorldPoint(Input.mousePosition));
                tilemap.SetTile(startPos, pathTile);
                hasStartPosSet = true;
            }
            else if (!hasEndPosSet)//第二次点击设置终点
            {
                endPos = tilemap.WorldToCell(Camera.main.ScreenToWorldPoint(Input.mousePosition));
                tilemap.SetTile(endPos, pathTile);
                hasEndPosSet = true;

                AStarSearchPath();
            }
            else//重置
            {
                hasStartPosSet = false;
                hasEndPosSet = false;

                foreach (var item in pathSave)
                {
                    tilemap.SetTile(item.Key, normalTile);
                }

                search.Clear();
                cost.Clear();
                pathSave.Clear();
                hadSearch.Clear();

            }
        }
    }
    //创建白色地图
    public void CreateNormalTiles()
    {
        for (int i = 0; i < mapSize.x; i++)
        {
            for (int j = 0; j < mapSize.y; j++)
            {
                Vector3Int position = new Vector3Int(i, j, 0);
                tilemap.SetTile(position, normalTile);
            }
        }
    }
    //创建黑色障碍
    public void CreateObstacleTiles()
    {
        List<Vector3Int> blankTiles = new List<Vector3Int>();

        for (int i = 0; i < mapSize.x; i++)
        {
            for (int j = 0; j < mapSize.y; j++)
            {
                blankTiles.Add(new Vector3Int(i, j, 0));
            }
        }

        for (int i = 0; i < obstacleCount; i++)
        {
            int index = Random.Range(0, blankTiles.Count);
            Vector3Int obstaclePos = blankTiles[index];
            blankTiles.RemoveAt(index);
            obstacle.Add(obstaclePos);

            tilemap.SetTile(obstaclePos, obstacleTile);
        }
    }
    //AStar算法查找
    public void AStarSearchPath()
    {
        //初始化
        search.Add(startPos, GetHeuristic(startPos, endPos));
        cost.Add(startPos, 0);
        hadSearch.Add(startPos);
        pathSave.Add(startPos, startPos);

        while (search.Count > 0)
        {
            Vector3Int current = GetShortestPos();//获取任务列表里的最少消耗的那个坐标

            if (current.Equals(endPos))
                break;

            List<Vector3Int> neighbors = GetNeighbors(current);//获取当前坐标的邻居

            foreach (var next in neighbors)
            {
                if (!hadSearch.Contains(next))
                {
                    cost.Add(next, cost[current] + 1);//计算当前格子的消耗,其实就是上一个格子加1步
                    search.Add(next, cost[next] + GetHeuristic(next, endPos));//添加要查找的任务,消耗值为当前消耗加上当前点到终点的距离
                    pathSave.Add(next, current);//保存路径
                    hadSearch.Add(next);//添加该点为已经查询过
                }
            }
        }

        if (pathSave.ContainsKey(endPos))
            ShowPath();
        else
            print("No road");
    }
    //获取周围可用的邻居
    private List<Vector3Int> GetNeighbors(Vector3Int target)
    {
        List<Vector3Int> neighbors = new List<Vector3Int>();

        Vector3Int up = target + Vector3Int.up;
        Vector3Int right = target + Vector3Int.right;
        Vector3Int left = target - Vector3Int.right;
        Vector3Int down = target - Vector3Int.up;

        //Up
        if (up.y < mapSize.y && !obstacle.Contains(up))
        {
            neighbors.Add(up);
        }
        //Right
        if (right.x < mapSize.x && !obstacle.Contains(right))
        {
            neighbors.Add(target + Vector3Int.right);
        }
        //Left
        if (left.x >= 0 && !obstacle.Contains(left))
        {
            neighbors.Add(target - Vector3Int.right);
        }
        //Down
        if (down.y >= 0 && !obstacle.Contains(down))
        {
            neighbors.Add(target - Vector3Int.up);
        }

        return neighbors;
    }
    //获取当前位置到终点的消耗
    private int GetHeuristic(Vector3Int posA, Vector3Int posB)
    {
        return Mathf.Abs(posA.x - posB.x) + Mathf.Abs(posA.y - posB.y);
    }
    //获取任务字典里面最少消耗的坐标
    private Vector3Int GetShortestPos()
    {
        KeyValuePair<Vector3Int, int> shortest = new KeyValuePair<Vector3Int, int>(Vector3Int.zero, int.MaxValue);

        foreach (var item in search)
        {
            if (item.Value < shortest.Value)
            {
                shortest = item;
            }
        }

        search.Remove(shortest.Key);

        return shortest.Key;
    }
    //显示查找完成的路径
    private void ShowPath()
    {
        print(pathSave.Count);
        Vector3Int current = endPos;

        while (current != startPos)
        {
            Vector3Int next = pathSave[current];

            tilemap.SetTile(current, pathTile);

            current = next;
        }
    }
}

  

其实没什么难点,主要是理解,每次取出来计算下一步的点,是字典里面最少消耗值的那个点,然后每个点的消耗值都是,起点到当前的步数与当前到终点的距离(专业名词叫曼哈顿距离Manhattan distance2333)之和。

完结。

欢迎交流,转载注明出处!

原文地址:https://www.cnblogs.com/JinT-Hwang/p/11141202.html

时间: 2024-10-07 23:02:30

unity利用A*算法进行2D寻路的相关文章

【Unity】12.2 导航网格寻路简单示例

开发环境:Win10.Unity5.3.4.C#.VS2015 创建日期:2016-05-09 一.简介 本节通过一个简单例子,演示如何利用静态对象实现导航网格,并让某个动态物体利用导航网格自动寻路,最终找到目标. 二.设计步骤 1.添加3个Cube 启动Unity应用程序打开ch1201_Navmesh_Sample工程,新建一个名为Demo1-1.unity的场景,然后在场景中创建3个Cube,如下图所示: 2.生成导航网格 (1)将3个Cube全变为Static 分别选中游戏场景中的3个C

利用QR算法求解矩阵的特征值和特征向量

利用QR算法求解矩阵的特征值和特征向量 为了求解一般矩阵(不是那种幼稚到shi的2 x 2矩阵)的特征值. 根据定义的话,很可能需要求解高阶方程... 这明显是个坑...高阶方程你肿么破... 折腾了好久 1.我要求特征值和特征向量. 2.找到一种算法QR分解矩阵求解特征值 3.QR矩阵分解需要Gram-schimidt正交化分解 有一种很明显的感觉,往往在现在很难有 很系统 很深入 的学习某一个学科的某一门知识. 往往学的时候"靠,学这东西有什么用""学了这么久,也不知道怎么用,不想学" 到后

算法:Astar寻路算法改进,双向A*寻路算法

早前写了一篇关于A*算法的文章:<算法:Astar寻路算法改进> 最近在写个js的UI框架,顺便实现了一个js版本的A*算法,与之前不同的是,该A*算法是个双向A*. 双向A*有什么好处呢? 我们知道,A*的时间复杂度是和节点数量以及起始点难度呈幂函数正相关的. 这个http://qiao.github.io/PathFinding.js/visual/该网址很好的演示了双向A*的效果,我们来看一看. 绿色表示起点,红色表示终点,灰色是墙面.稍浅的两种绿色分别代表open节点和close节点:

一步一步写算法(之寻路)

原文:一步一步写算法(之寻路) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 寻路是游戏设计中需要使用到一种功能,那么我们怎么样以一个点作为起始点,快速地寻找到目标点呢?其实寻路的方法不难.一种简单有效的方法就是回溯法.如果我们从一个点出发,那么这个点周围肯定有若干条路,只要有一条路存在,我们就一直走下去,直到发现没有路走为止:要是发现路走不下去了怎么办,那就只好回头了,我们只能从剩下的选项中继续选择一条路,继续尝试.如果很不幸,所有的

Unity利用AnimationCurve做物体的各种运动

?之前一直都是自己学习Unity各种做Demo,最近开始正式使用Unity来做一个款2d的游戏. 其中在做一个类似小球弹跳运动的时候遇到了点问题,查找了很多资料,无意间发现AnimationCurve,顿时那种心情啊! 然后苦心钻研了一翻 抛砖引玉 的写了个Move2D的类主要是个大家一个思路. 不多说上正菜: 直线平移运动效果: 曲线上升运动效果: 曲线上升然后下降的弧线运动效果: 小球弹跳运动效果: 下面是C#代码,由于之前一直用Cocos2d-x所以有点cocos的风格: using Un

利用RANSAC算法筛选SIFT特征匹配

关于RANSAC算法的基本思想,可从网上搜索找到,这里只是RANSAC用于SIFT特征匹配筛选时的一些说明. RANSAC算法在SIFT特征筛选中的主要流程是: (1) 从样本集中随机抽选一个RANSAC样本,即4个匹配点对 (2) 根据这4个匹配点对计算变换矩阵M (3) 根据样本集,变换矩阵M,和误差度量函数计算满足当前变换矩阵的一致集consensus,并返回一致集中元素个数 (4) 根据当前一致集中元素个数判断是否最优(最大)一致集,若是则更新当前最优一致集 (5) 更新当前错误概率p,

SA:利用SA算法解决TSP(数据是14个虚拟城市的横纵坐标)问题——Jason niu

%SA:利用SA算法解决TSP(数据是14个虚拟城市的横纵坐标)问题--Jason niu X = [16.4700 96.1000 16.4700 94.4400 20.0900 92.5400 22.3900 93.3700 25.2300 97.2400 22.0000 96.0500 20.4700 97.0200 17.2000 96.2900 16.3000 97.3800 14.0500 98.1200 16.5300 97.3800 21.5200 95.5900 19.4100

opencv利用hough概率变换拟合得到直线后,利用DDA算法得到直线上的像素点坐标

图片霍夫变换拟合得到直线后,怎样获得直线上的像素点坐标? 这是我今天在图像处理学习中遇到的问题,霍夫变换采用的概率霍夫变换,所以拟合得到的直线信息其实是直线的两个端点的坐标,这样一个比较直接的思路就是利用DDA算法来获取. 一.算法简介 DDA算法是计算机图形学中最简单的绘制直线算法.其主要思想是由直线公式y = kx + b推导出来的. 我们已知直线段两个端点P0(x0,y0)和P1(x1,y1),就能求出 k 和 b . 在k,b均求出的条件下,只要知道一个x值,我们就能计算出一个y值.如果

[Unity算法]A星寻路(一):基础版本

参考链接: https://www.cnblogs.com/yangyxd/articles/5447889.html 一.原理 1.将场景简化,分割为一个个正方形格子,这些格子称之为节点(node),从一个节点到另一个节点的距离称之为代价(cost).一个节点与水平/垂直方向的相邻节点的代价是1,与对角节点的代价是1.4.这里引用公式f = g + h,f表示该节点的总代价,g表示该节点与上一路径节点的代价,h表示该节点与目标节点的代价. 2.需要两个列表,开启列表(openList)和关闭列