1.在编辑器中动态读取文件
在实际的游戏开发中,其实有相当一部分的静态数据是可以放在客户端的,所以自然而然地产生了动态读取这些文件的需求,比如csv、xml等,不管是用什么系统做Unity的开发,如果想要实现这种效果,显然要先在编辑器中实现基本的功能,然后再去各个移动平台去测试,所以要读取外部文件的第一步,显然要再电脑端实现这个共嫩南瓜(编辑器)
下面举一个读取xml的例子-----读取xml文件并动态生成一个类
举例:
<?xml version="1.0" encoding="UTF-8"?>
<test>
<name>chenjd</name>
<blog>http://www.cnblogs.com/murongxiaopifu</blog>
<organization>Fanyoy</organization>
<age>25</age>
</test>
这是我们要加载的xml文件
将这个文件放在任意地方,通过指定地址,将其读取
public class Test2 : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
XElement result = LoadXML("Assets/xml-to-egg/Test.xml");
Debug.Log(result.ToString());
}
private XElement LoadXML(string v)
{
XElement xml = XElement.Load(v);
return xml;
}
// Update is called once per frame
void Update()
{
}
}
读取效果如下:
待考虑的问题
1.不规范的地址,如果地址参数如上写,跨平台一定不对,出现的问题就是再移动端的Unity中找不到目标文件夹
2.使用的是电脑上传统的读取资源的做法,没有使用unity提供的方法,可能会找到文件,但是不一定能正确读取文件内容
以上是可能导致造成一东莞读取资源失败的原因,接下来先看一下第一个问题(关于不同移动平台上的资源路径问题)
2.移动平台的资源路径问题
我们知道,如果你想要读取一个文件,自然要找到这个文件,下表是unity中资源路径以及他们的介绍
unity3D中的路径 | 介绍 |
---|---|
Application.dataPath | 用于返回程序的数据文件所在的文件及的路径 |
Application.streamingAssetsPath | 用于返回流数据的缓存目录,返回路径为相对路径 |
Application.persistentDataPath | 用于返回一个持久化的数据储存目录的路径,可以在此路径下储存一些持久化的数据文件 |
Application.temporaryCachePath | 返回临时数据的缓存目录 |
Android平台 | 具体路径 |
---|---|
Application.dataPath | /data/app/xxx.xxx.apk |
Application.streamingAssetsPath | jar:file:///data/app/xxx.xxx.apk/!assets |
Application.persistentDataPath | /data/data/xxx.xxx.xxx/files |
Application.temporaryCachePath | /data/data/xxx.xxx.xxx/cache |
IOS平台 | 具体路径 |
---|---|
Application.dataPath | Application/xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data |
Application.streamingAssetsPath | xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data/Raw |
Application.persistentDataPath | xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx/Documents |
Application.temporaryCachePath | xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx/Library/Caches |
是不是感觉很奇怪,我们知道再Unity编辑器中默认的资源文件存放路径就是Assets,怎么会出来这么多路径呢?
要解决这个疑问,先来看看unity中资源处理种类
再unity中涉及到的资源位置有以下几个类----------Resources、StreamingAssets、AssetBundle、PersistentDataPath
Resources:作为unity的保留文件夹出现的,也就是说,如果你创建的文件夹的名字与这个名字重名,那么在打包的时候里面的内容同要会被无条件的打包到发布包中,其特点有:
1.只读(不能修改)
2.会将文件夹中的内容打包集成到.assets中,建议放一些Prefab
3.主线程加载
4.资源读取用Resources.load()
StreamingAssets:与Resources很像,同样作为保留文件夹出现,但是不同的是,前者会被压缩和加密,erStreamingAssets不会,它主要用来放一些二进制文件,特点如下
1.只读不可写
2主要存放二进制文件
只能用WWW类来读取
AssetBundle:简而言之就是把Prefab或者二进制文件封装成AssetBundle文件(也是一种二进制)需要注意的是在移动端无法更新脚本
特点如下:
1.unity定义的二进制类型
2.最好将Prefab分装成AssetBundle,但是刚才说移动端无法更新脚本又是怎么回事?只要这个prefab上面挂载的是本地脚本是可以的
3.使用www加载
persistentDataPath:看上去它 就是一个路径,实际上在在这个路径下是可读可写的
特点:内容可读可写,不过只能运行时才能写入或者读取,提前将数据存入这个路径是不行的
2.无内容限制,你可以从StreamingAsset中读取二进制文件就或者从AssetBundle读取文件来写入这里
3.写下的文件是可以在电脑上查看,同样可以清理掉
介绍完这几个资源处理种类下面举几个例子来实现读取外部资源的过程
1.利用Resources读取
在unity中创建文件夹Resources,将Test.xml复制一份,通过Respurces读取
代码如下
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using UnityEngine;
public class Test3 : MonoBehaviour
{
private string _result;
// Start is called before the first frame update
void Start()
{
LoadXML("Test");
}
private void LoadXML(string v)
{
_result = Resources.Load(v).ToString();
XmlDocument doc = new XmlDocument();
doc.LoadXml(_result);
}
private void OnGUI()
{
GUIStyle titlestyle = new GUIStyle();
titlestyle.fontSize = 20;
titlestyle.normal.textColor = new Color(0, 1, 1);
GUI.Label(new Rect(400, 10, 500, 20), _result, titlestyle);
}
// Update is called once per frame
void Update()
{
}
}
绘制结果如下
2.利用StreamingAssets
创建StreamingAssets文件夹,并将xml文件复制一份
加载代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test4 : MonoBehaviour
{
string _result;
// Start is called before the first frame update
void Start()
{
StartCoroutine(LoadXML());
}
IEnumerator LoadXML()
{
string sPath = Application.streamingAssetsPath + "/Test.xml";
WWW wWW = new WWW(sPath);
yield return wWW;
_result = wWW.text;
}
private void OnGUI()
{
GUIStyle titlestyle = new GUIStyle();
titlestyle.fontSize = 20;
titlestyle.normal.textColor = new Color(0, 1, 1);
GUI.Label(new Rect(400, 10, 500, 20), _result, titlestyle);
}
// Update is called once per frame
void Update()
{
}
}
上文说过,必须使用www来进行加载,否则会报空异常的错误
运行结果
利用AssetBundle
1.创建一个空的Prefab,命名Cube,然后创建一个Cube,将其拉到刚创建好的Prefab
2.新建一个脚本ExportAssetBundles.cs(代码来自官方文档),保存在Asset/Editor目录下
//在Unity编辑器中添加菜单
[MenuItem("Assets/Build AssetBundle From Selection")]
static void ExportResourceRGB2()
{
// 打开保存面板,获得用户选择的路径
string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "assetbundle");
if (path.Length != 0)
{
// 选择的要保存的对象
Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
//打包
BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.StandaloneWindows);
}
}
3.选中预设Cube,运行Build AssetBundle From Selection。这时会弹出一个保存框,将其命名为cube.unity3d(这里为了测试方便,放在c盘。实际项目中,我们是需要将他们放在web服务器,供所有客户端下载更新)
4.新建一个场景scene1.unity,上面放置几个模型,然后保存
5.选中该场景,在之前的ExportAssetBundles.cs脚本中添加打包场景的函数,运行Assets->Build Scene,保存为scene1.unity3d(这里为了测试方便,也放在c盘)
[MenuItem("Assets/Save Scene")]
static void ExportScene()
{
// 打开保存面板,获得用户选择的路径
string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "unity3d");
if (path.Length != 0)
{
// 选择的要保存的对象
Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
string[] scenes = {"Assets/scene1.unity"};
//打包
BuildPipeline.BuildPlayer(scenes,path,BuildTarget.StandaloneWindows,BuildOptions.BuildAdditionalStreamedScenes);
}
}
a.AssetBundle的保存后缀名可以是assetbundle或者unity3d
b.BuildAssetBundle要根据不同的平台单独打包,BuildTarget参数指定平台,如果不指定,默认的webplayer
加载assetbundle
using System;
using UnityEngine;
using System.Collections;
public class Load: MonoBehaviour
{
private string BundleURL = "file:///C:/cube.assetbundle";
private string SceneURL = "file:///C:/scene1.unity3d";
void Start()
{
//BundleURL = "file//"+Application.dataPath+"/cube.assetbundle";
Debug.Log(BundleURL);
StartCoroutine(DownloadAssetAndScene());
}
IEnumerator DownloadAssetAndScene()
{
//下载assetbundle,加载Cube
using (WWW asset = new WWW(BundleURL))
{
yield return asset;
AssetBundle bundle = asset.assetBundle;
Instantiate(bundle.Load("Cube"));
bundle.Unload(false);
yield return new WaitForSeconds(5);
}
//下载场景,加载场景
using (WWW scene = new WWW(SceneURL))
{
yield return scene;
AssetBundle bundle = scene.assetBundle;
Application.LoadLevel("scene1");
}
}
}
这样就可以加载外部或者服务器上的资源了
使用Resources类加载资源
通过Resources类何以轻易的找到并访问我们想要得到的资源对象,例如使用这个类的FindObjectsTypeAll来获取当前场景中的资源信息
签名如下
public static object[] FindObjectsOfTypeAll(Type type)
它只需要返回一个Type类型的参数typr,用来搜寻资源时和获取与type相匹配的类型,并且返回一个Object数组,它能够获取的对象类型包括了Unity3d能够加载的所有资源类型,如游戏对象,预制体、材质、Mesh、贴图等,需要注意的是,这个方法运行消耗较大,不适合每一帧都调用
举例使用
private void OnGUI()
{
GUILayout.Label("All" + Resources.FindObjectsOfTypeAll(typeof(UnityEngine.Object)));
}
除了这中用法,还有一个静态方法是Load
方法签名如下
public static Object Load(string path);
public static Object Load(string path, [NotNull] Type systemTypeInstance);
需要注意的是,参数path并非是完整路径,而是先对于当前工程中Assets目录下的Resources文佳佳的路径,第二个参数则是用来作为过滤返回的对象,如果提供了第二个参数,那么只有和type类型相同的对象能够被返回
void Start () {
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Plane);
Renderer rend = go.GetComponent<Renderer>();
rend.material.mainTexture = Resources.Load("glass") as Texture;
}
利用WWW类的构造函数实现资源下载
www的构造函数有几种重载版本,一般使用的是只有一个string参数的方法,签名如下
public WWWW(string url)
这里面的string参数标识WWW要从URL所提供的地址下载资源,由于这是WWW的构造函数,一旦资源下载完成,就可以通过类对象访问下载的资源对象,除了创建一个新的WWW类的对象意外,该方法还会创建并发送一个GET请求,紧接着一个数据流会被创建并且开始进行下载,此时必须等待整个下载流程完成,然后才可以通过WWW类的对象访问下载得到的资源对象,而由于数据流可以通过yeild关键字被方便的挂起,所以很方便,举例如下
public string url = "http://image.earthcam.com/ec_metros/ourcame/fridays.jpg";
IEnumerator Start () {
WWW www = new WWW(url);
yield return www;
Image s = this.GetComponent<Image>();
Sprite sprite = Sprite.Create(www.texture,new Rect(0,0,www.texture.width,www.texture.height),new Vector2(0,0));
s.overrideSprite= sprite ;
Debug.Log("sucess");
}
本文介绍了几种再unity开发过程中,实际再读取外部资源的几种操作,当然还有很多别的方式。