[Unity]AstarPathfindingProject动态烘焙场景

发布于:2025-05-17 ⋅ 阅读:(19) ⋅ 点赞:(0)

需求

项目是MMO大场景,按地块划分了10x10的大格子。角色移动时动态更新周边场景,且角色还有传送功能。
项目中寻路用了AstarPathfindingProject的Grid。因此需要动态烘焙寻路信息。

核心代码

    private void bakeAStarPath()
    {
		AstarPath astarPath = AstarPath.active;
        if (astarPath == null)
        {
            Debug.LogError("AstarPath 实例未找到,请确保场景中有 AstarPath 组件!");
            return;
        }
        // 获取第一个 GridGraph(假设你只有一个 GridGraph)
        GridGraph gridGraph = astarPath.data.gridGraph;
        if (gridGraph == null)
        {
            Debug.LogError("GridGraph 未找到,请确保已添加 GridGraph!");
            return;
        }

        float newGraphCenterX = curCenterXIndex * xSize + xSize * 0.5f;
        float newGraphCenterZ = curCenterZIndex * zSize + zSize * 0.5f;
        gridGraph.center = new Vector3(newGraphCenterX, 0, newGraphCenterZ);
        gridGraph.width = (int)xSize * 3;
        gridGraph.depth = (int)zSize * 3;//

        // 重新计算并扫描 GridGraph
        astarPath.Scan();

可能遇到的问题

  • 1,烘焙后场景内可通行区域是不可通行的

一般是时机问题,烘焙的时候可能场景还没有准备好。笔者的做法是场景加载好后延迟一个物理帧再烘焙。因为烘焙的底层是通过射线来检测的,确保场景中的物理数据准备就绪再烘焙可以规避这个问题。

yield return new WaitForFixedUpdate();
//TODO:bake callback
  • 2,加了物理帧延迟后依旧很小概率会出现烘焙不上的问题

取一个必可通行的点(比如主角的脚下),检测该点所在的node是否可通行,若不可通行,再重复多次bake,直至可通行。

    int retryCount = 0;
    private void bakeAStarPath()
    {
        AstarPath astarPath = AstarPath.active;
        if (astarPath == null)
        {
            Debug.LogError("AstarPath 实例未找到,请确保场景中有 AstarPath 组件!");
            return;
        }
        // 获取第一个 GridGraph(假设你只有一个 GridGraph)
        GridGraph gridGraph = astarPath.data.gridGraph;
        if (gridGraph == null)
        {
            Debug.LogError("GridGraph 未找到,请确保已添加 GridGraph!");
            return;
        }

        float newGraphCenterX = curCenterXIndex * xSize + xSize * 0.5f;
        float newGraphCenterZ = curCenterZIndex * zSize + zSize * 0.5f;
        gridGraph.center = new Vector3(newGraphCenterX, 0, newGraphCenterZ);
        gridGraph.width = (int)xSize * 3;
        gridGraph.depth = (int)zSize * 3;//

        // 重新计算并扫描 GridGraph
        astarPath.Scan();


        var checkNode = gridGraph.GetNearest(new Vector3(selfRolePos.x, 0, selfRolePos.z));
        bool bakerFlag = false;
        if (checkNode.node.Walkable)
        { 
            bakerFlag = true;
        }
        else
        { 
            bakerFlag = false;
        }
        if (bakerFlag == false)
        {
            Debug.LogError("GridGraph扫描失败,准备重新扫描");
            if (retryCount < 3)
            {
                //再次扫描
                TimerTween.Delay(0.5f, () =>
                {
                    retryCount++;
                    this.bakeAStarPath();
                }).SetTag("delayBakerA*" + retryCount).Start();
            }
        }
        else
        {
            Debug.LogError("GridGraph Center 已更新为: " + gridGraph.center + " 并完成重新扫描, xIndex:" + this.curCenterXIndex + ", zIndex:" + this.curCenterZIndex);
            retryCount = 0;
        } 
    }

  • 3,异步烘焙

插件也提供异步扫描的方式,上述代码中astarPath.Scan();为同步扫描。若场景不太大的话同步扫描耗时较低是可以接受的。若耗时过高,造成严重卡顿,可以考虑使用异步烘焙

    private void bakeAStarPath()
    {
		//...略
		StartCoroutine(ScanGraphAsync(astarPath));
	}
	
    IEnumerator ScanGraphAsync(AstarPath astarPath)
    {
        // 开始异步扫描
        var progress = astarPath.ScanAsync();
        foreach (var p in progress)
        {
            //Debug.Log ("扫描进度: " + p.progress);  
            yield return null;
        }
        Debug.LogError("异步扫描完成!");
    }