在实际项目中,往往会有一些需求是要从网络上加载图片展示出来。例如玩家的头像,公告大图和一些需要加载的展示的图片,这时就需要用代码实现从网络上加载必要的图片资源。
更新迭代版本的Unity已经在逐步放弃对WWW的支持,推出UnityWebRequest,详见Unity资源存放与加载-本地资源 更新资源。
UnityWebRequest同时也支持AssetsBundle,xml等文件资源的下载。
本文在基于UnityWebRequest的基础上,实现网络图片和本地磁盘图片的加载,并且实现在屏幕上。
先看看使用WWW是怎么完成图片加载的:
using (WWW www = new WWW(url))
{
yield return www;
if (string.IsNullOrEmpty(www.error))
{
//获取到链接中的图片
Texture2D texture = www.texture;
}
}
这段代码比较简单,返回的www.texture就是链接图片,这样就完成了简单的图片加载了。
但是,实际上,通常需要将图片加载内存和缓存到本地,以便在下一次可以很快的加载,而不用再次从网络上拉取。
流程:
关键代码:github地址
///LoadImageMgr.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
using UnityEngine.Networking;
public class LoadImageMgr
{
/// <summary>
/// download from web or hard disk
/// </summary>
/// <param name="url"></param>
/// <param name="loadEnd">callback</param>
/// <returns></returns>
public IEnumerator LoadImage(string url, Action<Texture2D> loadEnd)
{
Texture2D texture = null;
//先从内存加载
if (imageDic.TryGetValue(url,out texture))
{
loadEnd.Invoke(texture);
yield break;
}
string savePath = GetLocalPath();
string filePath = string.Format("file://{0}/{1}.png", savePath, UnityUtil.MD5Encrypt(url));
//from hard disk
bool hasLoad = false;
if (Directory.Exists(filePath))
yield return DownloadImage(filePath, (state, localTexture) =>
{
hasLoad = state;
if (state)
{
loadEnd.Invoke(localTexture);
if (!imageDic.ContainsKey(url))
imageDic.Add(url, localTexture);
}
});
if (hasLoad) yield break; //loaded
//from web
yield return DownloadImage(url, (state, downloadTexture) =>
{
hasLoad = state;
if (state)
{
loadEnd.Invoke(downloadTexture);
if (!imageDic.ContainsKey(url))
imageDic.Add(url, downloadTexture);
Save2LocalPath(url, downloadTexture);
}
});
}
public IEnumerator DownloadImage(string url, Action<bool, Texture2D> downloadEnd)
{
using (UnityWebRequest request = new UnityWebRequest(url))
{
DownloadHandlerTexture downloadHandlerTexture = new DownloadHandlerTexture(true);
request.downloadHandler = downloadHandlerTexture;
yield return request.Send();
if (string.IsNullOrEmpty(request.error))
{
Texture2D localTexture = downloadHandlerTexture.texture;
downloadEnd.Invoke(true, localTexture);
}
else
{
downloadEnd.Invoke(false, null);
Debug.Log(request.error);
}
}
}
/// <summary>
/// save the picture
/// </summary>
/// <param name="url"></param>
/// <param name="texture"></param>
private void Save2LocalPath(string url, Texture2D texture)
{
byte[] bytes = texture.EncodeToPNG();
string savePath = GetLocalPath();
try
{
File.WriteAllBytes( string.Format("{0}/{1}.png", savePath , UnityUtil.MD5Encrypt(url)), bytes);
}
catch(Exception ex)
{
Debug.LogError(ex.ToString());
}
}
/// <summary>
/// get which path will save
/// </summary>
/// <returns></returns>
private string GetLocalPath()
{
string savePath = Application.persistentDataPath + "/pics";
#if UNITY_EDITOR
savePath = Application.dataPath + "/pics";
#endif
if (!Directory.Exists(savePath))
{
Directory.CreateDirectory(savePath);
}
return savePath;
}
private Dictionary<string, Texture2D> imageDic = new Dictionary<string, Texture2D>();
public static LoadImageMgr instance { get; private set; } = new LoadImageMgr();
}
///LoadImageHelper.cs
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class LoadImageHelper : MonoBehaviour
{
string defaultUrl = "http://avatar.csdnimg.cn/1/E/6/2_u013012420.jpg";
/// <summary>
/// use this to load image
/// </summary>
/// <param name="texture"></param>
/// <param name="url"></param>
public void LoadImage(RawImage rawImage, string url)
{
if (string.IsNullOrEmpty(url))
{
url = defaultUrl;
}
StartCoroutine(LoadTeture(url, (tex) => {
rawImage.texture = tex;
}));
}
IEnumerator LoadTeture(string url, Action<Texture2D> cb)
{
yield return LoadImageMgr.instance.LoadImage(url, cb);
}
}