本文为bbbbbbion(可以叫我六饼)原创总结,如有疏漏请各位拍砖留言。转载请尊重原作者成果,保留出处。
前言:
Unity3D 5.0之后API中取消了AddComponent(“”),因此若要使用string来获得对应的Type,可以使用Type.GetType(“”)。注意目前(2015-09-16)GetComponent(“”)仍然保留。
实现
采用官方XData方式的热更新方案。
游戏中的代码分为以下几类:
1:Support
用于游戏框架代码的编写,如:文件的读写、UI页面的开关、资源更新、逻辑热更assetbundle的更新等等。2:XData
用于和逻辑代码通信,通常XData代码会挂载在具体的obj上,在XData中通常会声明public的gameobject,实现对游戏内部物体的引用。3:逻辑热更代码
和XData脚本一一对应,通过反射的方式加载到gameobject上,另外通过获得XData中的public gameobject来实现逻辑代码的编写。
一般Support代码不需要热更,不过如果要热更的话,Support还需要分为SupportLogic和XDataSupport,本文以不需要热更为例。
support代码中提供editor模式和发布模式两种方式,保证editor模式下不需要检查逻辑代码的dll是否最新,可以直接运行工程和调试。
对于发布模式,发布的时候需要新建VS类库(建议.NET3.5),引用工程build出的Assembly-CSharp这个dll, build(建议Release模式下Build)后生成XXXXX.dll,建议后面加上新后缀.bytes,这样Unity会把它识别为AssetText,然后打包成assetbundle。
首次启动手机的时候把assetbundle解压到persistantDataPath下,然后通过本地注册表内的版本号和服务器上版本号比对,确定是否需要下载服务器上的assetbundle。然后WWW读取assetbundle,提取Type。
以上就是大概思路,另外VS类库打dll时,把不需要的引用给删除,确保dll最小。demo工程我已经放到百度云上了。
另外,以下附上,Unity官方提供的安卓代码热更的文档:
项目:Android平台代码增量更新
原则:在XData目录下的脚本不能涉及到序列化信息的更改,文件名,类名,类中所有能被序列化的field(哪些可以被序列化,参考网页serialization-in-unity和unity-serialization)。
编译步骤:
1、 编译Support的XData工程(refer: null; output:support.dll)
2、 编译Support的Logic工程(refer: support.dll; output:supportLogic.dll)动态加载
3、 单独编译本地代码的XData部分,移除Script(Logic)目录后编译(refer: support.dll; output:CSharp-Assembly.dll)
4、 编译本地代码的Logic部分(refer:support.dll, supportLogic.dll, CSharp-Assembly; output: CSharp-Assembly-Logic.dll)动态加载
Note
1、 保证四个dll的编译平台一致,统一用3.5
2、 保证CSharp-Assembly-Logic.dll和CSharp-Assembly.dll的宏定义一致
3、 关于AddComponent接口,如果参数是字符串,则只会在CSharp-Assembly中去查找对应的类;如果参数是Type,则只是调用该接口的方法所在的dll中去查找。
4、 关于程序报错可从以下方面去查找原因
- Logic类还没有加载,就被GetComponent,使用自然为空,例如子节点初始状态不是active的,在父节点中GetComponent是,子节点的Logic类还没有被初始化,因为XData类还没有调用Awake方法。(如果是这种情况,使用前判空,若为空,Add)
- 情况与上述类似,不一样的是,子节点的类是继承类。(Add的时候,注意要Add对应的子类,而不是声明的父类)
在A的Awake方法中GetComponent(B),其中B也是另一个被拆分的类,出现空引用,原因在于Awake方法是时序的,A被初始化时,B有可能还没有初始化(出现这种情况,保证B在前执行就行,调整XDataA和XDataB的Script Execution Order就行:Edit->Project Settings->Script Exection Order)
5、 关于脚本跟资源的依赖关系
- 脚本和AssetBundle,将资源带成AssetBundle包(1、脚本挂在资源上;2、脚本继承Scriptable,里面存数据,将其变成资源后打包),在load资源的时候,只会根据打包时生成的hashid去找对应的脚本。Hashid的计算是通过Assemblyname+Namespace+Classname计算出来的,跟序列化信息无关
- 在Editor状态的资源和脚本的依赖关系是跟序列化信息有关的,脚本和资源之间是通过guid来关联的,如果在外部修改了脚本的信息,返回Untiy Editor后会重新生成脚本的guid,那么相关的资源要重新attach脚本