Unity预设序列化的自动化方案

发表于2017-06-23
评论0 2.3k浏览

预设

在Unity中,我们会将重复使用的资源做成预设(Prefab)。预设上可以挂载脚本并在Aspector面板中指定可序列化的值便于配置。

在实际操作过程中,我们的脚本上需要配置的部分可能常常需要指定一些固定的组件。加入有这样的一个脚本:

像这样,rigBody和capsuleCollider如果在游戏运行时获取会有些消耗,而在Editor模式下又需要我们每次从Hierarchy面板拖动到Aspector面板来指定费时费力。而这一切我们可以通过脚本自动完成。我们在Monster脚本上添加如下接口:

1
2
3
4
5
public void DoSerialized()
{
    rigBody = GetComponent();
    capsuleCollider = GetComponent();
}

Apply

Unity提供了在预设Apply时对该预设进行操作的Attribute:InitializeOnLoadMethod

我们在Editor目录下创建一个脚本AssetHelper,实现该接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static void StartInitializeOnLoadMethod()
{
    // 注册Apply时的回调
    PrefabUtility.prefabInstanceUpdated = delegate(GameObject instance)
    {
        if(instance)
        SaveMonsterPrefab(instance);
    };
}   
 
static void SaveMonsterPrefab(GameObject instance)
{
    string prefabPath = AssetDatabase.GetAssetPath(PrefabUtility.GetPrefabParent(instance));
    if(!IsMonsterPrefab(prefabPath))
        return;
 
    Debug.LogFormat("SaveMonsterPrefab Path = {0}", prefabPath);
    Monster comp = instance.GetComponent();
    if (null == comp)
    {
        string msg = string.Format("{0} 缺少Monster组件", prefabPath);
        EditorUtility.DisplayDialog("Apply a Monster prefab!", msg, "OK");
        return;
    }
 
    comp.DoSerialized();
}
 
 
static bool IsMonsterPrefab(string path){
    if(path.Contains(MONSTER_FOLDER) && Path.GetExtension(path) == ".prefab")
        return true;
 
    return false;
}

以后创建Prefab的时候就可以直接添加一个Monster脚本,按一下Apply就行了。

保存

Apply的方法有个明显的缺点,那就是Prefab的改动有时候不会拖到Hierarchy中,而是直接修改,然后Ctrl+S保存,从而没有Apply过程。不用担心,即便这样Unity也有解决方案。

我们将AssetHelper类继承UnityEditor.AssetModificationProcessor类,并实现OnWillSaveAssets方法。这样就可以在资源保存时对资源做操作。实现细节如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static string[] OnWillSaveAssets(string[] paths){
    SaveMonsterPrefabs(paths);
    return paths;
}
 
static void SaveMonsterPrefabs(string[] paths)
{
    foreach (string path in paths)
    {
        if(!IsMonsterPrefab(path))
            continue;
 
        Debug.LogFormat("SaveMonsterPrefabs {0}", path);
 
        GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(path, typeof(GameObject));
        if(prefab == null)
        {
            Debug.LogWarning(string.Format("Can not load prefab {0}", path));
            continue;
        }
 
        GameObject go = UnityEngine.Object.Instantiate(prefab) as GameObject;
        Monster comp = go.GetComponent();
        if (null == comp)
        {
            Debug.LogWarning(string.Format("{0} 缺少Monster组件", path));
            continue;
        }
 
        comp.DoSerialized();
        PrefabUtility.ReplacePrefab(go, prefab);
        UnityEngine.Object.DestroyImmediate(go);
    }
}

这样一来对资源的修改都会做序列化了。

后记

本文所有源代码依然放在我的Github上,有兴趣的朋友可以下来自行参考。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引