Unity编辑模式SendMessage警告:脚本需兼容编辑与运行时
问题背景
我编写的Unity脚本需要同时兼容编辑模式和运行时,但在编辑模式下频繁收到以下警告:
SendMessage cannot be called during Awake, CheckConsistency, or OnValidate (Cube(Clone): OnDidAddComponent)
UnityEngine.GameObject:AddComponent()
GenerateLines:CreateCubeAtPosition (UnityEngine.Vector3) (at Assets/Scripts/GenerateLines.cs:91)
GenerateLines:GeneratePositions () (at Assets/Scripts/GenerateLines.cs:66)
GenerateLines:GenerateIfNeeded () (at Assets/Scripts/GenerateLines.cs:36)
GenerateLines:OnValidate () (at Assets/Scripts/GenerateLines.cs:23)
我在Unity论坛查阅过相关帖子,但未找到可行解决方案。能否通过编写Editor脚本替代[ExecuteInEditMode]属性,实现脚本在编辑模式下的正常工作?
原脚本代码
using System.Collections; using System.Collections.Generic; using UnityEngine; [ExecuteInEditMode] public class GenerateLines : MonoBehaviour { public List<Vector3> positions = new List<Vector3>(); public int amountOfPositions = 30; public int minRandRange, maxRandRange; public bool randomPositions = false; public bool generateNewPositions = false; public GameObject cubePrefab; // 若需复用逻辑,可分配已附加CubeScript的预制体 void Awake() { GenerateIfNeeded(); } private void OnValidate() { GenerateIfNeeded(); } private void GenerateIfNeeded() { // 运行模式下停止执行(可选) if (Application.isPlaying) { return; } if (generateNewPositions || (positions.Count == 0 && amountOfPositions > 0)) { GeneratePositions(); generateNewPositions = false; // 重置标志避免重复生成 } } private void GeneratePositions() { // 销毁现有子物体去重 foreach (Transform child in transform) { DestroyImmediate(child.gameObject); // 编辑模式专用销毁方法 } positions.Clear(); for (int i = 0; i < amountOfPositions; i++) { Vector3 position; if (randomPositions) { var randPosX = UnityEngine.Random.Range(minRandRange, maxRandRange); var randPosY = UnityEngine.Random.Range(minRandRange, maxRandRange); var randPosZ = UnityEngine.Random.Range(minRandRange, maxRandRange); position = new Vector3(randPosX, randPosY, randPosZ); } else { position = new Vector3(i, i, i); } positions.Add(position); CreateCubeAtPosition(position); } } private void CreateCubeAtPosition(Vector3 position) { GameObject cube; if (cubePrefab != null) { cube = Instantiate(cubePrefab, position, Quaternion.identity, this.transform); } else { cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.position = position; cube.transform.localScale = Vector3.one; cube.transform.SetParent(this.transform, false); } cube.AddComponent<CurvedLinePoint>(); } }
解决方案:用Editor脚本替代[ExecuteInEditMode]
1. 改造原运行时脚本
移除[ExecuteInEditMode]属性,拆分编辑模式与运行时逻辑:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GenerateLines : MonoBehaviour { public List<Vector3> positions = new List<Vector3>(); public int amountOfPositions = 30; public int minRandRange, maxRandRange; public bool randomPositions = false; public bool generateNewPositions = false; public GameObject cubePrefab; void Awake() { // 仅运行时执行生成逻辑 if (Application.isPlaying) { GenerateIfNeeded(); } } private void GenerateIfNeeded() { if (generateNewPositions || (positions.Count == 0 && amountOfPositions > 0)) { GeneratePositions(); generateNewPositions = false; } } private void GeneratePositions() { foreach (Transform child in transform) { // 根据模式选择销毁方法 if (Application.isPlaying) { Destroy(child.gameObject); } else { DestroyImmediate(child.gameObject); } } positions.Clear(); for (int i = 0; i < amountOfPositions; i++) { Vector3 position; if (randomPositions) { var randPosX = UnityEngine.Random.Range(minRandRange, maxRandRange); var randPosY = UnityEngine.Random.Range(minRandRange, maxRandRange); var randPosZ = UnityEngine.Random.Range(minRandRange, maxRandRange); position = new Vector3(randPosX, randPosY, randPosZ); } else { position = new Vector3(i, i, i); } positions.Add(position); CreateCubeAtPosition(position); } } private void CreateCubeAtPosition(Vector3 position) { GameObject cube; if (cubePrefab != null) { cube = Instantiate(cubePrefab, position, Quaternion.identity, this.transform); } else { cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.position = position; cube.transform.localScale = Vector3.one; cube.transform.SetParent(this.transform, false); } cube.AddComponent<CurvedLinePoint>(); } }
2. 创建专属Editor脚本
在Assets/Editor目录下新建GenerateLinesEditor.cs,专门处理编辑模式逻辑:
using UnityEditor; using UnityEngine; [CustomEditor(typeof(GenerateLines))] public class GenerateLinesEditor : Editor { private GenerateLines targetScript; private void OnEnable() { targetScript = (GenerateLines)target; } public override void OnInspectorGUI() { // 绘制默认Inspector界面 DrawDefaultInspector(); // 添加手动生成按钮 if (GUILayout.Button("Generate Positions")) { GenerateInEditMode(); } // 监听属性变更,自动触发生成 EditorGUI.BeginChangeCheck(); if (EditorGUI.EndChangeCheck() || targetScript.generateNewPositions) { if (!Application.isPlaying) { GenerateInEditMode(); targetScript.generateNewPositions = false; EditorUtility.SetDirty(targetScript); // 标记对象为已修改,确保Unity保存变更 } } } private void GenerateInEditMode() { if (targetScript.generateNewPositions || (targetScript.positions.Count == 0 && targetScript.amountOfPositions > 0)) { // 记录撤销操作,支持Ctrl+Z Undo.RecordObject(targetScript, "Generate Positions"); // 销毁子物体时同步记录撤销 foreach (Transform child in targetScript.transform) { Undo.DestroyObjectImmediate(child.gameObject); } targetScript.positions.Clear(); for (int i = 0; i < targetScript.amountOfPositions; i++) { Vector3 position; if (targetScript.randomPositions) { var randPosX = Random.Range(targetScript.minRandRange, targetScript.maxRandRange); var randPosY = Random.Range(targetScript.minRandRange, targetScript.maxRandRange); var randPosZ = Random.Range(targetScript.minRandRange, targetScript.maxRandRange); position = new Vector3(randPosX, randPosY, randPosZ); } else { position = new Vector3(i, i, i); } targetScript.positions.Add(position); CreateCubeAtPositionWithUndo(position); } } } private void CreateCubeAtPositionWithUndo(Vector3 position) { GameObject cube; if (targetScript.cubePrefab != null) { cube = (GameObject)PrefabUtility.InstantiatePrefab(targetScript.cubePrefab); cube.transform.position = position; cube.transform.rotation = Quaternion.identity; Undo.RegisterCreatedObjectUndo(cube, "Create Cube"); cube.transform.SetParent(targetScript.transform, false); } else { cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.position = position; cube.transform.localScale = Vector3.one; Undo.RegisterCreatedObjectUndo(cube, "Create Cube"); cube.transform.SetParent(targetScript.transform, false); } // 添加组件时记录撤销 CurvedLinePoint pointComp = cube.AddComponent<CurvedLinePoint>(); Undo.RegisterCreatedObjectUndo(pointComp, "Add CurvedLinePoint"); } }
方案优势
- 彻底规避
OnValidate中执行复杂操作引发的警告,Editor脚本逻辑在Unity Inspector回调中执行,不受Awake/OnValidate阶段的SendMessage限制 - 支持Unity撤销系统,提升编辑流程的容错性
- 编辑模式与运行时逻辑完全分离,减少模式冲突风险
内容的提问来源于stack exchange,提问作者Daniel Lip

