美文网首页unity
GameObject.Instantate前后对字段赋值在生命周

GameObject.Instantate前后对字段赋值在生命周

作者: 雨落随风 | 来源:发表于2018-11-02 13:37 被阅读4次

    有些问题不去试试,不清不楚不方便搞事情,就像笔者今天做的这个备忘一样。

    写在前面

    • 这个笔记就是记录一个测试结果。
    • 测试的目的是确认一个字段 在 Resource.Load 后获得,并在Instantate前后 赋值,观察它在生命周期中的表现:

    实验流程

    1 .测试用c#类:

    using UnityEngine;
    
    public class TestInstantatLifeCycle : MonoBehaviour {
        GameObject go;
        void Update () {
            if (Input.GetMouseButtonDown(2))
            {
                TestLifecycle();
            }
            if (Input.GetKeyDown(KeyCode.R))
            {
                if (null != go)
                {
                    Debug.Log("重新加载Prefab");
                    TestLifecycle(true);
                }
            }
        }
        #region TestCode
        private void TestLifecycle(bool ifReload = false)
        {
            if (null == go || ifReload)
            {
                go = Resources.Load<GameObject>("一个预制体");
            }
    
            Debug.Log("加载资源!");
            TestChangeFieldValueAfterResourceLoad before = go.GetComponent<TestChangeFieldValueAfterResourceLoad>();
            Debug.Log((null == before) + ":" + (null == go));
            Debug.Log("1.赋值前 " + before.AA+":"+before.BB);
            before.AA = "After Resources.Load";
            before.BB = new TestRefrenceField();
            Debug.Log("2.赋值后 " + go.GetInstanceID() + " : " + before.AA + " : " + before.GetInstanceID()+":"+before.BB.BB); //编辑模式Resource加载后实例化前赋值字段会导致Resource目录下的预制物字段的值被修改。 运行模式下只修改内存值,app重开恢复默认。
            Debug.Log("实例化前");
            GameObject go_after = GameObject.Instantiate(go);
            Debug.Log("实例化了");
            TestChangeFieldValueAfterResourceLoad after = go_after.GetComponent<TestChangeFieldValueAfterResourceLoad>();      //引用的参数,如果没有标记可序列化,Resource.Load之后修改了数据在instantate时依旧加载默认值,不能序列化的类默认值null。
            Debug.Log("3.实例化后 " + go_after.GetInstanceID() + " : " + after.AA + " : " + after.GetInstanceID()+" : "+after.BB.BB); 
            after.AA = "xxx实例化后 ";
            after.BB = new TestRefrenceField() {BB="实例化后赋值" };
            Debug.Log("4.实例化后赋值 " + go_after.GetInstanceID() + " : " + after.AA + " : " + after.GetInstanceID()+" : "+after.BB.BB);  //实例化后赋值,Awake 里面用的还是初始值,Start里面会受到影响。 也就是Awake里面加载UI组件,start订阅事件,这样保证就算是事件被触发了,事件里面用到的数据也是最新的
        }
        #endregion
    }
    
    
    1. 游戏对象上将挂载的组件,含 2 个字段,一个值类型,一个引用类型。
    using UnityEngine;
    public class TestChangeFieldValueAfterResourceLoad : MonoBehaviour
    {
        public string AA = "初始值";
        public TestRefrenceField BB;
        private void Awake()
        {
            Debug.Log("Awake" + AA);
            BB = new TestRefrenceField()
            {
                BB="Inside and Awake"
            };
            Debug.Log(BB.BB);
        }
        void Start()
        {
            Debug.Log("Start" + AA);
            Debug.Log(BB.BB);
        }
    }
    public class TestRefrenceField
    {
        public string BB = "AOE";
    }
    
    1. 触发测试的方法,输出如下:


      Debug输出

    经验总结

    1. 常识性的结论:

      • Resource.Load 是将 Prefab 二进制文件 通过实例化方式加载到内存,尽管他们是对象了 ,但不会渲染出也不会执行生命周期函数。仅当使用 GameObject.Instantate 实例化后生命周期函数才开始运转。
      • 值类型和可序列化的引用类型,将会预先写入 Prefab 二进制内。所以 Resource.Load 出来是存在默认值的。
      • 常规的引用类型的字段,没有数据写入 Prefab 中,所以加载到内存时 该字段指向 null 。
    2. 本实验的结论:

      • 尝试在 Resource.Load 后对 string 类型的字段赋值,发现:
        a. 此修改对 Instantate 实例化的游戏对象有效。
        b. 此修改对原 Prefab 文件数据产生修改 ,Editor 模式下直接就保存在预制体中了,Runtime 则仅在内存中且对本次启动全局有效 ,软件重启数据恢复。
      • 尝试在 Resource.Load 后对 引用类型 的字段赋值,发现:
        a. 此修改对 Load 在内存的数据有效
        b. 此修改对 Instantate 动作无效,即:实例化的时候 该字段依旧为 null 。
      • 在 GameObject.Instantate 后对任意类型字段赋值,此修改对 Awake 函数中的引用(应用)无效,即保持默认值。
      • 在 GameObject.Instantate 后对任意类型字段赋值,此修改对 Start 函数中的引用(应用)开始产生影响。
      • 综上:对 Instantate 得到的游戏对象字段进行修改,务必在 Start 函数再对传入的数据进行处理,否则多半出现明明赋值了还报 null的情况
    3. 一直半解最可怕了,毕竟生命周期和脚本执行顺序混乱就会异常不断。要不Unity 整个 Execution order 干嘛?

    写到最后

    发现 Execution Order 设置的便捷方式,用的人少知道快捷方式的就更少了吧,没必要单独成文,强插一波吧:


    • 添加脚本?拖~
    • 移除脚本?拖~
    • 调整执行顺序?还是拖。
    • 还有这样的属性也不知好不好用?


    相关文章

      网友评论

        本文标题:GameObject.Instantate前后对字段赋值在生命周

        本文链接:https://www.haomeiwen.com/subject/xggqxqtx.html