Static, non-walkable trees update graph runtime - solution for bad performance

Hello,
I wan’t to share script that I made. It helps me to improve bottleneck (rebaking trees) when updating Recast graph runtime. Without this it took 300-400 ms and A LOT of GC to update graph (by AstarPath.active.UpdateGraphs), now it is 20-30 ms and almost no GC. Solution is to replace trees with simple colliders + RecastMeshObject on pre-scanning and then comment/remove code that scans for trees changes.

        public static void ReplaceTrees()
        {
            string tag = "TreesObstacles";
            Terrain[] terrains = Terrain.activeTerrains;

            GameObject parent = GameObject.FindGameObjectWithTag(tag);

            if (parent != null)
            {
                DestroyImmediate(parent);
            }

            parent = new GameObject(tag);
            parent.tag = tag;
            parent.transform.position = Vector3.zero;

            if (terrains.Length > 0)
            {
                for (int j = 0; j < terrains.Length; j++)
                {
                    if (terrains[j].terrainData == null) continue;

                    TerrainData data = terrains[j].terrainData;

                    for (int i = 0; i < data.treeInstances.Length; i++)
                    {
                        TreeInstance instance = data.treeInstances[i];
                        TreePrototype prot = data.treePrototypes[instance.prototypeIndex];

                        if (prot.prefab == null)
                        {
                            continue;
                        }

                        var collider = prot.prefab.GetComponent<CapsuleCollider>();
                        var treePosition = terrains[j].transform.position + Vector3.Scale(instance.position, data.size);
                        var scale = new Vector3(instance.widthScale, instance.heightScale, instance.widthScale);
                        scale = Vector3.Scale(scale, prot.prefab.transform.localScale);

                        if (collider != null)
                        {
                            GameObject treeGo = new GameObject(string.Format("TreeGO i{0} j{1}", i, j));
                            treeGo.transform.position = treePosition;
                            treeGo.transform.localScale = scale;
                            var col = treeGo.AddComponent<CapsuleCollider>();
                            treeGo.AddComponent<RecastMeshObj>();
                            treeGo.transform.SetParent(parent.transform);

                            Unsupported.CopyComponentToPasteboard(collider);
                            Unsupported.PasteComponentValuesFromPasteboard(col);
                        }
                    }
                }
            }
        }

Then use this metod at the begining of AstarPathEditor.MenuScan. When you do this go to RecastMeshGatherer to CollectTreeMeshes and simply comment / remove this code.

It can be optimized more and I’m shure that if anyone needs it is possible to do walkable and nonstatic (for example trees that you can cut and disable collider later on) versions of this script, but for my needs is good enaught. I’ve hope it helps someone!

1 Like