Unity3D-NGUI动态加载图片
NGUI提供了很方便的UIAtlas,其主要作用是改进DrawCall,把众多图片整合在一张贴图上,由于UNITY3D简单易用的好处,所以只是用原生的GUI很容易忽视DrawCall的问题,所以NGUI为了改进,才有了UIAtlas。当然NGUI还做了很多优化。
这里主要还是介绍如何利用UISprite来动态的加载图片。NGUI所提供的UIAtlas虽然好用,但只能在Editor内生成贴图和prefab以供UISprite使用。为了能够让游戏资源与游戏本体尽可能的分离,特别是游戏资源需要动态更新的情况。很多时候,都需要动态加载,动态设置UIAtlas。
这里主要介绍2个方法。
方法1:直接在代码中创建和设置UIAtlas并对UISprite进行显示。这种方法可以对任何零散的贴图进行加载,但缺点是浪费DrawCall,主要应用在特别零散的贴图资源上。
public class ImageLoader : MonoBehaviour { //需要加载动态图片的对象 public UISprite m_img; //自用的Atlas private UIAtlas m_uiAtlas; /// <summary> /// 加载的贴图 /// </summary> /// <param name="tex">Tex.</param> public void ImageLoad(Texture2D tex) { if(tex == null) { return; } if(tex.name == m_img.spriteName) { return; } //准备对象和材质球 if(m_uiAtlas == null) { Material mat; Shader shader = Shader.Find("Unlit/Transparent Colored"); mat = new Material(shader); m_uiAtlas = this.gameObject.AddComponent<UIAtlas>(); m_uiAtlas.spriteMaterial = mat; } //设定贴图 m_uiAtlas.spriteMaterial.mainTexture = tex; m_uiAtlas.coordinates = UIAtlas.Coordinates.Pixels; //为对应UISprite接口,给Atlas加对象 UIAtlas.Sprite sprite = new UIAtlas.Sprite(); sprite.name = tex.name; sprite.outer = sprite.inner = new Rect(0f, 0f, tex.width, tex.height); m_uiAtlas.spriteList.Clear(); m_uiAtlas.spriteList.Add(sprite); //设置完成 m_img.atlas = m_uiAtlas; m_img.spriteName = tex.name; } }
方法2:将整个UIAtlas及其贴图打包,而后形成资源,驻留在内存中(仅仅是指向资源的指针),UNITY3D会根据引用来确定贴图是否直接放实际内存中。这种方法更适合ICON之类有规律可以配置整合的资源。缺点是需要维护。
//从资源文件夹加载打包成assetBundle的ICON资源文件 private IEnumerator LoadResIcon() { //准备好资源们 string strFormat = ResourcePath.GetPath() + "UI/{0}"; string strFilePath = ""; for(int i = 0 , nMax = GameConfig.Instance.IconSet.strIcons.Length; i < nMax ; i++) { string strAssetName = GameConfig.Instance.IconSet.strIcons[i]; strFilePath = string.Format(strFormat, strAssetName); WWW tmp_www = null; try { tmp_www = new WWW(strFilePath); } catch { tmp_www = null; } if(tmp_www==null) { continue; } yield return tmp_www; if(tmp_www.error !=null) { tmp_www.Dispose(); tmp_www = null; yield break; } AssetBundle tmp_assetBundle = tmp_www.assetBundle; tmp_www.Dispose(); tmp_www = null; UIAtlas atlas = tmp_assetBundle.Load(strAssetName,typeof(UIAtlas)) as UIAtlas; tmp_assetBundle.Unload(false); GameConfig.Instance.IconSet.SaveUIAtlas(i, atlas); } yield return null; }
管理UIAtlas
public class IconSet { public string[] strIcons = { "A1_Atlas", "A2_Atlas", "A3_Atlas", }; public UIAtlas[] m_AtlasData; Dictionary<string, int> m_dicIcon; public IconSet() { m_AtlasData = new UIAtlas[strIcons.Length]; m_dicIcon = new Dictionary<string, int>(); } //保存Atlas的完整信息 public void SaveUIAtlas(int nIndex, UIAtlas UIvalue) { m_AtlasData[nIndex] = UIvalue; foreach (string iconNames in UIvalue.GetListOfSprites()) { m_dicIcon[(string)iconNames.Clone()] = nIndex;//将所有的ICONNAME信息记录并且绑定成索引,以便查找 } } //根据ICONNAME找出对应UIATLAS public UIAtlas FindAtlasBySpriteName(string name) { int nAtlasIndex = 0; if (m_dicIcon.TryGetValue(name, out nAtlasIndex)) { return m_AtlasData[nAtlasIndex]; } return null; } }
实际使用的范例:
//设置显示对象 UISprite sprite = this.GetComponent<UISprite>(); sprite.atlas = GameConfig.Instance.IconSet.FindAtlasBySpriteName("Icon001"); sprite.spriteName = "Icon001";
总结:
以上两种方法基本能够应对大部分UI的现实问题,ImageLoader本身传入的是Texture2D,所以,即便是RenderTexture也没问题,都能与NGUI和谐相处。加以扩展的话,就是让另一个摄像机导出的东西和当前NGUI摆的UI结合。