【Unity热更新】C#反射动态获取类属性及方法详解

发表于2018-06-20
评论0 2.9k浏览
作为开发者正常更新游戏资源是会选择使用热更新的方式,只让它下载我们更新的资源包,用assetBundle打包资源的方式是可以的,但其中有个最大的例外,那就是脚本。

虽然assetBundle支持直接将脚本或者DLL打包在内,但没有经过unity编译的话,assetBundle中的脚本是访问不到unity总工程中的脚本的,因为他们已经完全分离在了两个程序集,用C#加载程序集的方式导入DLL的话是可行的,但即便如此,想要在assetBundle脚本与unity总工程脚本之间进行通信也还是会出现问题的。

例如我从assetBundle打包的DLL中取出的一个类NewlyAdded.cs(更新的资源包中的脚本),把他add到场景某一物体之上后,想要获取到场景物体Player上的Control.cs(发布时存在于项目中的脚本)脚本中的float变量Health,然后再调用他的方法MassHealing(float health)。

按照正常方式:
//NewlyAdded.cs  
GameObject _player = GameObject.Find("Player").gameObject;  
float _f = _player.GetComponent<Control>().Health;  
_player.GetComponent<Control>().MassHealing(100.0f); 

如果这样做的话,我的NewlyAdded.cs脚本在发布DLL的时候就无法编译通过,因为在这段代码里他找不到Control这个类,那么只有将Control.cs也放到一起打包,这样的话虽然编译通过成功发布成DLL了,但打包成assetBundle发到客户端运行的时候你会发现GetComponent<Control>()取到的是一个奇怪的类(因为Control类在原项目和新的资源包中都存着),事实上因为被分离在了两个程序集外的原因他这里所取到的是在发布NewlyAdded.cs 的DLL那个资源包里的Control类,就算本地有同名类,他是不会替换的。

解决这个问题的话,可以用到C#的反射:
//NewlyAdded.cs  
float _f;  
//取Player上的所有脚本  
MonoBehaviour[] monos = GameObject.Find("Player").GetComponents<MonoBehaviour>();  
for (int i = 0; i < monos.Length; i++)  
{  
    //筛选出其中的Control脚本  
    if (monos[i].GetType().ToString().EndsWith("Control"))  
    {  
        //获取Health字段的值  
        _f = (float)monos[i].GetType().GetField("Health").GetValue(monos[i]);  
        //执行其公有方法MassHealing  
        monos[i].GetType().GetMethod("MassHealing", BindingFlags.Public).  
            Invoke(null, new System.Object[] { 100.0f});  
        break;  
    }  
}  

这样的话,在编译的时候就可以完全的通过。


如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引