Unity Scene 数据迁移研究

背景

项目(手游)进行到中后期,在进行代码重构过程中,难免会遇到一些与数据相关的问题。

例如我今天遇到的一个:Unity Scene 中存在了一些关卡数据,这些关卡数据是由关卡设计者放在场景中的物体上配置的(Monobehavior),借由场景进行数据的保存。

当然,如果关卡设计不是使用 unity scene 作为媒介来保存,可能就不存在这个问题了。

言归正传,重构(Refactor)过程中,我需要将 Monobehavior 组件中散乱存储的数据项,集中到一个数据结构中,做一个聚合,让代码结构更加优化。此重构的优化目标此处不做讨论。

假设原先有 十几个场景,每个场景有十几个保存数据的 Monobehavior,如何将原来的数据迁移到新的结构中呢?

Unity 中对于重构的支持

Unity 通过 Monobehavior 中的 SerializeFiled 属性标记需要序列化存储的成员。然而成员变量名字的修改,会导致序列化数据的丢失。原因是识别序列化数据和对应成员是使用成员的名字来进行的,改名会导致 Unity 认为你删除了一个旧的成员,而添加了一个新的成员。

简单重命名可以使用 以下方法来避免数据丢失:

_playerPrefab 修改为 _newPlayerPrefab

//改名之前
[SerializeField]
private GameObject _playerPrefab;

//改名之后
[SerializeField]
[UnityEngine.Serialization.FormerlySerializedAs("_playerPrefab")]
private GameObject _newPlayerPrefab;

然而对于这样的修改:

[SerializeField]
private float _a;
[SerializeField]
private float _b;

_a 和 _b 字段聚集到一个类 AB 中

[System.Serializable]
public class AB
{
    public float A;
    public float B;
}

最后需要这样的存储方式:

[SerializeField]
private AB _ab;

暂时还未找到方便的 Unity 内置提供解决方案。
各位看官如果知道,可以指出来~(虚心求教)

解决思路

  • 遍历所有场景
  • 遍历所有目标脚本
  • 将存储在原来位置的数据放到目标结构中
  • 存储修改后数据

过程

思路很清楚了,不过过程是艰辛的。(似乎是似曾相识是吗?)

遍历所有场景

    public static string[] FindAllScene(string scenePath)
    {
        return  System.IO.Directory.GetFiles(scenePath, "*.unity");
    }

遍历所有目标脚本

    public static T[] FindAllComponent<T>() where T : Object
    {
        return Resources.FindObjectsOfTypeAll<T>();
    }

将存储在原来位置的数据放入目标结构中

    public static void MoveData(MonoBehaviour ab)
    {
        SerializedObject serializedObject = new SerializedObject(ab);
        serializedObject.FindProperty("_ab.A").floatValue = serializedObject.FindProperty("_a").floatValue;
        serializedObject.FindProperty("_ab.B").floatValue = serializedObject.FindProperty("_b").floatValue;
    }

存储修改后数据

这个部分包含了两个部分,一个是在数据移动的方法中添加一个调用:

serializedObject.ApplyModifiedProperties();

添加后:

    public static void MoveData(MonoBehaviour ab)
    {
        SerializedObject serializedObject = new SerializedObject(ab);
        serializedObject.FindProperty("_ab.A").floatValue = serializedObject.FindProperty("_a").floatValue;
        serializedObject.FindProperty("_ab.B").floatValue = serializedObject.FindProperty("_b").floatValue;
        serializedObject.ApplyModifiedProperties();
    }

另一部分则是场景的保存:

EditorSceneManager.SaveScene(scene);

最后给出最终的完成代码:

    public static void Move()
    {
        EditorSceneManager.SaveOpenScenes();
        string[] paths = System.IO.Directory.GetFiles("Assets/Maps", "*.unity");
        for (int i = 0; i < paths.Length; i++)
        {
            Scene scene = EditorSceneManager.OpenScene(paths[i]);
            BoostArea[] scripts = Resources.FindObjectsOfTypeAll<BoostArea>();
            for (int j = 0; j < scripts.Length; j++)
            {
                MoveData(scripts[j]);
            }
            EditorSceneManager.SaveScene(scene);
        }
    }

注意几点:

  1. 使用 EditorUtility.SetDirty() 是不能正确标记场景被修改过的。具体看文档
  2. 使用 serializedObject.ApplyModifiedProperties(); 会使得脚本所属的 prefab 也会被修改保存
  3. 经过测试 EditorSceneManager.MarkSceneDirty 也不能完成这个多场景遍历同时保存修改的操作。(没有使用 serializedObject.ApplyModifiedProperties(),而是直接使用脚本的实例相互赋值)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值