A星算法设计(在unity中编写)

前述

地图:

在这里插入图片描述

寻路步骤:

在这里插入图片描述

公式:

在这里插入图片描述

实现代码:

实现代码:

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

public class AStar : MonoBehaviour
{
    const int mapWidth = 8;
    const int mapHeight = 6;
    private Point[,] map = new Point[mapWidth, mapHeight];
    void Start()
    {
        InitMap();
        Point start = map[2, 3];
        Point end = map[6, 3];
        FindPath(start, end);
        ShowPath(start, end);
        GetArroundPoints(map[3, 3]);
    }

    private void ShowPath(Point start, Point end)
    {
        Point tempPoint = end;
        while(true)
        {
            Debug.Log("temp.X:" + tempPoint.X + "temp.Y:" + tempPoint.Y);
            tempPoint = tempPoint.parent;
            if(tempPoint == null)
            {
                return;
            }
        }
    }

    private void InitMap()
    {
        for(int x=0;x< mapWidth;x++)
        {
            for(int y=0;y<mapHeight;y++)
            {
                map[x, y] = new Point(x, y);
            }
        }

        map[4, 2].IsWall = true;
        map[4, 3].IsWall = true;
        map[4, 4].IsWall = true;
    }

    private void FindPath(Point start,Point end)
    {
        List<Point> openList = new List<Point>();
        List<Point> closeList = new List<Point>();
        openList.Add(start);
        while(openList.Count> 0 )
        {
            Point p = FindMinFofPoint(openList);
            openList.Remove(p);
            closeList.Add(p);
            List<Point> arroundPoints = GetArroundPoints(p);
            PointsFilter(arroundPoints, closeList);
            foreach (Point arroundPoint in arroundPoints)
            {
                if(openList.IndexOf(arroundPoint)> -1)
                {
                    float nowG = CalcG(arroundPoint, p);
                    if (nowG < arroundPoint.G)
                    {
                        arroundPoint.UpdateParent(p, nowG);
                    }
                }
                else //不在开启列表中
                {
                    arroundPoint.parent = p;
                    CalcF(arroundPoint, end);
                    openList.Add(arroundPoint);
                }
            }

            //判断终止
            if(openList.IndexOf(end)> -1)
            {
                return;
            }
        }
    }
    private void PointsFilter(List<Point> src,List<Point> closeList)
    {
        foreach(Point p in closeList)
        {
            if(src.IndexOf(p) > -1)
            {
                src.Remove(p);
            }
        }
    }
    private Point FindMinFofPoint(List<Point> openList)
    {
        float f = float.MaxValue;
        Point tempP = null;
        foreach(Point p in openList)
        {
            if(p.F < f)
            {
                tempP = p;
                f = p.F;
            }
        }
        return tempP;
    }

    private List<Point> GetArroundPoints(Point point)
    {
        Point up = null, down = null, left = null, right = null;
        Point lu = null, ru = null, ld = null, rd = null;
        if (point.Y < mapHeight - 1)
        {
            up = map[point.X, point.Y + 1];
        }
        if (point.Y > 0)
        {
            down = map[point.X, point.Y - 1];
        }
        if (point.X > 0)
        {
            left = map[point.X - 1, point.Y];
        }
        if (point.X < mapWidth - 1)
        {
            right = map[point.X + 1, point.Y];
        }
        if (up != null && left != null)
        {
            lu = map[point.X - 1, point.Y + 1];
        }
        if (up != null && right != null)
        {
            ru = map[point.X + 1, point.Y + 1];
        }
        if (down != null && left != null)
        {
            ld = map[point.X - 1, point.Y - 1];
        }
        if (down != null && right != null)
        {
            rd = map[point.X + 1, point.Y - 1];
        }
        List<Point> list = new List<Point>();
        if (down != null && down.IsWall == false)
        {
            list.Add(down);
        }
        if (up != null && up.IsWall == false)
        {
            list.Add(up);
        }
        if (left != null && left.IsWall == false)
        {
            list.Add(left);
        }
        if (right != null && right.IsWall == false)
        {
            list.Add(right);
        }
        if (lu != null && lu.IsWall == false && left.IsWall == false && up.IsWall == false)
        {
            list.Add(lu);
        }
        if (ld != null && ld.IsWall == false && left.IsWall == false && down.IsWall == false)
        {
            list.Add(ld);
        }
        if (ru != null && ru.IsWall == false && right.IsWall == false && up.IsWall == false)
        {
            list.Add(ru);
        }
        if (rd != null && rd.IsWall == false && right.IsWall == false && down.IsWall == false)
        {
            list.Add(rd);
        }
        return list;
    }

    private float CalcG(Point now, Point parent)
    {
       return Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(parent.X, parent.Y)) + parent.G;
    }

    private void CalcF(Point now, Point end)
    {
        //F = G + H
        float h = Mathf.Abs(now.X - end.X) + Mathf.Abs(now.Y - end.Y);
        float g = 0;
        if(now.parent == null)
        {
            g = 0;
        }
        else
        {
            g = Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(now.parent.X, now.parent.Y)) + now.parent.G;
        }
        float f = g + h;
        now.F = f;
        now.G = g;
        now.H = h;
    }

}

顶点定义脚本:

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

public class Point
{
    public Point parent { get; set; }
    public float F { get; set; }
    public float G { get; set; }
    public float H { get; set; }

    public int X { get; set; }
    public int Y { get; set; }

    public bool IsWall { get; set; }

    public Point(int x, int y, Point parent = null)
    {
        this.X = x;
        this.Y = y;
        this.parent = parent;
        IsWall = false;
    }

    public void UpdateParent(Point parent,float g)
    {
        this.parent = parent;
        this.G = g;
        F = G + H;
    }

}

代码详解:

AStar

1.把地图小方块转换成Point方式便于计算
 const int mapWidth = 8;
    const int mapHeight = 6;
    private Point[,] map = new Point[mapWidth, mapHeight];
2.初始化地图 InitMap()
private void InitMap()
    {
        for(int x=0;x< mapWidth;x++)
        {
            for(int y=0;y<mapHeight;y++)
            {
                map[x, y] = new Point(x, y);
            }
        }

        map[4, 2].IsWall = true;
        map[4, 3].IsWall = true;
        map[4, 4].IsWall = true;
    }

把那些是墙的地方赋值上去

3.查找寻路路径 FindPath(start, end)
思路:

分两个列表 开启列表和关闭列表,如前面步骤所述就不再描述了,

  1. 先从开启列表中找一个F最小的点,也就是最近的点,通过方法FindMinFofPoint 找到p
  2. 把p从开启列表中移出去,添加到关闭列表中
  3. 查找p周围可达到点 通过方法GetArroundPoints
  4. 判断周围可达到点 是否在关闭列表中 如果在的话,就排除掉,因为关闭列表的不需要检查
  5. 遍历这些周围可达到点,如果在开启列表中,就看看他需不需要换个爸爸,即到达该点有没有更短的路径
    如果他不在开启列表中(当然也不在关闭列表),这时要考虑把他加进开启列表,就要计算他的 F,G,H
    每个点的H基本是不变的,就是到end点的横坐标距离加上纵坐标距离的和 因为只能上下左右的移动
  6. 如果关闭点也在开启列表中了,那么就说明我们都计算完了,因为如果在开启列表中了,我们就知道他爸爸是谁了
代码:
private void FindPath(Point start,Point end)
    {
        List<Point> openList = new List<Point>();
        List<Point> closeList = new List<Point>();
        openList.Add(start);
        while(openList.Count> 0 )
        {
            Point p = FindMinFofPoint(openList);
            openList.Remove(p);
            closeList.Add(p);
            List<Point> arroundPoints = GetArroundPoints(p);
            PointsFilter(arroundPoints, closeList);
            foreach (Point arroundPoint in arroundPoints)
            {
                if(openList.IndexOf(arroundPoint)> -1)
                {
                    float nowG = CalcG(arroundPoint, p);
                    if (nowG < arroundPoint.G)
                    {
                        arroundPoint.UpdateParent(p, nowG);
                    }
                }
                else //不在开启列表中
                {
                    arroundPoint.parent = p;
                    CalcF(arroundPoint, end);
                    openList.Add(arroundPoint);
                }
            }

            //判断终止
            if(openList.IndexOf(end)> -1)
            {
                return;
            }
        }
    }
从开启列表中找一个最近的点 FindMinFofPoint

即F最小的点

private Point FindMinFofPoint(List<Point> openList)
    {
        float f = float.MaxValue;
        Point tempP = null;
        foreach(Point p in openList)
        {
            if(p.F < f)
            {
                tempP = p;
                f = p.F;
            }
        }
        return tempP;
    }
寻找P周围的点 GetArroundPoints
private List<Point> GetArroundPoints(Point point)
    {
        Point up = null, down = null, left = null, right = null;
        //左上,右上,左下,右下
        Point lu = null, ru = null, ld = null, rd = null;
        //检查p的上方是否有可达点
        if (point.Y < mapHeight - 1)
        {
            up = map[point.X, point.Y + 1];
        }
        //检查p的下方是否有可达点
        if (point.Y > 0)
        {
            down = map[point.X, point.Y - 1];
        }
        //检查p的左方是否有可达点
        if (point.X > 0)
        {
            left = map[point.X - 1, point.Y];
        }
        //检查p的右方是否有可达点
        if (point.X < mapWidth - 1)
        {
            right = map[point.X + 1, point.Y];
        }
        //检查p的左上方是否有可达点
        if (up != null && left != null)
        {
            lu = map[point.X - 1, point.Y + 1];
        }
        //检查p的右上方是否有可达点
        if (up != null && right != null)
        {
            ru = map[point.X + 1, point.Y + 1];
        }
        //检查p的左上方是否有可达点
        if (down != null && left != null)
        {
            ld = map[point.X - 1, point.Y - 1];
        }
        //检查p的右下方是否有可达点
        if (down != null && right != null)
        {
            rd = map[point.X + 1, point.Y - 1];
        }
        List<Point> list = new List<Point>();
        if (down != null && down.IsWall == false)
        {
            list.Add(down);
        }
        if (up != null && up.IsWall == false)
        {
            list.Add(up);
        }
        if (left != null && left.IsWall == false)
        {
            list.Add(left);
        }
        if (right != null && right.IsWall == false)
        {
            list.Add(right);
        }
        //因为左上方是先左边在上边移动过去的,所以左和上都不能说墙
        if (lu != null && lu.IsWall == false && left.IsWall == false && up.IsWall == false)
        {
            list.Add(lu);
        }
        if (ld != null && ld.IsWall == false && left.IsWall == false && down.IsWall == false)
        {
            list.Add(ld);
        }
        if (ru != null && ru.IsWall == false && right.IsWall == false && up.IsWall == false)
        {
            list.Add(ru);
        }
        if (rd != null && rd.IsWall == false && right.IsWall == false && down.IsWall == false)
        {
            list.Add(rd);
        }
        return list;
    }
关闭列表的点不需要再检查 PointsFilter

所以周围的点如果在关闭列表中 就把这个点移除出去

private void PointsFilter(List<Point> src,List<Point> closeList)
    {
        foreach(Point p in closeList)
        {
            if(src.IndexOf(p) > -1)
            {
                src.Remove(p);
            }
        }
    }
计算当前点的G点 CalcG

因为可以斜着 所以

 private float CalcG(Point now, Point parent)
    {
       return Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(parent.X, parent.Y)) + parent.G;
    }

即从当前点到父亲点距离,然后加上父亲点的G

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值