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

原文地址:http://www.unitymanual.com/thread-33321-1-1.html?_dsign=b3aeb908

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

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

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

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

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

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

[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public static string suffix = “.unity 3 d”; / / 资源包后缀
 
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 ) ) ; / / 本地加载
 
}
 
}


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

[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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作为参数就可以了。

[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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这个类就基本建立完成了。准备调用~

  一般思路:

[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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.1 68.1 . 122 / resource / , “monster 1 , 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上添加代码:

[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
private Resource rs;
 
string path = “     int version = 1 ;
 
string name = ”monster 1 “;
 
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方法的写法如下:

[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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实例的警告。

你可能感兴趣的:(unity)