项目中出现的问题
载入一个prefab并实例化后,获取该prefab上挂载的一个MonoBehaviour组件,例如:
public class Foo: MonoBehaviour
{
public bool newValue = true;
void Awake(){
Debug.Log(newValue);
}
}
此时,Awake中的Log输出的是 false。
分析原因
Foo组件中的newValue
默认为true
,但是Awake时为false
。这说明序列化出了问题。但是打开该prefab进入编辑模式观察,发现 newValue
的对勾是选中的。然后用文本编辑器打开该prefab,搜索newValue
,并没有搜索到。这说明Foo组件在prefab中没有被正确的序列化。在prefab的编辑模式下看到的,newValue
上的对勾,其实不是反序列化的结果,而是构造出来的Foo对象中的newValue
的默认值。
结论
如果仅仅是修改了脚本,添加了新的可序列化的成员,刷新编辑器,甚至打开包含该脚本的prefab,都不会将新添加的成员值序列化到prefab中。只有当打开prefab编辑,且修改了prefab中的某一个属性时,prefab才会重新序列化。这样新添加的成员值才会被序列化到prefab文件中。
思考
Prefab编辑模式,只有当对prefab做出任何修改后才重新保存prefab文件,这是合理的。但是,打开prefab进行编辑时,Unity并没有识别出prefab中包含的脚本已经做了修改。我觉得作为引擎是可以做到这点的,只要付出一点点的代价,比如将prefab包含的脚本组件的md5值记录在prefab文件中,当打开prefab编辑时计算一下引用的脚本的md5,和保存在prefab中的md5值进行对比。另一个问题是,当脚本修改时,包含该脚本的所有的prefab和scene,是否需要自动刷新?我觉得全部自动刷新的代价比较大,且脚本修改是很频繁的动作,确实不能去自动刷全部。另外Unity是否保存了脚本被哪些prefab/scene引用的信息?我觉得没有。因为这样要保存的信息很多,且容易失去同步。但是可控的手动刷新应该被支持。
解决方案
当某个脚本被若干prefab/scene包含时,修改这个脚本就要注意这个问题了。一般来说,项目中的资源会有比较良好的组织,虽然Unity引擎本身并没有去管理脚本被哪些prefab/scene包含的问题。但是项目自己知道这个脚本可能被哪些目录下的一组prefab/scene包含。这样就可以创建一个工具,去有选择的刷新那些会被影响到的prefab/scene。
如果prefab数量不多,可以手动打开,任意改变某个组件的某个值再改回去,退出prefab编辑。这样该prefab就会重新序列化。
感悟
虽然这个问题是一个并不复杂的小问题,但是当在一个较大型的项目中,却容易引起特别隐晦的bug,会浪费大量时间去修改bug。因此需要在项目中精心管理资源和代码,规范工作流程,辅助以工具,将bug扼杀在萌芽状态,这是一个游戏开发主程的基本修养。