华为noval10手机为什么

阅读:9112回复:21
【Unity+Vuforia】使用Assetbundle打包资源到SD卡并读取
发布于: 20:41
& &欢迎加入AR/VR技术交流群
& & & 很多同学反应把模型资源全部放在工程中导出的APK非常大,那么可以用把模型资源打包成Assetbundle(下文简称AB)包的形式,可以存在服务器上,使用的时候下载到SD卡,程序再从SD卡读取资源的方式。这样就可以大大减小APK的大小。 &
& & &Assetbundle的原理及实现代码参考 雨松MOMO 的教程:
& & &下面介绍我的实现步骤:
& & & (1)在Assets/Editor文件夹下创建脚本:ExportAssetBundles.cs
using UnityE
using UnityE
public class ExportAssetBundles : MonoBehaviour {
[MenuItem(&Custom Editor/Create AssetBunldes Main&)]
static void CreateAssetBunldesMain ()
//获取在Project视图中选择的所有游戏对象
Object[] SelectedAsset = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);
//遍历所有的游戏对象
foreach (Object obj in SelectedAsset)
string sourcePath = AssetDatabase.GetAssetPath (obj);
//本地测试:建议最后将Assetbundle放在StreamingAssets文件夹下,如果没有就创建一个,因为移动平台下只能读取这个路径
//StreamingAssets是只读路径,不能写入
//服务器下载:就不需要放在这里,服务器上客户端用www类进行下载。
string targetPath = Application.dataPath + &/StreamingAssets/& + obj.name + &.assetbundle&;
if (BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies, BuildTarget.Android))
if (BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies))
Debug.Log(obj.name +&资源打包成功&);
Debug.Log(obj.name +&资源打包失败&);
//刷新编辑器
AssetDatabase.Refresh ();
} & & & & 此脚本要注意的地方:
& & & & & & & &1、头文件与脚本放置的位置;
& & & & & & & &2、注意第20行与第21行,分别是用来打包使用在Android平台上的AB文件和使用在PC端上的AB文件)
& & & & 在哪个平台测试就要用相应方式导出的AB文件,不能混用(区别在于最后的那个参数);
& & & & & & & &3、一次只能打包一个资源(批量打包请继续研究雨松的教程)。
& & & & & & & &4、记得在Assets下创建一个StreamingAssets文件夹(如果原本没有的话)
& & & & 脚本保存完之后,刷新工程(保存一下),会在Unity菜单栏出现Custom Editor/Create AssetBunldes Main选项。打包流程:将要打包的模型资源保存成prefab → 选中此prefab → 选择Create AssetBunldes Main菜单项 → 导出成功。将导出得到的.assetbundle文件通过数据线放入手机SD卡根目录下等待测试吧。
& & & &(2)打开ImageTarget下的脚本DefaultTrackableEventHandler.cs
& & & & &1、加入头文件using System.C & & & &
& & & & &2、在此类中添加路径变量:(我在PC端测试时才使用到此路径,在Android端时使用了绝对路径)
//不同平台下StreamingAssets的路径是不同的,这里需要注意一下。
public static readonly string PathURL =
#if UNITY_ANDROID
&jar:file://& + Application.dataPath + &!/assets/&;
#elif UNITY_IPHONE
Application.dataPath + &/Raw/&;
#elif UNITY_STANDALONE_WIN || UNITY_EDITOR
&file://& + Application.dataPath + &/StreamingAssets/&;
#endif & & & & &
& & & & &3、添加一个函数:
//读取一个资源
private IEnumerator LoadMainGameObject(string path)
WWW bundle = new WWW(path);
//加载到游戏中
yield return Instantiate(bundle.assetBundle.mainAsset);
bundle.assetBundle.Unload(false);
& & & & & 4、在OnTrackingFound()函数的最后添加下列语句:
& & & & & 表示识别到识别图之后,加载相应的模型进场景。
//
StartCoroutine(LoadMainGameObject(PathURL + &carcar.assetbundle&));//PC端测试
StartCoroutine(LoadMainGameObject(&file:///storage/emulated/0/carcar.assetbundle&));//Android端测试(绝对路径)
StartCoroutine(LoadMainGameObject(&file:///& + Application.persistentDataPath + &/carcar.assetbundle&));//Android端测试(相对路径) & & & & & 这三句分别用于PC端测试和Android端测试。Android端的路径请注意路径格式,在真机中Application.persistentDataPath 得到的路径名是 /mnt/sdcard/Android/data/com.xxx.xxx/files,因此AB文件也要相应的找到那个文件夹的位置放置。
& & & & & 5、在OnTrackingLost()函数最后添加下列语句:
& & & & & 表示在识别图丢失之后将识别图上对应的模型从场景中删除
Destroy(GameObject.Find(&carcar(Clone)&)); & & & & & &从上面语句我们可以注意到,从AB文件读取来的资源会自动在后面添加 (Clone) ,比如我的资源本身名字是carcar的,加入场景后自动变成了carcar(Clone),因此操作时应该对物体名字做出相应的修改。
& & & & &(3)创建一个脚本:ModelShow.cs (注意不能放在Editor文件夹中,与步骤(1)区分开来)
& & & & & 此脚本用来对识别到识别图后加载进场景的模型进行位置、大小等方面的调节,因为模型加载进场景中时默认坐标为0,0,0 ,大小为原始大小,不利于我们观看。
using UnityE
using System.C
public class ModelShow : MonoBehaviour {
// Use this for initialization
void Start () {
count = 0;
// Update is called once per frame
void Update () {
if (count == 0)
if (GameObject.Find(&carcar(Clone)&))
obj = GameObject.Find(&carcar(Clone)&);
obj.transform.parent = GameObject.Find(&ImageTargetCar&).
obj.transform.position = GameObject.Find(&ImageTargetCar&).transform.
obj.transform.localScale = new Vector3(0.024f, 0.024f, 0.024f);
if (GameObject.Find(&carcar(Clone)&) == null)
count = 0;
& & & & &值得注意的是:如果模型身上带有脚本,在工程中最好也存在对应的脚本,加载模型到场景中后会根据脚本名字寻找对应的脚本重载挂在物体上。
&& & & & &最后一步:导出APK时别忘了打开SD卡读取权限。
& & & & &Player Settings → Other Settings → Write Access 选择 External(SDCard)
& & & & & & &完成,导出APK测试吧!
最新喜欢:
发布于: 16:36
发布于: 11:18
那关于AB素材包中模型与识别图的相对位置以及相对大小属性版主有所研究吗
发布于: 16:57
为什么我的在电脑上测试没问题,在手机上没有任何反应啊?
发布于: 15:15
这个教程有没有Demo啊。
发布于: 09:15
楼主为什么我的一打包出现一堆错误??500 || this.offsetHeight>500){if(this.offsetWidth/500 > this.offsetHeight/500){this.width=500;}else{this.height=500;}}" style="max-width:500max-height:500" onclick="if(this.parentNode.tagName!='A'&&this.width>screen.width-461) window.open(this.src);" />
发布于: 08:46
:楼主为什么我的一打包出现一堆错误?? 脚本编译有错误 检查一下
发布于: 17:48
有更详细点的教程吗?层次视图中的Imagetarget要删除吗?要启用云识别吗?
发布于: 21:11
楼主,你好,我想问下,我在实现了读取SD卡加载模型后,如果加入了交互(LeanTouch)后,有时手指划动几下后,模型会消失,卡住
发布于: 21:13
楼主,你好,我想问下,我在实现了读取SD卡加载模型后,如果加入了交互(LeanTouch)后,有时手指划动几下后,模型会消失,卡住,不知你那边是否会出现这样的情况!!
发布于: 10:54
发布于: 10:40
嗯,试了一下,是可以的,谢谢大神
发布于: 11:10
:好东西 问一下 &按照这个做的话 &当识别到的时候会有模型闪的一下 &该怎么处理呢
发布于: 15:35
版主,如果模型已经设置好了位置大小等属性,然后再打包,最后加载完还需要去设定这些属性吗?我在预制中加入了一个capsule模型,已经摆好位置大小,然后生成prefab,再打包成assetbundle,加载后也出现了capsule(clone),但是无法渲染出来。
发布于: 15:50
打印了Capsule(Clone)的属性,和打包前设定的一样,就是渲染不出来
[xiji321于 17:30编辑了帖子]
您需要登录后才可以回帖,&或者&1722人阅读
Unity3D开发(60)
1、我们一般把资源和代码作为两个工程。资源工程导出assetbundle供代码工程使用。这么做主要是防止资源过多导致代码工程启动速度慢,影响开发效率。
2、尽量避免使用Resources文件夹。这个文件夹就相当于一个系统默认的AssetBundle。只不过不能增量更新。它会严重影响打包速度。我们把资源从Resources移出来后,打包时间从半个多小时,减少到2~5分钟。
2、Shader都要加入到Proejct Settings--Graphics--Always Included Shaders中。否则代码工程中没有使用到的Shader可能会被裁剪,导致的问题是Shader丢失。
3、Shader要打包到一个AssetBundle中,即Shader需要指定一个AssetBundle包名。如果Shader没有指定的话,那么每个AB包都会包含这个Shader。一方面造成资源浪费。另一方面当Shader被修改的时候,所有的模型都需要重新导出。另外由于我们代码工程里面肯定也维护了一份Shader,模型实际使用的Shader是它自己包内的,不是代码工程的Shader。
& & & 这里要注意,2和3必须都要做。之前我认为加入到Always Included Shaders中,Unity打包ab的时候就不会打包Shader。然而并不是这样。这个列表只负责预先加载Shader。防止资源被裁剪,以及临时加载造成的卡顿。
4、Unity打包后的Shader是平台相关的,Editor下是无法直接使用的。所以Editor下加载AB包中的模型或特效会出现Shader错误。这个时候需要重新指定一下Shader,即遍历所有的Render把Material的Shader重新附下值。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1016916次
积分:12360
积分:12360
排名:第1098名
原创:195篇
转载:72篇
评论:509条
(1)(1)(5)(1)(2)(1)(2)(3)(2)(1)(1)(2)(4)(2)(2)(1)(9)(2)(3)(7)(8)(6)(10)(5)(8)(3)(7)(5)(3)(2)(1)(3)(7)(3)(7)(10)(1)(1)(11)(26)(25)(14)(2)(2)(1)(10)(4)(1)(1)(1)(2)(1)(11)(3)(2)(1)(2)(3)(2)(2)(1)Unity5新的AssetBundle系统使用心得
时间: 01:01:50
&&&& 阅读:901
&&&& 评论:
&&&& 收藏:0
标签:& & & & Unity的AssetBundle系统是对资源管理的一个扩展,动态更新,网页游戏,资源下载都是基于AssetBundle系统的。但是不得不说,这个系统非常恶心,坑很深。至于有多深,请看这篇文章:&
& & & & 原先的AssetBundle系统需要自己写一大坨导出的代码(BuildPipeline),这个新手会无从下手,老手也经常会被坑到。想正确处理好资源的依赖关系从而保证资源完整而又不会产生重复资源,确实不是一件非常容易的事情。 & & &&
& & & & Unity5新的AssetBundle系统大大简化了这一操作。Unity打包的时候会自动处理依赖关系,并生成一个.manifest文件,这个文件描述了assetbundle包大小、crc验证、包之间的依赖关系等等,是一个文本文件。加载资源的时候Unity会自动处理好其依赖包的加载。
& & & & 打包代码简化为一个函数(其实也没什么必要了,因为流程固定了,做成内嵌的菜单选项也没什么影响)
BuildPipeline.BuildAssetBundles(outputPath);
& & & &执行这个函数,它会自动打包工程目录下的所有的assetbundle,函数足够智能,它只会打包有修改的资源。
& & & &如何添加一个AssetBundle呢?
& & & &很简单,在资源属性窗口底部有一个选项,这个地方设置AssetBundle的名字。它会修改资源对应的.meta文件,记录这个名字。 AssetBundle的名字固定为小写。另外,每个AssetBundle都可以设置一个Variant,其实就是一个后缀,实际AssetBundle的名字会添加这个后缀。如果有不同分辨率的同名资源,可以使用这个来做区分。
& & & 我手头的模型资源非常多,所以我又写了个脚本自动遍历prefab的meta文件,添加AssetBundle名字。有一个需要注意的地方就是.meta文件貌似权限问题,无法直接写入,需要删除原文件,然后使用新的文件替换。。
# -*- coding: utf-8 -*-
import os, sys,
EXT_LIST = ['.prefab.meta', '.png.meta', '.jpg.meta'];
def doWork(path):
for root, dirs, files in os.walk(path):
for file in files:
for ext in EXT_LIST:
if file.endswith(ext):
fullPath = os.path.join(root, file)
fullPath = fullPath.replace('\\', '/')
prefabName = fullPath.replace(path, '');
prefabName = prefabName[:prefabName.find('.')] + '.data';
fileData = [];
fp = open(fullPath, 'r');
for line in fp:
if line.find('assetBundleName:') != -1:
fileData.append('
assetBundleName: ' + prefabName.lower() + '\n');
fileData.append(line);
fp.close();
# os.remove(fullPath);
fpw = open(fullPath + '.tmp', 'w');
fpw.writelines(fileData);
fpw.close();
os.remove(fullPath)
shutil.copy(fullPath + '.tmp', fullPath);
os.remove(fullPath + '.tmp')
doWork(r'Assets/Resources/Prefab/')
os.system('PAUSE')
c#编辑器扩展(与python代码功能一样,喜欢哪个用哪个)
public class ExportAssetBundles : Editor
// 设置assetbundle的名字(修改meta文件)
[MenuItem(&Tools/SetAssetBundleName&)]
static void OnSetAssetBundleName()
UnityEngine.Object obj = Selection.activeO
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
string[] extList = new string[] { &.prefab.meta&, &.png.meta&, &.jpg.meta& , &.tga.meta& };
EditorUtil.Walk(path, extList, DoSetAssetBundleName);
//刷新编辑器
AssetDatabase.Refresh();
Debug.Log(&AssetBundleName修改完毕&);
static void DoSetAssetBundleName(string path)
path = path.Replace(&\\&, &/&);
int index = path.IndexOf(EditorConfig.PREFAB_PATH);
string relativePath = path.Substring(path.IndexOf(EditorConfig.PREFAB_PATH) + EditorConfig.PREFAB_PATH.Length);
string prefabName = relativePath.Substring(0, relativePath.IndexOf('.')) + EditorConfig.ASSETBUNDLE;
StreamReader fs = new StreamReader(path);
List&string& ret = new List&string&();
while((line = fs.ReadLine()) != null) {
line = line.Replace(&\n&, &&);
if (line.IndexOf(&assetBundleName:&) != -1) {
assetBundleName: & + prefabName.ToLower();
ret.Add(line);
fs.Close();
File.Delete(path);
StreamWriter writer = new StreamWriter(path + &.tmp&);
foreach (var each in ret) {
writer.WriteLine(each);
writer.Close();
File.Copy(path + &.tmp&, path);
File.Delete(path + &.tmp&);
[MenuItem(&Tools/CreateAssetBundle&)]
static void OnCreateAssetBundle()
BuildPipeline.BuildAssetBundles(EditorConfig.OUTPUT_PATH);
//刷新编辑器
AssetDatabase.Refresh();
Debug.Log(&AssetBundle打包完毕&);
&&国之画&&&& &&&&chrome插件
版权所有 京ICP备号-2
迷上了代码!&&&&&&&&&&&
通常我们在游戏程式执行过程,并不希望一次将全部的资源都载入,而比较希望实际上有使用到的才载入,以免占用多余的记忆体,所以我们可能会尽量规划好不同功能的场景,在需要时才载入场景并释放掉前个场景中不需要的资源,或是将资源放在 Resource 资料夹中,在真正需要时才利用 Resources.Load() 把资源载入;这些都是不错的管理方法,但是当我们游戏中的资源相当多时,输出的程式还是会相当庞大,而且如果是时常会更新内容资源的游戏,也会因为庞大的资源而造成编译输出时要花相当多的时间;特别是手机或网页游戏,几乎输出最後的结果只是一个档案而已,这个档案如果很庞大,将可能造成下载或启动游戏的不方便;这时候我们可能就要考虑制作 AssetBundle。
制作与游戏主程式分离的资源包可以得到某程度上的好处,我们可以让游戏主程式只管理游戏逻辑的部份,当有需要某些资源则从外部的 AssetBundle 载入资源,这样一来,如果有任何游戏更新只是添加或更换资源,并没有变更到游戏程式逻辑或内容部署,我们只需要重新打包资源的部份就能完成更新,而不再需要整个游戏重新编译输出。
例如我们有已上架的 iPhone 游戏,因为特定节日想把游戏内的图片换成另一种气氛,通常就必须要重新输出,然後送审,等节日过後又想把图片换回原本的,要再次输出游戏,再送审,一方面送审费时费工,另一方面是从送审到架上游戏更新完成的时间,我们并不能准确掌控;如果事先将资源分离打包成 AssetBundle 储存在我们自己管理的伺服器中,在这种需要换图的时刻,只需要打包自行在伺服器中将 AssetBundle 更新即可,可以省掉不断送审的麻烦,时间上也更能掌控,另外就是游戏主程式中没有庞大的资源资料,主程式也就会小一点,那麽玩家要下载我们的游戏也会更快速些。
还有就是,我们输出为网页游戏时一定会发现到,输出结果只是一个 html 档及一个 unity3d 档,应该有写过网页的人都知道,网页的运作是浏览器将网页中的文件、图片、音乐、影像& 等等的资料下载到客户端暂存之後再执行呈现出结果,那麽如果我们游戏内容的庞大资源都与主程式编在一起的话,那麽输出後的 unity3d 档应该也会不小,此时我们可能要思考一下,我们希望玩家花多久时间完成启动页面再进行游戏呢?也许 Unity 的 Web Player 有办法解决这个问题使玩家不会花太多时间启动页面,但是正因为网页的运作方式是如此,所以当我们在伺服器端更新时,玩家只要没有重新载入页面,玩到的游戏内容始终是旧版的,此时又要考虑到,如果没有更新游戏逻辑或内容规则的情况下,是否有必要强制玩家中断游戏重新载入页面;如果将资源都打包成 AssetBundle 分离出来,那麽 unity3d 档这个主程式的部份将会变得更小,而当有任何资源更新时只需要在伺服器端将 AssetBundle 的档案换掉,玩家不需要重新载入页面一样能体验到更新後的内容。
以上主要说明 AssetBundle 是个很重要又好用的东西,毕竟在没变更程式或游戏部署的情况下,没必要去重新 Build 游戏主程式,以下就来说明如何制作 AssetBundle &
首先要自定义选单命令,使我们能够操作何时开始制作 AssetBundle 及如何做,接下来我们需要能够指定哪些资源将打包成 AssetBundle,所以需要使用到 Editor class 的 Selection.GetFiltered(),还有就是要过滤哪些型别的资源才是我们要的,选择错误则略过,不要处理,最後再使用 BuildPipeline.BuildAssetBundle() 建立 AssetBundle 即可,以下范例会在 Unity 专案资料夹(不是 Asset 资料夹)中建立 _AssetBunldes 资料夹用来存放打包好的 AssetBundle,打包的目标为 GameObject、Texture2D、Material 三种型别的资源,如果储存档案路径档名相同则会先删除旧档:
[MenuItem("Custom Editor/Create AssetBunldes")]
static void ExecCreateAssetBunldes(){
// AssetBundle 的資料夾名稱及副檔名
string targetDir = "_AssetBunldes";
string extensionName = ".assetBunldes";
//取得在 Project 視窗中選擇的資源(包含資料夾的子目錄中的資源)
Object[] SelectedAsset = Selection.GetFiltered(typeof (Object), SelectionMode.DeepAssets);
//建立存放 AssetBundle 的資料夾
if(!Directory.Exists(targetDir)) Directory.CreateDirectory(targetDir);
foreach(Object obj in SelectedAsset){
//資源檔案路徑
string sourcePath = AssetDatabase.GetAssetPath(obj);
// AssetBundle 儲存檔案路徑
string targetPath = targetDir + Path.DirectorySeparatorChar + obj.name + extensionN
if(File.Exists(targetPath)) File.Delete(targetPath);
if(!(obj is GameObject) && !(obj is Texture2D) && !(obj is Material))
//建立 AssetBundle
if(BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies)){
Debug.Log(obj.name + " 建立完成");
Debug.Log(obj.name + " 建立失敗");
以上是直接将选择的资源打包成 AssetBundle,另外,你也可以在打包前依需求将型别为 GameObject 的资源 Instantiate 到场景中添加需要的 Component 并回到 Project 中建立 Prefab,最後再以这个 Prefab 打包成 AssetBundle,一切就看个人如何运用了。
制作好的 AssetBundle 最终目的还是要在游戏中载入,以下用简短的范例在游戏画面上显示一个按钮,当按下按钮之后载入型别为 GameObject 的 AssetBundle 并利用 Instantiate() 使其实例化而在场景中产生 GameObject:
void OnGUI(){
if(GUI.Button(new Rect(5,35,100,25) , "Load GameObject")){
StartCoroutine(LoadGameObject());
private IEnumerator LoadGameObject(){
// AssetBundle 檔案路徑
string path = string.Format("file://{0}/../_AssetBunldes/{1}.assetBunldes" , Application.dataPath , "TestGameObject");
載入 AssetBundle
WWW bundle = new WWW(path);
//等待載入完成
//實例化 GameObject 並等待實作完成
yield return Instantiate(bundle.assetBundle.mainAsset);
//卸載 AssetBundle
bundle.assetBundle.Unload(false);
范例中的档案路径(path)字串是以此程式码在 Unity 编辑器中执行为例,实作时应该以实际执行平台及个人喜好指定 AssetBundle 存放路径,详情可参考官方文件 Application.dataPath 说明;因为载入的资源必须要等待载入完成以确保游戏运作正常,在 C# 使用 yield 除了回传型别为 IEnumerator 之外,调用时也必须使用 StartCoroutine() 调用,如果使用 Javascript 则可直接呼叫该 function;如果 AssetBundle 已载入过,下次再请求载入将会出现 [The asset bundle &档名' can't be loaded because another asset bundle with the same files are already loaded]的错误讯息,即使使用区域变数(范例中的 bundle)仍然会得到这个错误讯息,所以必须在每次载入使用完后将 AssetBundle 卸载;另外,要特别注意的是载入 AssetBundle 到卸载期间可能消耗些微时间,如果连续调用载入同一个 AssetBundle,造成在卸载前重复载入也可能出现前面的错误讯息,所以,最好是能定义个阵列或其它变数来记录载入中的 AssetBundle 有哪些,以此变数内容在载入 AssetBundle 前检查。
阅读(...) 评论()

我要回帖

更多关于 noval 的文章

 

随机推荐