using UnityEngine; using UnityEditor; using System.Collections.Generic; public class ReplacePrefabsWithTerrainTrees : EditorWindow { Terrain targetTerrain; [MenuItem("Tools/Terrain/Replace Prefabs with Trees")] static void ShowWindow() { GetWindow("Replace Prefabs"); } void OnGUI() { EditorGUILayout.HelpBox("先在 Project 里选中你想替换的 Prefabs,然后点击按钮。", MessageType.Info); targetTerrain = (Terrain)EditorGUILayout.ObjectField("目标 Terrain", targetTerrain, typeof(Terrain), true); if (GUILayout.Button("替换选中 Prefab 实例为 Terrain 树")) { if (targetTerrain == null) targetTerrain = Terrain.activeTerrain; if (targetTerrain == null) { Debug.LogError("未指定 Terrain,也未检测到活动地形。"); return; } ReplacePrefabs(); } } void ReplacePrefabs() { Object[] selectedPrefabs = Selection.GetFiltered(typeof(GameObject), SelectionMode.Assets); if (selectedPrefabs.Length == 0) { Debug.LogWarning("你需要在 Project 面板中选择 Prefab!"); return; } TerrainData terrainData = targetTerrain.terrainData; Vector3 terrainPos = targetTerrain.transform.position; List newTrees = new List(terrainData.treeInstances); int count = 0; foreach (GameObject prefab in selectedPrefabs) { GameObject[] instances = FindObjectsOfType(); foreach (GameObject obj in instances) { if (PrefabUtility.GetCorrespondingObjectFromSource(obj) == prefab) { Vector3 worldPos = obj.transform.position; // 转换为 Terrain 本地坐标(归一化) Vector3 normalizedPos = (worldPos - terrainPos); normalizedPos.x /= terrainData.size.x; normalizedPos.z /= terrainData.size.z; normalizedPos.y = 0f; // 选择一个随机树种(TreePrototype) int prototypeIndex = Random.Range(0, terrainData.treePrototypes.Length); TreeInstance tree = new TreeInstance { position = new Vector3(normalizedPos.x, 0, normalizedPos.z), prototypeIndex = prototypeIndex, heightScale = 1f, widthScale = 1f, color = Color.white, lightmapColor = Color.white }; newTrees.Add(tree); Undo.DestroyObjectImmediate(obj); count++; } } } terrainData.treeInstances = newTrees.ToArray(); Debug.Log($"✅ 替换完成,处理了 {count} 个实例。添加到 Terrain 的 Tree 中。"); } }