Unity5的AssetBundle与NGUI的坑

Unity5的AssetBundle机制相较于以前的改动非常大,它把以前繁杂的push pop依赖和各种自定义名称的步骤全简化了~具体新的用法在这里我就不提了,官方的Demo或是在网上都能看到新机制的用法,这里我只想说下我遇到的新AssetBundle与NGUI的坑。

这个坑很容易发现,当UIFont或是UIAtlas与使用它们的Prefab不在一个Bundle里时,加载Prefab就会出现scripit misssing的问题:

在Editor下 unity可能还会找到原代码给你Fixing了,但是在实机上是百分百会导致UI显示不出来的。虽然我可以把UIAtlas或UIFont全部扔到prefab的bundle里,但是如果有很多prefab引用同一个UIAtlas或UIFont的话,那bundle的大小就不忍直视了。所以有没有一种方法可以继续保持prefab对UIAtlas或UIFont的依赖关系且不会出错呢?

找解决方案之前,我们得先了解出错的原因,NGUI官网上有人总结得很精辟,我直接引用了:“Loading will fail if exist game object that have component with a script property type and value is prefab's script”,需要给他补充一点就是,只有2个prefab不在同一AssetBundle时loading才会出问题。大概就是下图这样一个脚本,引用了另一个AssetBundle的Prefab(即Sphere)。




好了,了解出问题的本质之后,我们来试着找一下解决方法


1、NGUI官网上有人给出了一种方法,就是修改NGUI底层。把代码里脚本属性全换成GameObject。

如果以前是Atlas

class UISprite
{
  [SerializeField] UIAtlas mAtlas; // It's prefab's script. Not works for AssetBundle
}
那么就把它替换成GameObject,然后通过GetComponent<UIAtlas>()来获得UIAtlas
class UIScript
{
  [SerializeField] GameObject mAtlasPrefab; // for AB, use mAtlasPrefab.GetComponent<UIAtlas>()...
}

这个问题确实可以从本质上解决因为自定义序列化脚本带来的问题,不过改NGUI底层实在是个不小的工作量,对应类的Inspector编辑器都要修改,所以我稍微试了一下之后马上就放弃了。



2、同事告诉我一种动态赋值的方法,因为我们在做UISprite时不给UIAtlas赋值的话,unity是不会报错的,所以我们可以写一个脚本,当运行时去动态加载对应的Atlas的Bundle,然后动态给UISprite附上UIAtlas以及spriteName。

具体做法是脚本里可以设置BundleName,AtlasName,SpriteName,以及要赋值的UISprite。

然后我们通过BundleName从一个预先设定好的位置(newPathName)取出atlas的AssetBundle,然后根据AtlasName来load出我们需要的Atlas的prefab赋值给m_sprite,然后把spriteName附上,代码大致如下:

AssetBundle bundle = AssetBundle.CreateFromFile(newPathName); 
m_atlas = bundle.LoadAsset<GameObject>(assetName); 
m_sprite.GetComponent<UISprite>().atlas = m_atlas.GetComponent<UIAtlas>();  
m_sprite.GetComponent<UISprite>().spriteName = m_spriteName;  
bundle.Unload(false);
把这个脚本挂在面板上,设置好各种名称,然后把对应Sprite的UIAtlas设为None(这步是这个方法的关键),打包。加载时,脚本会动态把UIAtlas赋值上去,成功运行。

不过这种方法的缺点也是很明显的,要在打AssetBundle包之前把UIAtlas设为None,且挂上脚本手动输入名称。这会很大地影响UI界面的开发效率,而且一个面板里公共的Atlas如果很多的话,那得挂多少个动态加载Atlas的脚本,我还没算公共的UIFont呢。只能说这个方法可以绕过问题,但是不适于处于开发阶段的项目。

3、最终解决方案

后来认真想了一下问题的本质,引用的prefab要在同一个bundle下,而像material、texture这类component可以分到不同bundle下。那我是不是可以把Atlas的三部分(prefab、texture、material)分开,让prefab存在于所有用到的bundle里,而把texture和material单独提出来呢?


如果是旧版的AssetBundle机制,想按这种方式打包代码里估计得写不少依赖相关的判断逻辑。恰好,新的AssetBundle机制通过命名就可以实现这种打包关系:直接把Common Atlas.mat和Common Atlas.png命名为同一个bundle,而Common Atlas.prefab不进行命名(None)即可。当然如果想用脚本自动化命名的话估计还是得写不少判断逻辑。

这样打包之后每个Panel的AssetBundle包只比不加入Common Atlas.prefab前大了几KB,即解决了新机制的打包依赖问题,又没有过大的增加包体容量,一举两得。至此问题解决,下一步就是写一个自动化命名和检测的脚本了。毕竟不能总是人工去命名,而且也要防止其他人误操作。

PS:有人按照方法试了一下没成功,这里需要注意的是,在加载UIPanel的Bundle时需要先加载其依赖包也就是CommonAtlas所在的Bundle


不得不说,新的AssetBundle还有很多不成熟的地方,比如检测是否需要重新打包的逻辑,未进行bundle命名的文件不出现在Manifest里等等,想用让人完全放心的AssetBundle的话,估计还得等上一段时间。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值