A*算法

A星算法,重要是用递归,对周围的八个点或者四个点进行遍历,一直找到结束点为止

先定义节点类型

/// <summary>
/// 节点类型
/// </summary>
public enum NodeType 
{
    Normal,  // 可以通过
    Obstacle,   //障碍物
}

定义格子(节点)

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

/// <summary>
/// 节点(格子)
/// </summary>
public class Node 
{
    public int x;  //节点x轴位置

    public int z;   //节点y轴位置

    public int F;  //估价函数G = G+ H

    public int G;  //表示当前节点距离起始点的位置

    public int H;  //表示当前节点距离结束点的位置

    public Node parent;  //父节点

    public Transform trans;

    public NodeType nodeType;  //该节点属性

    public Node(int x, int z)
    {
        this.x = x;
        this.z = z;
    }

    public void SetF()
    {
        this.F = this.H + this.G;
    }

    //计算当前节点距离起始点的距离代价
    public void SetG(Node parent, int value)
    {
        this.G = parent.G + value;
    }

    //计算出当前节点距离目标点的距离
    public void SetH(Node target)
    { 
        this.H = Mathf.Abs(target.x - this.x) + Mathf.Abs(target.z - this.z);
        H *= 10;
    }

}

算法

public class AStar : MonoBehaviour
{
    [SerializeField]
    private int moveSpeed = 2;

    [SerializeField]
    private Vector2Int mapSize = new Vector2Int(24, 24);  //地图大小

    private Transform player;  //玩家
    private Node[,] maps;   //地图大小
    private Stack<Vector3> ways = null;
    private Vector2Int[] dirs =  //八个方向
     { 
       new  Vector2Int(0,1),
       new  Vector2Int(0,-1),
       new  Vector2Int(1,0),
       new  Vector2Int(-1,0),  //上、下、左、右四个方向
       new  Vector2Int(-1,-1),
       new  Vector2Int(1,1),
       new  Vector2Int(-1,1),
       new  Vector2Int(1,-1) //斜对角四个方向
    };

    private void Awake()
    {
        this.maps = new Node[mapSize.x, mapSize.y] ;
    }

    private void Start()
    {
        InitMaps();
        InitPlayer();

        int x = Random.Range(0, mapSize.x);
        int z = Random.Range(0, mapSize.y);
        maps[x, z].trans.GetComponent<Renderer>().material.color = Color.blue;  //目标点
        this.ways = FindWays(new Vector2(x, z));  //随机目标点
    }

    //创建随机地图
    private void InitMaps()
    {
        GameObject nodes = new GameObject("nodes");
        for (int i = 0; i < maps.GetLength(0); i++)
        {
            for (int j = 0; j < maps.GetLength(1); j++)
            {
                maps[i, j] = new Node(i, j);  //创建该位置节点
                GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
                maps[i, j].trans = go.transform;
                maps[i, j].trans.name = string.Format("{0},{1}", i, j);
                maps[i, j].trans.position = new Vector3(i, 0, j);  //位置
                maps[i, j].trans.localScale = Vector3.one * 0.5f;
                maps[i, j].trans.SetParent(nodes.transform);
                maps[i,j].nodeType = Random.Range(0, 10) < 8? NodeType.Normal:NodeType.Obstacle;  //随机生成障碍物
                switch (maps[i, j].nodeType)  //可以通过的为白色,不能通过的为红色
                {
                    case NodeType.Normal:
                        maps[i, j].trans.GetComponent<Renderer>().material.color = Color.white;
                        break;
                    case NodeType.Obstacle:
                        maps[i, j].trans.GetComponent<Renderer>().material.color = Color.red;
                        break;
                }
            }
        }
    }

    //初始化玩家
    private void InitPlayer()
    {
        this.player = GameObject.CreatePrimitive(PrimitiveType.Capsule).transform;
        this.player.name = "Player";
        this.player.localScale = new Vector3(0.5f,1,0.5f);
        int x = Random.Range(0, mapSize.x);
        int z = Random.Range(0, mapSize.y);
        maps[x, z].trans.GetComponent<Renderer>().material.color = Color.gray;
        this.player.position = new Vector3(x,1,z);  
        this.player.GetComponent<Renderer>().material.color = Color.green;
    }

    //玩家位置为起始点
    private Node GetFirstNode()
    {
        int x = (int)this.player.position.x;
        int z = (int)this.player.position.z;
        return this.maps[x,z];
    }

    //根据位置获取目标节点
    private Node GetTargetNode(Vector2 targetPos)
    {
        int x = (int)targetPos.x;
        int z = (int)targetPos.y;
        return maps[x, z];
        //if (isOk(x, z))
        //{
        //    return maps[x, z];
        //}
        //else
        //{
        //    return null;
        //}
    }

    //寻找目标点
    List<Node> closeList = new List<Node>();  //必须要寻找的节点
    private Node FindTarget(Vector2 targetPos)
    {
        Node firstNode = GetFirstNode();
        Debug.Log("起始点:" + firstNode.trans.name);
        Node targetNode = GetTargetNode(targetPos);
        if (targetNode == null)
        {
            Debug.Log("目标点不存在或者不合法");
            return null;
        }
        Debug.Log("目标点:" + targetNode.trans.name);
        List<Node> openList = new List<Node>();  //需要计算的节点
        openList.Add(firstNode);
        closeList.Add(firstNode);
        while (openList.Count > 0)
        {
            Node node = openList[0];

            for (int i = 0; i < dirs.Length; i++)
            {
                int x = node.x + dirs[i].x;
                int z = node.z + dirs[i].y;
                int value = i < 4 ? 10: 14;
                if (isOk(x, z, node, value))
                {  
                    //满足条件的节点放入OpenList
                    maps[x, z].parent = node;  //设置父元素
                    maps[x, z].SetG(node, value);
                    maps[x, z].SetH(targetNode);
                    maps[x, z].SetF();
                    openList.Sort(Compare);

                    if (maps[x, z] == targetNode)
                    {
                        Debug.Log(targetNode.x + "," + targetNode.z);
                        return maps[x, z];  //找到目标点
                    }
                    closeList.Add(maps[x,z]);
                    openList.Add(maps[x,z]);
                }
            }
            openList.Remove(node);
        }
        return null;
    }
    
    //排序
    public int Compare(Node left, Node right)
    {
        if (left.F > right.F)
        {
            return 1;
        }
        if (left.F < right.F)
        {
            return -1;
        }
        return 0;
    }

    //寻路
    private Stack<Vector3> FindWays(Vector2 targetPos)
    {
        Node targetNode = FindTarget(targetPos);
        if (targetNode != null)
        {
            Debug.Log("找到的Target:" + targetNode.trans.name);
        }
        Stack<Vector3> ways = new Stack<Vector3>();
        //从目标点往起始点寻找,栈最上面为起始点,然后以此弹出为路径
        while (targetNode != null) 
        {
            Vector3 pos = new Vector3(targetNode.x, 0, targetNode.z);
            ways.Push(pos);
            targetNode = targetNode.parent;
        }
        return ways;
    }

    //是否满足条件
    private bool isOk(int x, int z, Node parent, int value)
    {
        if (x < maps.GetLength(0) && x >=0  && z < maps.GetLength(1) && z >= 0)
        {
            Node node = maps[x, z];
            if (node.nodeType == NodeType.Obstacle )
            {
                return false;   //障碍物,已经寻找过的也返回false
            }

            if (closeList.Contains(maps[x, z])) //如果已经访问了,也返回假
            {
                //检测更新G值
                if (maps[x, z].G > parent.G + value)
                {
                    Debug.Log(maps[x,z].trans.name + "+++++" + parent.trans.name + "G:" + parent.G + "===" + value);
                    maps[x, z].G = parent.G + value;
                    maps[x, z].parent = parent;
                }
                return false;
            }

            else
            {
                return true;
            }
        }
        else
        {
            return false;  //超出地图范围
        }
    }

    private void Update()
    {
        if (ways != null && ways.Count > 0)
        {
            Vector3 lastPos = ways.Peek();
            Vector3 targetPos = new Vector3(lastPos.x, player.position.y, lastPos.z);
            if (Vector3.Distance(player.position, targetPos) > 0.02f)
            {
                Vector3 dirNormal = Vector3.Normalize(targetPos - player.position);
                player.Translate(dirNormal * Time.deltaTime * moveSpeed);
            }
            else
            {
                ways.Pop();
            }
        }
    }

}

 

posted @ 2022-06-18 20:11  狂奔的老鳖  阅读(43)  评论(0编辑  收藏  举报