昨晚上有了dll 热更新的想法,今天做了一天的实验,也遇到些坑,这里总结下
工作环境: U3D5.3.2 + vs2010 +mono
下面要模拟的是一个登陆环境,点击按钮,就加载一个iGameObjec的Item, Item 上得到更新的文本内容。具体如下图
-LoginPanel(动态加载脚本LoginUIc.cs)
-itemRoot
-Item(动态加载的Item.cs) 并加载动态的脚本
1> 程序集管理
using UnityEngine;
using System.Collections;
using System;
public class AssemblyLoader : MonoBehaviour {
private static readonly string DLL_URL ="file:///" + Application.dataPath+"/Plugins/ExtDll.assetbundle";
System.Reflection.Assembly assembly;
void Awake(){
StartCoroutine(loadDllScript());
}
private IEnumerator loadDllScript()
{
WWW www = new WWW(DLL_URL);
yield return www;
AssetBundle bundle = www.assetBundle;
TextAsset asset = bundle.LoadAsset("ExtDll",typeof(TextAsset)) as TextAsset;
assembly = System.Reflection.Assembly.Load(asset.bytes);
yield return null;
if (assembly != null) {
Type type = assembly.GetType ("DllManager");
Component com = gameObject.AddComponent(type);
System.Reflection.MethodInfo methodInfo = type.GetMethod ("InitAssembly");
object[] obj = new object[]{assembly};
methodInfo.Invoke (com, obj); //反射调用DllManager
Debug.Log ("dll load suc");
}
}
}
2> Dll管理与加载
using UnityEngine;
using System.Collections;
using System;
public class DllManager:MonoBehaviour{
public static DllManager Instance;
public System.Reflection.Assembly assembly;
void Awake(){
Instance = this;
}
public void InitAssembly(System.Reflection.Assembly assembly){
this.assembly = assembly;
AddCompotent(gameObject, "WndManager");
}
public Component AddCompotent(GameObject prefab, string compotentName){
//只有android进行dll热更处理
if (Application.platform == RuntimePlatform.Android) {
if (assembly != null) {
Type type = assembly.GetType (compotentName);
if (prefab != null) {
Debug.Log ("---------->add momo suc");
prefab.AddComponent(type);
}
return null;
}
} else {
Component com = prefab.GetComponent (compotentName);
if(com == null){
Type type = Type.GetType (compotentName);
prefab.AddComponent (type);
}
return com;
}
return null;
}
}
3> 逻辑代码和UI的实现
这里窗口管理器只是一个模拟加载LoginUI的过程,具体的会有自己的一套窗口管理~~勿喷
using UnityEngine;
using System.Collections;
public class WndManager : MonoBehaviour {
// Use this for initialization
void Start () {
DllManager.Instance.AddCompotent (gameObject, "LoginUI");
}
}
下面来看看LoginUI做了些什么
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class LoginUI : Momo {
string msg;
public Text text;
public Button btn;
public GameObject itemRoot;
protected override void Awake ()
{
base.Awake ();
}
protected override void InitPanelCompotent ()
{
base.InitPanelCompotent ();
text = transform.FindChild ("Text").GetComponent<Text>();
text.text = "andorid modify ";
btn = transform.FindChild ("Button").GetComponent<Button>();
btn.onClick.AddListener(OnClick);
itemRoot = transform.FindChild ("itemRoot").gameObject;
Debug.Log ("LoginUI InitPanelCompotent");
}
public void OnClick(){
LoginManager.Instance.data = new Data (text.text, text.text);
msg = "name = "+ LoginManager.Instance.data.name+" , password = " + LoginManager.Instance.data.password;
Object res = Resources.Load ("Item");
GameObject item = GameObject.Instantiate (res, Vector3.zero, Quaternion.identity) as GameObject;
item.transform.parent = itemRoot.transform;
item.transform.localPosition = Vector3.zero;
DllManager.Instance.AddCompotent(item, "Item");
}
protected override void InitPanelData ()
{
base.InitPanelData ();
Debug.Log ("LoginUI InitPanelData");
AddFun ();
}
protected void AddFun(){
Debug.Log ("this is add fun");
}
void OnGUI(){
GUI.Label(new Rect(0, 0, Screen.width, Screen.height), msg);
}
}
LoignUI 继承了Momo的一个类。这个类定义好了一个初始化的流程,也就是初始化panel 数据, 加载panel组件,所有的UI必须这样来完成(其实lua也有这个过程,不停的find,而这里我们可以展望一个工具,这个工具会帮我们初始化这部分工作。)
using UnityEngine;
using System.Collections;
public class Momo : MonoBehaviour {
protected virtual void Awake(){
InitPanelCompotent ();
InitPanelData ();
}
protected virtual void InitPanelCompotent(){
Debug.Log ("moo InitPanelCompotent");
}
protected virtual void InitPanelData(){
Debug.Log ("moo InitPanelData");
}
}
其他脚本略(下载后面的脚本集合,因为后面还有更重要的要写,这篇又会太长)
4> 打包dll+打包Assetbundle
ok 完成了上诉的代码编写工作,现在就进行dll的生成
外部库引入的时候注意UnityEngine.UI 的路径是不同于UnityEngine.dll的。自己在安装目录下搜就知道了。
这里需要注意的是:.net必须选为3.5 否则无法进行,低于3.5的unity的库文件找不到,高了的unity运行会报错,所以必须是3.5
然后点击生成就可以生成我们需要的dll了,然后去你工程目录下的 bin/debug/ExtDll .dll工程
然后将这个ExtDll.dll放到unity中的Plugin下并改名为ExtDll.bytes 然后打包成AssetBundle. 注意Assetbundle的平台设置
ok。打出来的ExtDll.assetbundle 就安静的躺在了Plugin下面了。
5>测试
点击button 如果出现如下的表示成功了
可能遇到的坑~~~不过在我这边都是被解决了
限制1:在Android手机里动态加载dll不能使用Assembly.LoadFile(string path),只能使用Assembly.Load(byte[] rawAssembly)这个接口,所以要自己想办法先读出来。
限制2:动态加载的脚本不能在编辑器里挂在prefab上。
限制3:如果脚本在动态dll里,调用AddComponent()挂此脚本上prefab上时不能使用AddComponent(“SomeScript”)的方式调用,要用AddComponent(Type.GetType(“SomeScript”))。
限制4:在动态dll里使用[RequireComponent(typeof(SomeScript))]无效,所以不能使用。
====================================================================================================================================补充一下vs2010 中dll 的生成步骤
新建项目
已安装模板(这里选择C# /Windows/类库)点击确定
删掉默认创建的那个文件,拷贝UnityEngine.dll 和 你需要的**.cs文件到工程目录中
右键添加 / 添加现有项 把cs文件导入到工程
右键添加 / 添加引用把 UnityEngine.dll 文件加入到类库中
最后右键生成,如果没有报错那么在你的debug下就有你刚刚生成的***.dll文件