项目内需要从web服加载texture,整理了一版简单的纹理管理,包含加载,卸载,控制同时加载数量
1、封装一个LoadingTexture,包含开始下载,中断,callback,释放,是否正在下载,是否开始下载(如果不需要控制下载数量,此属性可不要)
class LoadingTexture
{
private UnityWebRequest www;
private string mUrl;
private List<Action<string, Texture>> mCallback = new List<Action<string, Texture>>();
bool mIsStarted = false;
public LoadingTexture(string url, Action<string, Texture> callback)
{
mUrl = url;
mCallback.Add(callback);
}
public bool IsStarted()
{
return mIsStarted;
}
public void StartLoad()
{
mIsStarted = true;
System.Uri uri = new System.Uri(mUrl);
//如果不需要读写,第二个参数改为true,节省内存
www = UnityWebRequestTexture.GetTexture(uri,true);
www.SendWebRequest();
}
public string GetUrl()
{
return mUrl;
}
public void AddCallback(Action<string, Texture> callback)
{
mCallback.Add(callback);
}
public void RemoveCallback(Action<string, Texture> callback)
{
mCallback.Remove(callback);
//如果没人需要了,就中断吧
if (mCallback.Count == 0)
{
if (www != null)
{
www.Abort();
www.Dispose();
www = null;
}
}
}
public bool IsDone()
{
if (www != null)
{
return www.isDone;
}
return true;
}
public Texture GetTexture()
{
if (www == null)
{
return null;
}
if (!www.isDone)
{
return null;
}
if (www.isNetworkError || www.isHttpError)
{
return null;
}
if(www.downloadHandler == null)
{
return null;
}
//如果调用压缩,该texture需要改为可读写,并不能节省内存
//Texture2D tex = ((DownloadHandlerTexture)www.downloadHandler).texture;
//tex.Compress(false);
//return tex;
return ((DownloadHandlerTexture)www.downloadHandler).texture;
}
public void DoCallback()
{
Texture texture = GetTexture();
foreach ( var callback in mCallback)
{
callback?.Invoke(mUrl, texture);
}
}
public void Dispose()
{
if (www != null)
{
www.Dispose();
www = null;
}
}
}
2、添加管理加载流程的Manager,包含TexturePool,需要下载的LoadTextureList,需要释放的ReleaseList,同时下载的数量MaxLoadingTextureCount
int MaxLoadingTextureCount = 3;
//不能用Dictionary,不能保证执行按照加载顺序
//private Dictionary<string, LoadingTexture> mDicLoadingTexture = new Dictionary<string, LoadingTexture>();
List<LoadingTexture> mLisLoadingTexture = new List<LoadingTexture>();
Dictionary<string, Texture> mTexturePool = new Dictionary<string, Texture>();
//有可能一个Texture被多个引用,所以当一个Texture不需要时,要遍历是否还有引用
List<Texture> mTryReleaseTexture = new List<Texture>();
//下载函数
//被调用时先去池子里找,如果池子里有直接返回
//再去下载池子里找,如果有把回调加到该loadingtexture里
//都没有,new 一个 loadingtexture放进下载池子
public void LoadTexture(string url, Action<string, Texture> callback)
{
//当前容器
if (mTexturePool .ContainsKey(url))
{
Texture workTexture = mWorkTexture[url];
callback?.Invoke(url, workTexture);
return;
}
//正在下载,有可能多个地方引用
LoadingTexture loadingTexture = GetLoadingTexture(url);
if (loadingTexture != null)
{
if (callback != null)
{
loadingTexture.AddCallback(callback);
}
return;
}
//创建www去下载
mLisLoadingTexture.Add(new LoadingTexture(url, callback));
//如果没有正在下载的,启动当前的下载任务,下载一个一个顺利执行
// if (mLisLoadingTexture.Count == 1)
// {
// mLisLoadingTexture[0].StartLoad();
// }
}
//把下载成功的texture放进池子里,每次加载成功后调用
public void AddTexture(string url, Texture texture)
{
if(!mTexturePool.ContainsKey(url))
{
mTexturePool.Add(url, texture);
}
}
//通过url获取loadtexture
LoadingTexture GetLoadingTexture(string url)
{
foreach (var loadingTexture in mLisLoadingTexture)
{
if (loadingTexture.GetUrl() == url)
{
return loadingTexture;
}
}
return null;
}
//这种方式不确定能正确的移除
public void CancelLoadTexture(Action<string, Texture> callback)
{
foreach (var loadingTexture in mLisLoadingTexture)
{
loadingTexture.RemoveCallback(callback);
}
}
//尝试Texture是否被需要
public void TryReleaseTexture(Texture texture)
{
for (int i = 0; i < mLisLoadingTexture.Count; ++i)
{
if (mLisLoadingTexture[i].GetTexture() == texture)
{
return;
}
}
mTryReleaseTexture.Add(texture);
}
//通过URL从池子里取出texture
public Texture GetTextureByUrl(string url)
{
if (mTexturePool.ContainsKey(url))
{
return mTexturePool[url];
}
return null;
}
//通过texture取出url
public string GetUrlByTexture(Texture texture)
{
var eTexture = mTexturePool.GetEnumerator();
while (eTexture.MoveNext())
{
if (eTexture.Current.Value == texture)
{
return eTexture.Current.Key;
}
}
return null;
}
private float nextTimer = 0;
private float gap = 0.5f;
//每帧调用下载,每隔0.5f,调用释放
void Update()
{
UpdateLoading();
if(Time.time > nextTimer)
{
UpdateTryReleaseTexture();
nextTimer = Time.time + gap;
}
}
//下载函数,如果下载列表为空,return
//遍历下载列表,控制同时load数量为3
//loadingtexture下载完成后remove该任务
//将下载成功的对象AddToPool
//Invoke callback
//释放www
void UpdateLoading()
{
if (mLisLoadingTexture.Count == 0)
{
return;
}
for( int i = 0; i < MaxLoadingTextureCount && i < mLisLoadingTexture.Count; /*++i*/)
{
var loadingTexture = mLisLoadingTexture[i];
if (!loadingTexture.IsStarted())
{
loadingTexture.StartLoad();
}
if (!loadingTexture.IsDone())
{
++i;
continue;
}
//下载完删除任务
mLisLoadingTexture.RemoveAt(i);
var texture = loadingTexture.GetTexture();
if (texture != null)
{
//加载成功先保存Texture,再执行回调
mTexturePool.Add(loadingTexture.GetUrl(), texture);
loadingTexture.DoCallback();
}
else
{
//失败就不回调了,这里打个log吧
}
//释放www资源
loadingTexture.Dispose();
}
}
int releaseCount = 0;
int MAX_RELEASE_COUNT = 8;
//释放函数
//本来想实现,判断texture无引用,调用UnloadAsset
//发现这个函数只能卸载从磁盘下载的texture,不能卸载从web服下载的texture
//后来改成无引用的texture count > 8,直接调用UnloadUnusedAssets
//经测试不调用destory 也能被清理掉,考虑版本已验收就不改了
void UpdateTryReleaseTexture()
{
if (releaseCount > MAX_RELEASE_COUNT)
{
Resources.UnloadUnusedAssets();
releaseCount = 0;
}
if (mTryReleaseTexture.Count == 0)
{
return;
}
//获取第一个Texture,每次只处理一个
var texture = mTryReleaseTexture[0];
//无论是否成功,都先移除
mTryReleaseTexture.RemoveAt(0);
//取出所有的引用
var workPoints = GetAllTexturePoint();
foreach (var workPoint in workPoints)
{
if (workPoint != null)
{
//发现有被引用,直接返回
if (workPoint .Texture == texture)
{
return;
}
}
}
//执行到这说明texture没有被引用了
//先从TexturePool里移除
string url = GetUrlByTexture(texture);
if (url == null)
{
return;
}
mTexturePool.Remove(url);
Destroy(texture);
releaseCount++;
//直接卸载
//Resources.UnloadAsset(texture);
}
//清理函数,离开运行环境时调用
//clear Pool 之后调用UnloadUnusedAssets
public void Clear()
{
mTryReleaseTexture.Clear();
foreach (var t in mTexturePool.Values)
{
Destroy(t);
//Resources.UnloadAsset(t);
}
mTexturePool.Clear();
Resources.UnloadUnusedAssets();
}
}