Unity:如何在Editor脚本中调用StartCoroutine执行协程?
解决方案
要实现编辑模式和运行时都能通过按钮启动预制体生成,需要修改两处脚本,解决协程在编辑模式下的执行问题,同时确保地形数据初始化正确。
修改后的Mono脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public class InstantiateOnTerrain : MonoBehaviour { private float terrainWidth; private float terrainLength; private float xTerrainPos; private float zTerrainPos; public Terrain terrain; public GameObject prefab; public int numberOfObjects; public float instantiateTime; public float yOffset; void Start() { InitializeTerrainData(); StartCoroutine(GenerateObjectsOnTerrain()); } /// <summary> /// 初始化地形尺寸和位置数据 /// </summary> public void InitializeTerrainData() { if (terrain == null) { terrain = Terrain.activeTerrain; } if (terrain == null) return; terrainWidth = terrain.terrainData.size.x; terrainLength = terrain.terrainData.size.z; xTerrainPos = terrain.transform.position.x; zTerrainPos = terrain.transform.position.z; } /// <summary> /// 生成物体的协程方法(改为公开以便编辑器调用) /// </summary> public IEnumerator GenerateObjectsOnTerrain() { InitializeTerrainData(); if (terrain == null || prefab == null) { Debug.LogError("请指定地形或预制体!"); yield break; } for (int i = 0; i < numberOfObjects; i++) { float objOffsetX = prefab.transform.localScale.x / 2; float objOffsetZ = prefab.transform.localScale.z / 2; float randX = UnityEngine.Random.Range(xTerrainPos + objOffsetX, xTerrainPos + terrainWidth - objOffsetX); float randZ = UnityEngine.Random.Range(zTerrainPos + objOffsetZ, zTerrainPos + terrainLength - objOffsetZ); float yVal = terrain.SampleHeight(new Vector3(randX, 0, randZ)) + yOffset; GameObject objInstance; if (Application.isPlaying) { objInstance = Instantiate(prefab, new Vector3(randX, yVal, randZ), Quaternion.identity); } else { objInstance = PrefabUtility.InstantiatePrefab(prefab) as GameObject; if (objInstance != null) { objInstance.transform.SetPositionAndRotation(new Vector3(randX, yVal, randZ), Quaternion.identity); EditorUtility.SetDirty(objInstance); } } if (instantiateTime > 0) { yield return new WaitForSeconds(instantiateTime); } } if (!Application.isPlaying) { EditorUtility.SetDirty(terrain.gameObject); SceneView.RepaintAll(); } } }
修改后的Editor脚本
using System.Collections; using UnityEditor; using UnityEngine; [CustomEditor(typeof(InstantiateOnTerrain))] public class InstantiateOnTerrainEditor : Editor { public override void OnInspectorGUI() { DrawDefaultInspector(); InstantiateOnTerrain onTerrain = (InstantiateOnTerrain)target; if (GUILayout.Button("生成物体")) { if (Application.isPlaying) { onTerrain.StartCoroutine(onTerrain.GenerateObjectsOnTerrain()); } else { EditorCoroutine.StartCoroutine(onTerrain.GenerateObjectsOnTerrain(), onTerrain); } } } }
核心修改点
地形数据独立初始化:
- 把原Start方法里的地形数据提取成单独的
InitializeTerrainData方法,确保编辑模式下点击按钮时也能正确获取地形参数,不会因为Start没执行导致数据为空。 - 协程开头也调用这个方法,保证每次生成都用最新的地形状态。
- 把原Start方法里的地形数据提取成单独的
跨模式协程执行:
- 把协程改成公开方法,让编辑器脚本能直接调用。
- 编辑器脚本里判断当前是运行时还是编辑模式:运行时用Mono自带的StartCoroutine,编辑模式用Unity提供的
EditorCoroutine来跑协程。
编辑模式实例化优化:
- 用
PrefabUtility.InstantiatePrefab替代普通Instantiate,这样生成的物体还能和原预制体保持关联。 - 调用
EditorUtility.SetDirty标记修改,确保编辑模式下生成的物体能被保存到场景里。 - 生成完刷新场景视图,让效果立刻显示出来。
- 用
细节修复:
- 加了空值检查,避免没指定地形或预制体时崩溃。
- 修正了原按钮文字的拼写错误。
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

