超级快的A*寻路

众所周知NavMesh经过一轮又一轮的优化,它的效率已经完爆大多数A*算法,为确保项目稳定性,还是建议能用就用,用不了能使DOTS就用DOTS,反正多线程这玩意unity2019就有了

本文资料参考:unity CodeMonkey的教学视频

1、整个脚本不使用class,而使用struct

用途:给unity版本不支持DOTS或多线程的替代方案,用不了Native类型的数组就用普通的换下,效率也会高不少,尽管避免不了内存的剧烈波动问题。

特点:全部都为值类形的对象数据,对栈极为友好,方便CPU处理数据效率高。采用Native类型的集合数组减少内存波动效率高。

代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using CodeMonkey.Utils;
using UnityEngine;
using Unity.Mathematics;
using Unity.Collections;
using Unity.Jobs;
using Unity.VisualScripting;

public class Pathfinding : MonoBehaviour
{
    private const int MOVE_STRAIGHT_COST = 10;
    private const int MOVE_DIAGONAL_COST = 14;

    private void Start()
    {
        // FindPath(new int2(0,0),new int2(3,1));
        FunctionPeriodic.Create(() =>
        {
            float startTime = Time.realtimeSinceStartup;

            for (int i = 0; i < 5; i++)
            {
                FindPath(new int2(0,0),new int2(19,19));
            }
            
            Debug.Log("时间"+(Time.realtimeSinceStartup-startTime)*1000f);
            
        },1f);
    }
    
   

    private void FindPath(int2 startPosition, int2 endPosition)
    {
        //设置建立网格大小
        int2 gridSize = new int2(20, 20); //建立一个4*4的网格
        //建立高性能数组
        NativeArray pathNodeArray = new NativeArray(gridSize.x * gridSize.y, Allocator.Temp);

        for (int x = 0; x < gridSize.x; x++)
        {
            for (int y = 0; y < gridSize.y; y++)
            {
                //创建一个新节点
                //设置起始点
                PathNode pathNode = new PathNode();
                pathNode.x = x;
                pathNode.y = y;
                pathNode.index = CalculateIndex(x,y,gridSize.x);

                pathNode.gCost = int.MaxValue;
                pathNode.hCost = CalculateDistanceCost(new int2(x,y),endPosition);
                pathNode.CalculateFCost();

                pathNode.isWalkble = true;
                pathNode.cameFromNodeIndex = -1;

                pathNodeArray[pathNode.index] = pathNode;
            }
        }
        
        //阻挡节点
        {
            PathNode walkablePathNode = pathNodeArray[CalculateIndex(1,0,gridSize.x)];
            walkablePathNode.SetIsWalkable(false);
            pathNodeArray[CalculateIndex(1, 0, gridSize.x)] = walkablePathNode;
            
            walkablePathNode = pathNodeArray[CalculateIndex(1,1,gridSize.x)];
            walkablePathNode.SetIsWalkable(false);
            pathNodeArray[CalculateIndex(1, 1, gridSize.x)] = walkablePathNode;
            
            walkablePathNode = pathNodeArray[CalculateIndex(1,2,gridSize.x)];
            walkablePathNode.SetIsWalkable(false);
            pathNodeArray[CalculateIndex(1, 2, gridSize.x)] = walkablePathNode;

        }
       
        //临近节点的偏移数组
        NativeArray neighbourOffsetArray = new NativeArray(new int2[]
        {
            new int2(-1,0),
            new int2(1,0),
            new int2(0,1),
            new int2(0,-1),
            new int2(-1,-1),
            new int2(-1,1),
            new int2(1,-1),
            new int2(1,1),
        },Allocator.Temp);

        int endNodeIndex = CalculateIndex(endPosition.x,endPosition.y,gridSize.x);
        
        PathNode startNode = pathNodeArray[CalculateIndex(startPosition.x,startPosition.y,gridSize.x)];

        startNode.gCost = 0;
        startNode.CalculateFCost();
        pathNodeArray[startNode.index] = startNode;

        NativeList openList = new NativeList(Allocator.Temp);
        NativeList closeList = new NativeList(Allocator.Temp);

        openList.Add(startNode.index);

        while (openList.Length>0)
        {
            int currentNodeIndex = GetLowestCostFNodeIndex(openList,pathNodeArray);
            PathNode currentNode = pathNodeArray[currentNodeIndex];

            if (currentNodeIndex==endNodeIndex)
            {
                //到达了目的地
                break;//打破循环
            }
            
            //如果没有到达目的地的
            for (int i = 0; i < openList.Length; i++)
            {
                if (openList[i]==currentNodeIndex)
                {
                    openList.RemoveAtSwapBack(i);
                    break;
                }
            }
            closeList.Add(currentNodeIndex);

            for (int i = 0; i < neighbourOffsetArray.Length; i++)
            {
                int2 neighbourOffset = neighbourOffsetArray[i];
                int2 neighbourPosition = new int2(currentNode.x+neighbourOffset.x,currentNode.y+neighbourOffset.y);
                
                if (!IsPositionInsideGrid(neighbourPosition,gridSize))
                {
                    //如果不在则继续测试
                    continue;
                }

                int neighbourNodeIndex = CalculateIndex(neighbourPosition.x,neighbourPosition.y,gridSize.x);

                if (closeList.Contains(neighbourNodeIndex))
                {
                    //该节点已被搜寻过
                    continue;
                }

                PathNode neighbourNode = pathNodeArray[neighbourNodeIndex];
                if (!neighbourNode.isWalkble)
                {
                    //如果不通过
                    continue;
                }

                int2 currentNodePosition = new int2(currentNode.x,currentNode.y);
               
                int tentativeGCost = currentNode.gCost + CalculateDistanceCost(currentNodePosition,neighbourPosition);
                if (tentativeGCost path =CalculatePath(pathNodeArray,endNode);

            foreach (var pathPosition in path)
            {
                Debug.Log(pathPosition);
            }
            
            path.Dispose();
        }
        
        //清除高性能数组
        pathNodeArray.Dispose();
        neighbourOffsetArray.Dispose();
        openList.Dispose();
        closeList.Dispose();
    }

    private NativeList CalculatePath(NativeArray pathNodeArray,PathNode endNode)
    {
        if (endNode.cameFromNodeIndex==-1)
        {
            //没有找到
            return new NativeList(Allocator.Temp);
        }
        else
        {
            //找到路径
            NativeList path = new NativeList(Allocator.Temp);
            path.Add(new int2(endNode.x,endNode.y));

            PathNode currentNode = endNode;
            while (currentNode.cameFromNodeIndex!=-1)
            {
                PathNode cameFromNode = pathNodeArray[currentNode.cameFromNodeIndex];
                path.Add(new int2(cameFromNode.x,cameFromNode.y));
                currentNode = cameFromNode;
            }
            return path;
        }
    }

    private bool IsPositionInsideGrid(int2 gridPostion,int2 gridSize)
    {
        return
            gridPostion.x >= 0 &&
            gridPostion.y >= 0 &&
            gridPostion.x < gridSize.x &&
            gridPostion.y < gridSize.y;
    }
    //计算对角线权重
    private int CalculateDistanceCost(int2 aPosition,int2 bPosition)
    {
        int xDistance = math.abs(aPosition.x-bPosition.x);
        int yDistance = math.abs(aPosition.y-bPosition.y);
        int remaining = math.abs(xDistance-yDistance);
        return MOVE_DIAGONAL_COST * math.min(xDistance,yDistance)+MOVE_STRAIGHT_COST*remaining;
    }

    private int GetLowestCostFNodeIndex(NativeList openList,NativeArray pathNodeArray)
    {
        PathNode lowestCostPathNode = pathNodeArray[openList[0]];
        for (int i = 1; i < openList.Length; i++)
        {
            PathNode testPathNode = pathNodeArray[openList[i]];
            if (testPathNode.fCost

2、unityDOTS方案的A*寻路,需要先配置DOTS环境

用途:解决性能瓶颈,充分利用设备的计算性能

特点:快!(但目前估计项目组都不会用)

这篇代码能给在学DOTS的人不少帮助

代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using CodeMonkey.Utils;
using UnityEngine;
using Unity.Mathematics;
using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;
using Unity.VisualScripting;

public class Pathfinding1 : MonoBehaviour
{
    private const int MOVE_STRAIGHT_COST = 10;
    private const int MOVE_DIAGONAL_COST = 14;

    private void Start()
    {
        // FindPath(new int2(0,0),new int2(3,1));
        FunctionPeriodic.Create(() =>
        {
            float startTime = Time.realtimeSinceStartup;

            int findPathJobCount = 5;
            NativeArray jobHandleArray = new NativeArray(findPathJobCount, Allocator.Temp);
            
            for (int i = 0; i < findPathJobCount; i++)
            {
                FindPathJob findPathJob = new FindPathJob
                {
                    startPosition =new int2(0,0),//起点
                    endPosition = new int2(19,19)//终点
                };
                //findPathJob.Run();//主线程
                //findPathJob.Schedule();//并发多线程
                jobHandleArray[i]=findPathJob.Schedule();
            }
            
            JobHandle.CompleteAll(jobHandleArray);
            jobHandleArray.Dispose();
            
            Debug.Log("时间"+(Time.realtimeSinceStartup-startTime)*1000f);
            
        },1f);
    }
    
    //建立多线程
   // [BurstCompile]//开启DOTSBurst编译器
    private struct FindPathJob:IJob
    {
       // private IJob _jobImplementation;
        public int2 startPosition;
        public int2 endPosition;
        public void Execute()
        {
            //设置建立网格大小
        int2 gridSize = new int2(20, 20); //建立一个4*4的网格
        //建立高性能数组
        NativeArray pathNodeArray = new NativeArray(gridSize.x * gridSize.y, Allocator.Temp);

        for (int x = 0; x < gridSize.x; x++)
        {
            for (int y = 0; y < gridSize.y; y++)
            {
                //创建一个新节点
                //设置起始点
                PathNode pathNode = new PathNode();
                pathNode.x = x;
                pathNode.y = y;
                pathNode.index = CalculateIndex(x,y,gridSize.x);

                pathNode.gCost = int.MaxValue;
                pathNode.hCost = CalculateDistanceCost(new int2(x,y),endPosition);
                pathNode.CalculateFCost();

                pathNode.isWalkble = true;
                pathNode.cameFromNodeIndex = -1;

                pathNodeArray[pathNode.index] = pathNode;
            }
        }
        
        // //阻挡节点
        // {
        //     PathNode walkablePathNode = pathNodeArray[CalculateIndex(1,0,gridSize.x)];
        //     walkablePathNode.SetIsWalkable(false);
        //     pathNodeArray[CalculateIndex(1, 0, gridSize.x)] = walkablePathNode;
        //     
        //     walkablePathNode = pathNodeArray[CalculateIndex(1,1,gridSize.x)];
        //     walkablePathNode.SetIsWalkable(false);
        //     pathNodeArray[CalculateIndex(1, 1, gridSize.x)] = walkablePathNode;
        //     
        //     walkablePathNode = pathNodeArray[CalculateIndex(1,2,gridSize.x)];
        //     walkablePathNode.SetIsWalkable(false);
        //     pathNodeArray[CalculateIndex(1, 2, gridSize.x)] = walkablePathNode;
        //
        // }
       
        //临近节点的偏移数组
        NativeArray neighbourOffsetArray = new NativeArray(8, Allocator.Temp);

        neighbourOffsetArray[0] = new int2(-1, 0);
        neighbourOffsetArray[1] = new int2(1, 0);
        neighbourOffsetArray[2] = new int2(0, 1);
        neighbourOffsetArray[3] = new int2(0, -1);
        neighbourOffsetArray[4] = new int2(-1, -1);
        neighbourOffsetArray[5] = new int2(-1, 1);
        neighbourOffsetArray[6] = new int2(1, -1);
        neighbourOffsetArray[7] = new int2(1, 1);
       

        int endNodeIndex = CalculateIndex(endPosition.x,endPosition.y,gridSize.x);
        
        PathNode startNode = pathNodeArray[CalculateIndex(startPosition.x,startPosition.y,gridSize.x)];

        startNode.gCost = 0;
        startNode.CalculateFCost();
        pathNodeArray[startNode.index] = startNode;

        NativeList openList = new NativeList(Allocator.Temp);
        NativeList closeList = new NativeList(Allocator.Temp);

        openList.Add(startNode.index);

        while (openList.Length>0)
        {
            int currentNodeIndex = GetLowestCostFNodeIndex(openList,pathNodeArray);
            PathNode currentNode = pathNodeArray[currentNodeIndex];

            if (currentNodeIndex==endNodeIndex)
            {
                //到达了目的地
                break;//打破循环
            }
            
            //如果没有到达目的地的
            for (int i = 0; i < openList.Length; i++)
            {
                if (openList[i]==currentNodeIndex)
                {
                    openList.RemoveAtSwapBack(i);
                    break;
                }
            }
            closeList.Add(currentNodeIndex);

            for (int i = 0; i < neighbourOffsetArray.Length; i++)
            {
                int2 neighbourOffset = neighbourOffsetArray[i];
                int2 neighbourPosition = new int2(currentNode.x+neighbourOffset.x,currentNode.y+neighbourOffset.y);
                
                if (!IsPositionInsideGrid(neighbourPosition,gridSize))
                {
                    //如果不在则继续测试
                    continue;
                }

                int neighbourNodeIndex = CalculateIndex(neighbourPosition.x,neighbourPosition.y,gridSize.x);

                if (closeList.Contains(neighbourNodeIndex))
                {
                    //该节点已被搜寻过
                    continue;
                }

                PathNode neighbourNode = pathNodeArray[neighbourNodeIndex];
                if (!neighbourNode.isWalkble)
                {
                    //如果不通过
                    continue;
                }

                int2 currentNodePosition = new int2(currentNode.x,currentNode.y);
               
                int tentativeGCost = currentNode.gCost + CalculateDistanceCost(currentNodePosition,neighbourPosition);
                if (tentativeGCost path =CalculatePath(pathNodeArray,endNode);

            foreach (var pathPosition in path)
            {
                Debug.Log(pathPosition);
            }
            
            path.Dispose();
        }
        
        //清除高性能数组
        pathNodeArray.Dispose();
        neighbourOffsetArray.Dispose();
        openList.Dispose();
        closeList.Dispose();
    }
        private NativeList CalculatePath(NativeArray pathNodeArray,PathNode endNode)
        {
            if (endNode.cameFromNodeIndex==-1)
            {
                //没有找到
                return new NativeList(Allocator.Temp);
            }
            else
            {
                //找到路径
                NativeList path = new NativeList(Allocator.Temp);
                path.Add(new int2(endNode.x,endNode.y));

                PathNode currentNode = endNode;
                while (currentNode.cameFromNodeIndex!=-1)
                {
                    PathNode cameFromNode = pathNodeArray[currentNode.cameFromNodeIndex];
                    path.Add(new int2(cameFromNode.x,cameFromNode.y));
                    currentNode = cameFromNode;
                }
                return path;
            }
        }
        
 
        private bool IsPositionInsideGrid(int2 gridPostion,int2 gridSize)
        {
         return
         gridPostion.x >= 0 &&
         gridPostion.y >= 0 &&
         gridPostion.x < gridSize.x &&
         gridPostion.y < gridSize.y;
        }
 
        //计算对角线权重
        private int CalculateDistanceCost(int2 aPosition,int2 bPosition)
        {
         int xDistance = math.abs(aPosition.x-bPosition.x);
         int yDistance = math.abs(aPosition.y-bPosition.y); 
         int remaining = math.abs(xDistance-yDistance);
         return MOVE_DIAGONAL_COST * math.min(xDistance,yDistance)+MOVE_STRAIGHT_COST*remaining;
        }

        private int GetLowestCostFNodeIndex(NativeList openList,NativeArray pathNodeArray)
        {
         PathNode lowestCostPathNode = pathNodeArray[openList[0]];
         for (int i = 1; i < openList.Length; i++)
        {
            PathNode testPathNode = pathNodeArray[openList[i]];
            if (testPathNode.fCost

你可能感兴趣的:(unity,DOTS,unity)