unity 图片资源加载

2015年01月31日 10:17 0 点赞 0 评论 更新于 2025-11-21 15:48

在 Unity 开发中,图片资源加载是一个常见且重要的任务。本文将详细介绍 Unity 中利用 NGUI 进行图片资源加载的过程和方法。

NGUI 与 UIAtlas

NGUI 提供了方便的 UIAtlas 功能,其主要作用是优化 DrawCall,将众多图片整合在一张贴图上。在 Unity3D 中,若仅使用原生的 GUI,很容易忽视 DrawCall 问题,而 NGUI 的 UIAtlas 正是为改进这一问题而设计的。此外,NGUI 还进行了许多其他方面的优化。

不过,NGUI 提供的 UIAtlas 只能在 Editor 内生成贴图和 prefab 以供 UISprite 使用。为了使游戏资源与游戏本体尽可能分离,特别是在游戏资源需要动态更新的情况下,往往需要动态加载和设置 UIAtlas。

动态加载图片的方法

方法 1:在代码中创建和设置 UIAtlas

此方法可直接在代码中创建和设置 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 及其贴图

将整个 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

以下是一个管理 UIAtlas 的类,用于保存和查找 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 相结合。

作者信息

feifeila

feifeila

共发布了 3994 篇文章