You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity编辑模式SendMessage警告:脚本需兼容编辑与运行时

Unity编辑模式下SendMessage警告的解决与Editor脚本替代方案

问题背景

我编写的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 05:35:55