AstarA星寻路算法Unity实现

s表示开始的位置。

e表示结束的位置。

共两个脚本:GameManager和MyGrid。

GameManager管理全局。

MyGrid管理每个小格子。

GameManager:

其中h值用对角线距离求得,为了方便计算将距离*10

可以另用曼哈顿距离和欧几里得距离求,close用hash来检测可以提高效率(项目中未用)。

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

public class GameManager : MonoBehaviour {

    public GameObject grid;
    public float startX;
    public float startY;
    private float grab = 1f;
    private GameObject parent;
    private MyGrid[,] map;
    private int row = 10;
    private int col = 20;

    public MyGrid grid1 = null;
    public MyGrid grid2 = null;

    private static GameManager instance;

    public static GameManager Instance
    {
        get
        {
            return instance;
        }
    }

    private void Awake()
    {
        instance = this;
    }

    void Start ()
    {
        parent = GameObject.Find("parent");
        map = new MyGrid[col, row];
        for(int i = 0; i < col; i++)
        {
            for(int j = 0; j < row; j++)
            {
                Vector2 pos = new Vector2(startX + grab * i, startY - grab * j);
                GameObject go = Instantiate(grid, pos, Quaternion.identity) as GameObject;
                go.transform.SetParent(parent.transform);
                map[i, j] = go.GetComponent<MyGrid>();
                map[i, j].SetInfo(i, j, pos);
                if (i == 0 || j == 0 || i == col - 1 || j == row - 1)
                {
                    map[i, j].SetWall();
                }
            }
        }
    }

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            SetOrigin();
        }
    }

    void SetOrigin()
    {
        grid1 = null;
        grid2 = null;
        for (int i = 0; i < col; i++)
        {
            for (int j = 0; j < row; j++)
            {
                if (i == 0 || i == col - 1 || j == 0 || j == row - 1)
                    continue;
                else
                    map[i, j].SetOrigin();
            }
        }
    }

    public void Astar(MyGrid start, MyGrid end)
    {
        List<MyGrid> open = new List<MyGrid>();
        List<MyGrid> close = new List<MyGrid>();
        open.Add(start);
        while(open.Count > 0)
        {
            MyGrid cur = GetMin(open);
            open.Remove(cur);
            if(cur == end)
            {
                PaintRoad(start, end);
                return;
            }
            close.Add(cur);
            for(int i = -1; i <= 1; i++)
            {
                for(int j = -1; j <= 1; j++)
                {
                    int newX = cur.x + i;
                    int newY = cur.y + j;
                    MyGrid temp = map[newX, newY];
                    if (temp.iswall || close.Contains(temp))
                        continue;
                    int newCost = cur.gCost + GetDis(cur.x, cur.y, temp.x, temp.y);
                    if(newCost < temp.gCost || !open.Contains(temp))
                    {
                        temp.gCost = newCost;
                        temp.hCost = GetDis(temp.x, temp.y, end.x, end.y);
                        temp.parent = cur;
                        if(!open.Contains(temp))
                        {
                            open.Add(temp);
                        }
                    }
                }
            }
        }
        SetOrigin();
    }

    MyGrid GetMin(List<MyGrid> open)
    {
        MyGrid cur = open[0];
        foreach(var node in open)
        {
            if(node.fCost <= cur.fCost && node.hCost < cur.hCost)
            {
                cur = node;
            }
        }
        return cur;
    }

    int GetDis(int sx, int sy, int ex, int ey)
    {
        int disx = Mathf.Abs(sx - ex);
        int disy = Mathf.Abs(sy - ey);
        if(disx >= disy)
        {
            return (disx - disy) * 10 + disy * 14;
        }
        else
        {
            return (disy - disx) * 10 + disx * 14;
        }
    }

    void PaintRoad(MyGrid start, MyGrid end)
    {
        while(end != start)
        {
            end.SetRoad();
            end = end.parent;
        }
        end.SetRoad();
    }

    public void SetGrid(MyGrid grid)
    {
        if(grid1 == null)
        {
            grid1 = grid;
            grid1.SetStart();
            return;
        }
        if(grid1 == grid)
        {
            grid1.SetOrigin();
            grid1 = null;
            return;
        }
        grid2 = grid;
        Astar(grid1, grid2);
        grid1 = null;
        grid2 = null;
    }
}

MyGrid:

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

public class MyGrid : MonoBehaviour
{
    public int x;
    public int y;
    public Vector2 pos;
    public MyGrid parent;
    public bool iswall = false;
    public int gCost;
    public int hCost;
    public int fCost
    {
        get { return gCost + hCost; }
    }

    public void SetInfo(int x, int y, Vector2 pos)
    {
        this.x = x;
        this.y = y;
        this.pos = pos;
        iswall = false;
        parent = null;
        gCost = 0;
        hCost = 0;
    }

    public void SetWall()
    {
        iswall = true;
        GetComponent<SpriteRenderer>().color = Color.red;
    }

    public void SetRoad()
    {
        GetComponent<SpriteRenderer>().color = Color.green;
    }

    public void SetOrigin()
    {
        GetComponent<SpriteRenderer>().color = Color.white;
        iswall = false;
    }

    public void SetStart()
    {
        GetComponent<SpriteRenderer>().color = Color.blue;
    }

    private void OnMouseDown()
    {
        if(Input.GetKey(KeyCode.W))
        {
            if (!iswall)
                SetWall();
            else
                SetOrigin();
            return;
        }
        if (iswall)
            return;
        GameManager.Instance.SetGrid(this);
    }
}

按住w再对着格子按左键,可以变成墙格子或者恢复成正常格子(不要把边缘的墙取消了,未加入出界判断)

按空格恢复初始状态

左键选择起点和终点

 

项目下载地址:https://github.com/lMonster81/AstarProject

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值