Unity的WWW网络动态加载和储存在本地

本文探讨了在游戏开发中使用Unity的WWW加载类从服务器加载资源包的方法,包括本地文件版本校验、资源更新与删除策略,以及构建一个用于资源加载的单例类。此外,介绍了如何在游戏初始化时正确初始化此单例类并实现资源的本地与网络加载,确保资源高效管理和复用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

游戏开发中需要从服务器上加载一下资源包,场景等,这时就需要使用Unity的WWW加载类。最近研究WWW加载发现很多问题和报错,这里接写出来和大家共享一下。

关于加载:首先是检查本地文件里是否存在相同的资源包文件(检查和校验版本号),如果都是正确的就不需要从服务器端下载了直接从本地(手机就是SD卡里了)加载就可以了。

如果版本号不对那么就要下载最新版本的资源了,当然要把老版本的从本地删除,不然在手机里会很占储存空间的。

新建一个Resource类,作为加载工具类继承与MonoBehaviour。

新建方法,传入必要信息提供加载:

System.IO包里提供了一个File类,其Exists方法就是查询指定路径下是否有指定的文件存在。

1
2
3
4
5
6
7
8
9
10
public  static  string  suffix =  ".unity3d" ; //资源包后缀
public  static  Dictionary< string ,GameObject> cache =  new  Dictionary< string , GameObject>();
 
public  void  load( string  path, string  name, int  version){
         if (!File.Exists(Application.persistentDataPath + name + suffix + version)){
             StartCoroutine(loader(path,name,version)); //网络加载   
         } else {
             StartCoroutine(loadBundleFromLocal(path,name,version));  //本地加载
         }
     }

在加载时需要启动一个协程,异步加载。如果加载成功则就写入本地和内存里方便调用,如果有新的版本则需要吧老版本的删除掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private  IEnumerator loadBundle( string  path, string  name, int  version){
         print( "fuck you" );
         int  oldVersion = version - 1; //写死了
         string  pathName = Application.persistentDataPath + path + name;
         string  url = path + name + suffix;
         using (WWW www = new  WWW(url)){
             yield  return  www;
             if (www.error ==  null ){
                 if (File.Exists(pathName + oldVersion)){
                     File.Delete(pathName + oldVersion);
                     Debug.Log( "delete old version data succeed..." );
                 }  
                 if (!File.Exists(pathName + version)){
                     //write file in local
                     File.WriteAllBytes(pathName + version,www.bytes);
                     //other function
//                  FileStream fs = new FileStream(Application.persistentDataPath + path + name + version,FileMode.OpenOrCreate);
//                  fs.Write(www.bytes,0,www.bytes.Length);
//                  fs.Flush();
//                  fs.Close();
                     Debug.Log( "load bundle succeed,write file path:" +pathName + version);
                 } else {
                     Debug.Log( "write file error..." );
                 }
                 AssetBundle ab = www.assetBundle;
                 GameObject go = ab.mainAsset  as  GameObject;
                 cache[name] = go;
                 Resource.go = go;
                 //ab.Unload(false);
             }
         }
     }

从本地加载就更简单了,直接把本地文件所在的路径传给WWW作为参数就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
private  IEnumerator loadBundleFromLocal( string  path, string  name, int  version){
         using (WWW www =  new  WWW( "file:///" +Application.persistentDataPath + path + name + version)){
             yield  return  www;
             if (www.error ==  null ){
                 AssetBundle ab = www.assetBundle;
                 GameObject go = ab.mainAsset  as  GameObject;
                 cache[name] = go;
                 Resource.go = go;
                 Debug.Log( "load data frome local succeed..." );
             }
         }
     }

好了,Resource这个类就基本建立完成了。准备调用~

一般思路:

1
2
3
4
5
6
7
8
9
10
11
12
13
using  UnityEngine;
using  System.Collections;
 
public  class  LoadUIResponse : MonoBehaviour {
     public  Resource rs;
     void  Start(){
         if (rs ==  null ){
             rs =  new  Resource();
         }
         int  version = 1;
         rs.load( "http://192.168.1.122/resource/" , "monster1" ,version);
     }
}

但是在运行时会报出异常:NullReferenceException UnityEngine.MonoBehaviour.StartCoroutine (IEnumerator routine)。

这是咋回事,原来我的Resource是继承与MonoBehaviour的。看看这个警告:

You are trying to create a MonoBehaviour using the 'new' keyword.  This is not allowed.  MonoBehaviours can only be added using AddComponent().  Alternatively, your script can inherit from ScriptableObject or no base class at all

UnityEngine.MonoBehaviour:.ctor()

这就要注意了,这个Resource不能用new。是需要加在一个显示组件上通过AddComponent()方法来获取的。

so...正确做法如下:

在响应组件上添加Resource脚本,和UIResponse脚本。

在UIResponse上添加代码:

1
2
3
4
5
6
7
8
9
10
11
12
private  Resource rs;
     
     string  path = "      int  version = 1;
     string  name =  "monster1" ;
     void  start(){
         rs = gameObject.GetComponent( typeof (Resource))  as  Resource;
         Resource.instance = rs;
     }
     void  OnMouseDown(){
         Resource.instance.load(path,name,version);
     
     }

这样,随着游戏的初始化,加载工具类Resource就被初始化出来了,并且已经存放在Resource的instance变量里。以后就可以直接使用Resource.instance来调用加载了。

并且已有的加载都储存在cache里了,也可以先根据name到内存里去查找一遍再去加载。

最终加载出来的gameObject需要Instantiate实例化出来。

以后就可以直接使用Resource.instance来访问Resource里的个个加载方法了。

Resource的instance的get/set方法的写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private  static  Resource _instance;
//添加get和set
public  static  Resource instance{
        set  {
         _instance = value;
     }
     get 
         if  (_instance ==  null ) {
             //_instance = new Resource();
             Debug.Log( "no instance." );
         }
         return  _instance;
     }
}

看起来和单例的创建方法很类似,如果直接使用调用单例的方式来调用的话。执行到建立协程StartCoroutine时就会抛出空引用的异常,并出现不能创建新的MonoBehaviour实例的警告。

有关WWW的说明文档参考:http://docs.unity3d.com/Documentation/ScriptReference/WWW.html

有关File的本地缓存请参考微软.NET帮组文档:http://msdn.microsoft.com/zh-cn/library/system.io.file(v=vs.85).aspx

据说可以使用FileStrenm进行同样的操作,上面代码已实现(注释部分)只是没有进行验证。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值