纳金网
标题:
【转载】脚本化对象ScriptableObject设置数据成为AssetBundle
[打印本页]
作者:
艾西格亚
时间:
2012-9-15 01:57
标题:
【转载】脚本化对象ScriptableObject设置数据成为AssetBundle
本帖最后由 艾西格亚 于 2013-7-25 13:39 编辑
不管是制作游戏或应用软件,多少都会有一些系统设定值,有些人可能会使用一个文件档,依照定义好的格式,要求制作团队全部的人都依照这个格式去设定这些值,可是因为文件档的编写太过方便和自由,如果解读这个文件文件的程序漏掉某些检查的地方,有可能因为人员的疏忽,弄错了空白、换行、引号、逗号....等而造成解读结果错误,而且要针对各种设定数据的文文件编写解读程序并转换成可用的对象成员,有时候还蛮费工的,而且也不方便管理;另外,當我們将 Project view 中的资源打包成 AssetBundle,并于游戏执行时视需要再从外部加载,那么对于自定义的数据资源,又该如何运用在 AssetBundle 呢?此时就要使用到 ScriptableObject。
试想,如果我们把需要设定的数据值都写成 script 的 public 字段,然后再拉到场景中的对象做设定,那么我们可能要每个场景建立时都要做一个这样的设定对象,当然,此时可以为这个对象建立个 Prefab,那么每次建场景时直接拉进来就能设定了,但是,如果有一天发现其中一个共享的设定值需要变更,就必须把场景一个個打开来修改;此时可能会想不如做个游戏启始设定场景,只要转换场景时,这个场景的对象不要销毁就好了,但常常一个场景中并不用使用到整个游戏全部的设定值,那真的有必要这样做吗?而且把这些设定值放在场景中的对象来使用,可移植性不佳;此时,如果能在 Project view 里的资源文件设定好,于游戏进行中再读入可能会方便些,但可能又会有上一段所提到的文件格式问题;有鉴于以上顾虑到的部份,我们可以使用 ScriptableObject 在 Project view 建立专用的 Asset 来做设定,并将它存放在 Resources 文件夹中,于是,游戏中有需要用到时,才使用 Resources.Load() 取到设定值,编辑场景内容时也不用再加入设定值专用的对象,有任何设定值需要修改的话,只要打开 Project view 内的 Resources 文件夹统一设定;另外,由于 ScriptableObject 的域名及内容值的型态都已规范好,所以也大大降低人员在设置上输入错误的问题。
首先,先撰写一个继承自 ScriptableObject 的 class ...
public class DataHolder : ScriptableObject {
public string[] strings;
public int[] integers;
public float[] floats;
public bool[] booleans;
public byte[] bytes;
}
接下来使用【自定义 Unity 工具栏选单处理项目内容】一文中提到的方式制作选单命令,使这个命令执行时在 Resources 文件夹中建立 DataHolder Asset ...
[MenuItem("Custom Editor/Create Data Asset")]
static void CreateDataAsset(){
//数据 Asset 路径
string holderAssetPath = "Assets/Resources/";
if(!Directory.Exists(holderAssetPath)) Directory.CreateDirectory(holderAssetPath);
//建立实体
DataHolder holder = ScriptableObject.CreateInstance<DataHolder> ();
//使用 holder 建立名为 dataHolder.asset 的资源
AssetDatabase.CreateAsset(holder, holderAssetPath + "dataHolder.asset");
}
当点击选单 Custom Editor/Create Data Asset 之后,会发现在 Project view 会多出 Resources 文件夹,且里面会多一个名为 dataHolder 的资源,点击它的话可以在 Inspector view 查看到它的内容字段就如同我们在前面撰写的 DataHolder class 中的字段一样,此时我们就可以开始使用它来设定域值,如果我们需要多个同类型的设定,只要在点选 Resources 文件夹内的 dataHolder 时按键盘的 Ctrl+D,就能复制多个来使用。
当游戏执行中,只需要用一句即可取得这个资源...
DataHolder holder = (DataHolder)Resources.Load("dataHolder" , typeof(DataHolder));
到这里应该就能明白,holder.strings 代表的就是 Inspector view 中 strings 字段的内容,以此类推,这些设定值是不是就变得很容易取得。
[MenuItem("Custom Editor/Create Data AssetBundle")]
static void CreateDataAssetBundle(){
// AssetBundle 的文件夹路径及扩展名
string targetDir = "_DataAssetBundles" + Path.DirectorySeparatorChar;
string extensionName = ".assetData";
//取得在 Project 窗口中选择的资源(包含文件夹的子目录中的资源)
Object[] SelectedAsset = Selection.GetFiltered(typeof (Object), SelectionMode.DeepAssets);
//建立存放 AssetBundle 的文件夹
if(!Directory.Exists(targetDir)) Directory.CreateDirectory(targetDir);
foreach(Object obj in SelectedAsset){
//只处理型别为 DataHolder 的资源
if(!(obj is DataHolder)) continue;
string targetFile = targetDir + obj.name + extensionName;
//建立 AssetBundle
if(BuildPipeline.BuildAssetBundle(obj, null, targetFile, BuildAssetBundleOptions.CollectDependencies)) Debug.Log(obj.name + " 建立完成");
else Debug.Log(obj.name + " 建立失败");
}
}
当选择我们在前面所建立的 Resources 文件夹内的 dataHolder 之后再点击选单 Custom Editor/Create Data AssetBundle,即会在 Unity 项目目录下建立 _DataAssetBundles 文件夹并将此资源储存为档名是 dataHolder.assetData 的档案。
之后于游戏中要加载,则调用以下方法...
private IEnumerator LoadDataHolder(){
// AssetBundle 档案路径
string path = string.Format("file://{0}/../_DataAssetBundles/{1}.assetData" , Application.dataPath , "dataHolder");
// 载入 AssetBundle
WWW bundle = new WWW(path);
//等待载入完成
yield return bundle;
//取出 dataHolder 资源的内容
DataHolder holder = (DataHolder)bundle.assetBundle.mainAsset;
//卸除 AssetBundle
bundle.assetBundle.Unload(false);
}
此处的 holder 局部变量内容则与前面使用 Resources.Load() 取得的内容相同,当然,实作时可能不是使用局部变量,这就看个人如何运用了。
以上,如果是使用制作为 AssetBundle 的方式来使用的话,那么在 CreateDataAsset() 那边的【数据 Asset 路径】就不需要是在 Resources 文件夹,以免都打包成 AssetBundle 要外部使用了,结果游戏在输出时还全都编进去了,反而变成多余的浪费。
也许有人会问,究竟有什么地方使用到这些做法了,简单的例子... 与服务器连接的设定值,总不能改个 IP 或 port 就要重新编译输出整个游戏吧!详细运用可以查看官方的 Character Customization 范例;还有很多可运用的地方,就看个人如何发挥啰!!
作者:
may
时间:
2012-11-30 22:29
支持楼主的帖子
欢迎光临 纳金网 (http://old.narkii.com/club/)
Powered by Discuz! X2.5