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

拆分MonoBehaviour编辑器与构建逻辑:分部类是否为最优方案?

Unity编辑器与运行时逻辑拆分的最佳实践

分部类的可行性与性能开销

  • 分部类(partial class)完全可行:可以将运行时核心逻辑和OnDrawGizmo()、OnValidate()这类编辑器专属回调拆分为多个.cs文件(比如PlayerController.cs放运行时逻辑,PlayerController.Editor.cs放编辑器方法)。编译后这些分部文件会合并为同一个类,私有字段可直接访问,无需额外编写getter。
  • 性能无额外开销:分部类只是C#的编译语法糖,最终生成的IL代码与单文件编写的类完全一致,不会带来任何性能损耗。
  • 注意局限:分部类必须属于同一个程序集,因此编辑器相关的分部文件不能放在Editor文件夹(该文件夹代码编译到独立的Editor程序集)。需配合#if UNITY_EDITOR指令包裹编辑器方法,确保真机打包时这部分代码被自动剔除。

行业通用解决方案

1. 官方推荐:CustomEditor + SerializedProperty

这是Unity生态中最常用的分离方式,完全隔离运行时与编辑器代码:

  • 创建对应MonoBehaviour的Editor脚本,放在Editor文件夹下(仅在编辑器环境编译)。
  • 通过SerializedProperty访问目标类的私有字段(无需手动写getter),Unity会自动处理序列化逻辑。
  • 示例代码:
[CustomEditor(typeof(PlayerController))]
public class PlayerControllerEditor : Editor
{
    private SerializedProperty _debugMoveSpeed;

    void OnEnable()
    {
        _debugMoveSpeed = serializedObject.FindProperty("_moveSpeed");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(_debugMoveSpeed, new GUIContent("Debug Move Speed"));
        serializedObject.ApplyModifiedProperties();
    }

    [DrawGizmo(GizmoType.Selected | GizmoType.Active)]
    static void DrawPlayerGizmo(PlayerController target, GizmoType type)
    {
        var serializedObj = new SerializedObject(target);
        float speed = serializedObj.FindProperty("_moveSpeed").floatValue;
        Gizmos.DrawWireSphere(target.transform.position, speed * 0.5f);
        serializedObj.Dispose();
    }
}

优点:运行时代码完全纯净,编辑器逻辑独立打包,无冗余代码。

2. 条件编译块

无需拆分文件,用#if UNITY_EDITOR包裹编辑器专属代码,实现逻辑隔离:

public class PlayerController : MonoBehaviour
{
    [SerializeField] private float _moveSpeed;

    // 运行时逻辑
    void Update()
    {
        transform.Translate(Vector3.forward * _moveSpeed * Time.deltaTime);
    }

#if UNITY_EDITOR
    // 编辑器专属方法
    void OnValidate()
    {
        _moveSpeed = Mathf.Clamp(_moveSpeed, 1f, 10f);
    }

    void OnDrawGizmosSelected()
    {
        Gizmos.DrawWireSphere(transform.position, _moveSpeed * 0.5f);
    }
#endif
}

优点:保持类的完整性,无需额外文件;真机打包时编辑器代码会被自动移除。

3. 编辑器辅助组件

创建一个标记[ExecuteInEditMode]的独立MonoBehaviour,挂载到同一个GameObject上,专门处理编辑器回调:

[ExecuteInEditMode]
[RequireComponent(typeof(PlayerController))]
public class PlayerControllerEditorHelper : MonoBehaviour
{
    private PlayerController _controller;

    void OnEnable()
    {
        _controller = GetComponent<PlayerController>();
    }

    void OnDrawGizmosSelected()
    {
        // 通过SerializedObject访问私有字段
        var serializedObj = new SerializedObject(_controller);
        float speed = serializedObj.FindProperty("_moveSpeed").floatValue;
        Gizmos.DrawWireSphere(transform.position, speed * 0.5f);
        serializedObj.Dispose();
    }

    void OnValidate()
    {
        var serializedObj = new SerializedObject(_controller);
        var speedProp = serializedObj.FindProperty("_moveSpeed");
        speedProp.floatValue = Mathf.Clamp(speedProp.floatValue, 1f, 10f);
        serializedObj.ApplyModifiedProperties();
    }
}

优点:彻底分离运行时与编辑器代码,主类无需包含任何编辑器逻辑;缺点是会多一个组件,需额外管理。

方案选择建议

  • 追求代码纯净、符合官方规范:选CustomEditor + SerializedProperty。
  • 希望保持类结构统一、不想额外写Editor脚本:选分部类配合条件编译。
  • 快速实现逻辑隔离、不想拆分文件:选条件编译块。

内容的提问来源于stack exchange,提问作者Rastle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:37:51