UnityA星寻路-3个脚本超简易实现

实现效果
未运行默认状态

[a*寻路scene场景绘制效果]a*寻路scene场景绘制效果

脚本绑定:
![[Pasted image 20231212144346.png]]

源码:
1 .FindPath

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

public class FindPath : MonoBehaviour
{
    public Transform player, EndPoint;
    Grid grid;
    void Start()
    {
        grid = GetComponent<Grid>();
    }

    void Update()
    {
        FindingPath(player.position, EndPoint.position);
        Control();
    }

    /// <summary>
    /// 控制移动方法(测试用)
    /// </summary>
    public virtual void Control() {
        if (Input.GetKey(KeyCode.W))
        {
            if (grid.path.Count > 0)
            {
                player.position = grid.path[0].WorldPos;
            }
        }
    
    }
    /// <summary>
    /// 获取两点之间的距离
    /// </summary>
    /// <param name="StartPos"></param>
    /// <param name="EndPos"></param>
    void FindingPath(Vector3 StartPos, Vector3 EndPos)
    {
        Node startNode = grid.GetFromPostion(StartPos);

        Node EndNode = grid.GetFromPostion(EndPos);
        List<Node> openSet = new List<Node>();
        HashSet<Node> closeSet = new HashSet<Node>();
        openSet.Add(startNode);

        //当开启列表中还有元素的情况下不断的查找最优点
        while (openSet.Count > 0)
        {
            Node currentNode = openSet[0];
            for (int i = 0; i < openSet.Count; i++)
            {
                //如果openSet[i]的费用低于currentNode或者.........
                if (openSet[i].FCost < currentNode.FCost || openSet[i].FCost == currentNode.FCost && openSet[i].hCost < currentNode.hCost)
                {
                    currentNode = openSet[i];
                }
            }

            //如果该节点是最优点,从开始列表中移除
            openSet.Remove(currentNode);
            //添加到关闭列表
            closeSet.Add(currentNode);
            if (currentNode == EndNode)
            {
                GeneratePath(startNode, EndNode);
                return;
            }
            foreach (var node in grid.GetNeibourhood(currentNode))
            {
                if (!node.CanWalk || closeSet.Contains(node)) continue;
                //计算相邻格子的消费
                int newCost = currentNode.gCost + GetDistanceNodes(currentNode, node);
                if (newCost < node.gCost || !openSet.Contains(node))
                {
                    node.gCost = newCost;
                    node.hCost = GetDistanceNodes(node, EndNode);
                    node.parent = currentNode;
                    if (!openSet.Contains(node))
                    {
                        openSet.Add(node);
                    }
                }

            }
        }

    }

    //递归Node的父节点 得到最终的寻路路径
    private void GeneratePath(Node StartNode, Node EndNode)
    {
        List<Node> path = new List<Node>();
        Node temp = EndNode;
        //当目标节点不是开始节点的话循环
        while (temp != StartNode)
        {
            path.Add(temp);
            temp = temp.parent;

        }
        path.Reverse();
        grid.path = path;
    }

    int GetDistanceNodes(Node a, Node b)
    {
        int cntX = Mathf.Abs(a.gridX - b.gridX);
        int cntY = Mathf.Abs(a.gridY - b.gridY);
        if (cntX > cntY)
        {
            return 14 * cntY + 10 * (cntX - cntY);
        }
        else
        {
            return 14 * cntX + 10 * (cntY - cntX);
        }

    }

}

2.Grid

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

public class Grid : MonoBehaviour
{
    private Node[,] grid;
    /// <summary>
    /// 网格大小
    /// </summary>
    public Vector2 gridSize;
    /// <summary>
    /// 每个网格的半径
    /// </summary>
    public float nodeRadius;
    /// <summary>
    /// 每个网格的直径
    /// </summary>
    public float nodeDiameter;
    /// <summary>
    /// 获得不可行走的层
    /// </summary>
    public LayerMask WhatLayer;
    /// <summary>
    /// 网格的长度
    /// </summary>
    public int gridCntX;
    public int gridCntY;
    public Transform player;
    public List<Node> path = new List<Node>();


    void Start()
    {
        nodeDiameter = nodeRadius * 2;
        gridCntX = Mathf.RoundToInt(gridSize.x / nodeDiameter);
        gridCntY = Mathf.RoundToInt(gridSize.y / nodeDiameter);
        grid = new Node[gridCntX, gridCntY];
        CreatGrid();


    }
    /// <summary>
    /// 初始化网格数据
    /// </summary>
    private void CreatGrid()
    {
        //获取初始位置
        Vector3 startPoint = transform.position - gridSize.x / 2 * Vector3.right - gridSize.y / 2 * Vector3.forward;
        for (int i = 0; i < gridCntX; i++)
        {
            for (int j = 0; j < gridCntY; j++)
            {
                Vector3 worldPoint = startPoint + Vector3.right * (i * nodeDiameter + nodeRadius) + Vector3.forward * (j * nodeDiameter + nodeRadius);
                bool walkable = SelectUnWalkable(worldPoint);
                grid[i, j] = new Node(walkable, worldPoint, i, j);
            }
        }
    }
    /// <summary>
    /// 获取格子相对应该的节点位置
    /// </summary>
    /// <param name="postion">格子的V3位置</param>
    /// <returns></returns>
    public Node GetFromPostion(Vector3 postion)
    {
        //获取该位置在X,Y轴上的百分比
        float percentX = (postion.x + gridSize.x / 2) / gridSize.x;
        float percentY = (postion.z + gridSize.y / 2) / gridSize.y;
        percentX = Mathf.Clamp01(percentX);
        percentY = Mathf.Clamp01(percentY);
        int x = Mathf.RoundToInt((gridCntX - 1) * percentX);
        int y = Mathf.RoundToInt((gridCntY - 1) * percentY);
        return grid[x, y];
    }
    //绘制辅助显示线框
    void OnDrawGizmos()
    {
        //绘制一个线框Cuble        参数1:位置 参数2:线框大小
        Gizmos.DrawWireCube(transform.position, new Vector3(gridSize.x, 1, gridSize.y));
        if (grid == null) return;
        //绘制基础地图点
        foreach (var node in grid)
        {
            Gizmos.color = node.CanWalk ? Color.white : Color.red;
            Gizmos.DrawCube(node.WorldPos, Vector3.one * (nodeDiameter - 0.05f));

        }
        Node playerNode = GetFromPostion(player.position);
        //绘制寻路点
        foreach (var node in path)
        {
            Gizmos.color = Color.black;
            Gizmos.DrawCube(node.WorldPos, Vector3.one * (nodeDiameter - 0.05f));
        }

        //绘制起始点
        if (playerNode != null && playerNode.CanWalk)
        {
            Gizmos.color = Color.cyan;
            Gizmos.DrawCube(playerNode.WorldPos, Vector3.one * (nodeDiameter - 0.05f));
        }




    }
    /// <summary>
    /// 获取选定节点的相邻节点(此处可优化,建议处理成在初始化的时候就记录好相邻格子的数据列表)
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    public List<Node> GetNeibourhood(Node node)
    {
        List<Node> neibourhood = new List<Node>();
        for (int i = -1; i <= 1; i++)
        {
            for (int j = -1; j <= 1; j++)
            {
                if (i == 0 && j == 0)
                {
                    continue;
                }
                int tempX = node.gridX + i;
                int tempY = node.gridY + j;
                if (tempX < gridCntX && tempX > 0 && tempY > 0 && tempY < gridCntY)
                {
                    neibourhood.Add(grid[tempX, tempY]);
                }


            }
        }
        return neibourhood;

    }

    /// <summary>
    /// 设置选择遮挡路径的方式
    /// </summary>
    /// <param name="worldPoint"></param>
    /// <returns></returns>
    public virtual bool SelectUnWalkable(Vector3 worldPoint)
    {

        bool walkable = !Physics.CheckSphere(worldPoint, nodeRadius, WhatLayer);
        return walkable;


    }
}

3.Node

using UnityEngine;
using System.Collections;

public class Node
{

    //是否可以行走
    public bool CanWalk;
    //世界坐标
    public Vector3 WorldPos;
    //相对坐标 X,Y
    public int gridX, gridY;
    //从跟节点到此节点的总费用
    public int gCost;
    //从父节点走到此节点花费的费用
    public int hCost;
    //父节点 
    public Node parent;
    public int FCost
    {
        get { return gCost + hCost; }
    }
   
    public Node(bool canWalk, Vector3 worldPos, int x, int y)
    {
        CanWalk = canWalk;
        WorldPos = worldPos;
        gridX = x;
        gridY = y;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值