找回密码
 立即注册
楼主: sunshin

[脚本] Unity3D 异步读取类代码

  [复制链接]
发表于 2012-11-27 11:24 | 显示全部楼层 |阅读模式

制作网页网游,常常需要从Server端临时下载一个资源进来,然后读取,通常使用WWW下载AssetBundle的方法来实现。而下载过程需要时间,不可能立即完成,这通常需要提供下载需求的用户自己实现同步的机制,比较麻烦,而且需要重复劳动。因此我想提供一个简单的资源下载管理的类,这个类大概提供以下的功能:

1提供一个简单的异步加载的回调机制。调用这个类的一个函数,提供一个资源下载请求,接到请求后,这个类开始下载这个资源,当资源完成后,就调用用户所提供的回调函数,通知用户下载已经完成,这时用户可以选择如何处理下载完的对象。

2.用户提出请求时,可以提供一个自定义的参数,这个参数在回调函数中,作为参数传入,这样方便用户传输一些这个资源特定的信息,方便在同一个回调函数中处理不同种类的资源。

3.可以多次请求同一资源的下载请求,但不会造成实际的多次下载。而会直接把之前下载完成后的资源返回。

下面是这个类的实现代码:
[code=csharp]
using UnityEngine;
using System;
using System.Collections.Generic;

public class MyResourceManager : MonoBehaviour

{

   public delegate void DownFinishDelegate (WWW wwwObj,object customParam);
   public class WWWRequest
    {
       public string requestURl;
      public DownFinishDelegate calbackFun;
       public WWW wwwObject = null;
       public bool bHasDeal = false;
      public List<object> customParams = new List<object>();
      public WWWRequest(){}
      public WWWRequest (string url, DownFinishDelegate cbFun,object customParam=null)
        {
           requestURl = url;
          calbackFun = new DownFinishDelegate (cbFun);
         wwwObject = new WWW (url);
         customParams.Add(customParam);


        }


    }

   //WWW Request List

   private Dictionary<string, WWWRequest> m_WWWMap = new Dictionary<string, WWWRequest> ();

   public void AddDownRequest (string url, DownFinishDelegate callBackFun,object customParam=null)
  {
       if(url != "")
     {
         //增加新的资源下载需求
          if (!m_WWWMap.ContainsKey (url))

           {

            m_WWWMap.Add (url, new WWWRequest (url, callBackFun,customParam));

       }
           else
          {
               //已经提交相同请求,但是没有下载完成
            if(!m_WWWMap[url].wwwObject.isDone)
             {
                m_WWWMap[url].calbackFun += callBackFun;
                  m_WWWMap[url].customParams.Add(customParam);

              }
            //已下载资源,直接调用回调函数
               else
              {
                   callBackFun.Invoke (m_WWWMap[url].wwwObject,customParam);
               }

          }

      }



   }


  // Use this for initialization
   void Start ()


  {


   }
   // Update is called once per frame
  void Update ()
    {
      foreach (KeyValuePair<string,WWWRequest> wwwPair in m_WWWMap)
      {
          WWWRequest wwwReq  = wwwPair.Value;
         //如果尚未调用回调,并且下载完成,则调用
       if ((!wwwReq.bHasDeal) && wwwReq.wwwObject.isDone)
        {

            //print("DelegationCount:"+wwwReq.calbackFun.GetInvocationList().GetLength(0));
         for(int i=0;i<wwwReq.calbackFun.GetInvocationList().GetLength(0);i++)
              {

                  ((DownFinishDelegate)wwwReq.calbackFun.GetInvocationList()).Invoke(wwwReq.wwwObject,wwwReq.customParams);

            }

            wwwReq.bHasDeal = true;

         }

}

       }
}
[/code]

复制代码关于实现,有以下一些考虑和说明:

1. 这个类继承自脚本的类MonoBehaviour,这样主要是为了可以被放到主循环里,在Update函数检查每个请求是否已经下载完成,这样需要在场景中添加一个GameObject,将这个脚本附加到上边。

2.回调托管函数的原型声明为:

public delegate void DownFinishDelegate (WWW wwwObj,object customParam);

是一个无返回值,带有两个参数的函数,第一个参数是下载的WWW对象,第二个是自定义参数。

3.类中包含一个内部类WWWRequest,这个类代表一种资源的下载请求,而不是一次。这里的一种是以一个URL来区分的,也就是说可以多次请求同一个URL资源下载,但是可以提供不同的回调和自定义参数,这多次的下载请求都属于同一个WWWRequest,这样提供了更多的灵活性,也不需要重复下载同一个资源多次。为了实现这个功能,使用了C#中的Dictionary类型,这个字典类型类似于C++中的map,由一个键值索引一个值,这个键值就是url的字符串。值就是这个WWWRequest对象。

这个对象中包含了以下属性:

url字符串:string requestURl
复制代码回调函数的托管对象:DownFinishDelegate calbackFun
复制代码资源下载所用到的WWW对象:WWW wwwObject

标示是否下载完成的旗标:bool bHasDeal

自定义参数列表:List<object> customParams

因为一个托管对象,可以加载多个托管函数,因此这里只使用一个托管对象,而自定义参数需要提供一个列表。每个托管的回调函数按顺序一一对应列表中的参数。

3.AddDownRequest就是提供下载资源请求所需要调用的函数。实现比较简单,先检查在Dictonary里是否已有这个资源的下载请求,如果没有增添一个新的请求,创建一个WWWRequest对象加入到字典中。

如果已有,那么检查当前的状态,如果已经下载完成,则直接调用用户提供的回调函数。

如果还没下载完成,就在字典中已经创建的WWWRequest中的托管对象 和参数列表中相应都增加新的一项,等待下载完成后调用。

4.在Update函数中,遍历检查每个WWWRequest中的WWW对象的当前状态,如果有完成的,则依次调用每个注册到这个WWWRequest的回调函数,传入对应的自定义参数。

调用完成后,将旗标置位,防止下次更新再调用。

【使用方法】:

要使用这个类,首先需要找到场景中的资源管理的GameObject,并且获得脚本类对象。

1.MyResourceManager resMgr = (MyResourceManager)GameObject.Find("MyResourceManager").GetComponent("MyResourceManager") ;

然后提出下载请求:

1.resMgr.AddDownRequest("http://"+GlobalConfig.GetConnectIP()+"/AssetBundleResource/Terrain/Terrain3.unity3d",DownFinishDelegate,null);

这里第一个参数就是下载的Url,第二个是回调函数,自定义参数因为这里没用到,传入null。

然后需要声明回调处理函数:

public void DownFinishDelegate (WWW wwwParam,object customParam)



   {



      wwwObj = wwwParam;



   }
复制代码这个函数在资源下载完成后会被调用,这里的处理逻辑比较简单,只是简单保存下下载的WWW对象,以便之后使用其中的assetBundle对象。
发表于 2012-11-28 17:10 | 显示全部楼层
异步是很有用的功能吗?会不会出现资源没有下载完就运行出错呢?
发表于 2013-3-4 10:54 | 显示全部楼层

感谢楼主的无私分享!{:soso__11402694654016840197_7:}
发表于 2017-2-28 09:51 | 显示全部楼层
很不错
发表于 2017-2-28 10:07 | 显示全部楼层
顶顶多好
发表于 2017-2-28 09:32 | 显示全部楼层
真心顶
发表于 2017-2-28 09:32 | 显示全部楼层
难得一见的好帖
发表于 2017-2-28 10:07 | 显示全部楼层
LZ真是人才
发表于 2017-5-26 21:08 | 显示全部楼层
楼主是超人
发表于 2017-5-26 21:28 | 显示全部楼层
好帖就是要顶
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-27 12:23 , Processed in 0.099539 second(s), 24 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表