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

Unity中ScriptableObject变量的OnValueChanged触发方法可行性问询

绝对可以实现!我平时做Unity项目的时候,经常用到几种不同的方式来实现这个需求,根据场景选就行,给你挨个捋一遍:

方法1:用Unity内置的OnValidate()方法

这是最简单直接的方案,Unity会在编辑模式下Inspector里修改值时自动调用,同时在脚本被加载、重置的时候也会触发,完美适配ScriptableObject的使用场景。

举个实际代码例子:

using UnityEngine;

[CreateAssetMenu(fileName = "MyData", menuName = "Custom/My Data")]
public class MyScriptableObject : ScriptableObject
{
    [SerializeField] private int _myValue;

    // 当Inspector里的值被修改时自动触发
    private void OnValidate()
    {
        // 调用你的自定义逻辑
        OnValueChanged();
    }

    private void OnValueChanged()
    {
        Debug.Log($"MyValue changed to: {_myValue}");
        // 这里写你需要执行的操作,比如更新关联数据、触发事件、校验值的合法性等
    }
}

⚠️ 注意点:

  • OnValidate()是在编辑模式下运行的,如果你的逻辑里有运行时专属代码(比如Instantiate),记得用if (Application.isPlaying)做判断,避免编辑器报错。
  • 不管是值类型(int、float)还是引用类型(比如另一个ScriptableObject),修改时都会触发这个方法。
方法2:属性包装+OnValidate()同步

如果想把逻辑封装在属性的setter里(比如做值的范围限制),可以用私有序列化字段配合公开属性,再通过OnValidate()同步逻辑:

using UnityEngine;

[CreateAssetMenu(fileName = "MyData", menuName = "Custom/My Data")]
public class MyScriptableObject : ScriptableObject
{
    [SerializeField] private int _myValue;
    public int MyValue
    {
        get => _myValue;
        set
        {
            // 先做值的校验,比如限制在0-100之间
            var clampedValue = Mathf.Clamp(value, 0, 100);
            if (_myValue != clampedValue)
            {
                _myValue = clampedValue;
                OnValueChanged();
            }
        }
    }

    private void OnValidate()
    {
        // 确保Inspector修改字段后,属性的校验逻辑也能触发
        MyValue = _myValue;
    }

    private void OnValueChanged()
    {
        Debug.Log($"MyValue updated to: {_myValue}");
    }
}

这样不管是代码里修改MyValue属性,还是Inspector里改_myValue字段,都会触发你的自定义逻辑。

方法3:自定义编辑器(精细控制场景)

如果需要更精细的控制(比如只监听某个特定字段的变化、做编辑器专属的UI交互),可以写一个自定义Editor类:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MyScriptableObject))]
public class MyScriptableObjectEditor : Editor
{
    private SerializedProperty _myValueProp;
    private int _previousValue;

    private void OnEnable()
    {
        // 获取序列化的目标字段
        _myValueProp = serializedObject.FindProperty("_myValue");
        // 记录初始值用于后续对比
        _previousValue = _myValueProp.intValue;
    }

    public override void OnInspectorGUI()
    {
        // 更新序列化对象的状态
        serializedObject.Update();

        // 绘制默认的Inspector字段
        EditorGUILayout.PropertyField(_myValueProp);

        // 检查值是否发生变化
        if (_myValueProp.intValue != _previousValue)
        {
            // 调用目标ScriptableObject的自定义方法
            ((MyScriptableObject)target).OnValueChanged();
            // 更新记录的旧值
            _previousValue = _myValueProp.intValue;
        }

        // 应用所有修改
        serializedObject.ApplyModifiedProperties();
    }
}

这种方式适合需要监听特定字段、或者在值变化时刷新编辑器预览窗口等场景。

额外小提醒

如果需要在值变化时通知场景中的MonoBehaviour对象,可以在OnValueChanged()里用UnityEvent或者自定义事件系统,但要注意编辑模式下的事件触发——接收方可能需要加[ExecuteInEditMode]标记,或者用EditorApplication.update来监听变化。

内容的提问来源于stack exchange,提问作者Tung Le Minh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:58:48