unity3d 依赖打包和加载

2016-06-22 17:06:16 ywjun0919 阅读数 1561
1. 加载顺序问题,被依赖的资源必须先加载完成。 
2. Shader依赖丢失问题:Editor版、Android版 均正常,但在IOS上丢失。

      问题现象:丢失的文件Shader1 与 Shader2具有相同Shader名,且文件内容完全一样,但是Shader1没有丢失被依赖,改名后也不行
      问题原因猜测:编译IOS平台与其他平台有区别(文件相同的Shader会出问题)
      解决方法:批量替换使用Shader2的材质
      **该问题的具体原因还未找到**
2017-09-19 15:59:33 strugglebydreamlin 阅读数 3595

写在前面:

很久很久很久。。没有更新了,当然写这个需要坚持,最近因为工作调整转了移动开发,之后的博客更新会以移动开发为主了,当然写博客纯粹是为了记录自己的学习过程,毕竟好记性不如硬键盘嘛,有什么错误的地方还望大家指正。

Unity自5.0开始提供了新的AssetBundle打包Api,以下是新旧Api对比:

旧版:
图1
图1


打包方式:

public class ExportAssetBundles {
        [MenuItem("Assets/Build AssetBundle From Selection - Track dependencies")]
        static void ExportResource () {
            // Bring up save panel
            string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d");
            if (path.Length != 0) {
                // Build the resource file from the active selection.
                Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
                BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, 0);
                Selection.objects = selection;
            }
        }
        [MenuItem("Assets/Build AssetBundle From Selection - No dependency tracking")]
        static void ExportResourceNoTrack () {
            // Bring up save panel
            string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d");
            if (path.Length != 0) {
                // Build the resource file from the active selection.
                BuildPipeline.BuildAssetBundle(Selection.activeObject, Selection.objects, path);
            }
        }
    }

而在目前最新的Unity2017版本中,旧版本的打包方式已经被完全废弃了。

新版打包Api:

图2
图2


public class PackageAssetBundle : MonoBehaviour {
    [MenuItem("Build/Build AssetBundles")]
    static void buildAssetBundles()
    {
        string buildPath = Application.dataPath + "/Assets/Abs";//打包路径
        if (!Directory.Exists(buildPath))
            Directory.CreateDirectory(buildPath);
        BuildPipeline.BuildAssetBundles(buildPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
    }

}

可以看到打包的代码非常简单。

如何指定需要打包的assets?

有几种方式:
第一种是直接在Inspecot面板进行指定:
图3
图3


打包时Api将选择所有你指定了AssetBundle VarName的资源,然后统一进行打包,如图3中该资源被打包后最终会生成cube.ab和cube.ab.manifest两个文件,manifest文件中记录的是cube.ab的信息,如依赖资源等等。

第二种呢是在代码中进行指定:

public class BuildAssetBundlesBuildMapExample : MonoBehaviour
{
    [MenuItem("Example/Build Asset Bundles Using BuildMap")]
    static void BuildMapABs()
    {
        // Create the array of bundle build details.
        AssetBundleBuild[] buildMap = new AssetBundleBuild[2];

        buildMap[0].assetBundleName = "enemybundle";

        string[] enemyAssets = new string[2];
        enemyAssets[0] = "Assets/Textures/char_enemy_alienShip.jpg";
        enemyAssets[1] = "Assets/Textures/char_enemy_alienShip-damaged.jpg";

        buildMap[0].assetNames = enemyAssets;
        buildMap[1].assetBundleName = "herobundle";

        string[] heroAssets = new string[1];
        heroAssets[0] = "char_hero_beanMan";
        buildMap[1].assetNames = heroAssets;

        BuildPipeline.BuildAssetBundles("Assets/ABs", buildMap, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
    }
}

当然指定方式灵活度还是不错的,你可以直接Selection,然后再对Selection的资源构造成buildMap,再进行处理即可。

打包说完了,接着就是加载咯。

如何加载?

加载可以分为三个步骤:
1.首先读取Manifest文件,读取需要加载ab(AssetBundle)的依赖资源
2.加载依赖ab
3.加载ab

ok,说了这么多好像。。。

那好吧,接下来还是看个小例子吧.

打包资源如下:

图4
图4


依赖关系:

Prefabs depends Materials
cube.ab depend on texure.ab
capsule.ab depend on emission.ab

我们只需要把材质挂在prefabs上即可,Unity新的打包方式会自动帮我们处理依赖关系的,运行打包代码后得到如下目录:
图5
图5
show in explorer来看看:
图6
图6
如图6所示,打包完成后,在打包路径下生成了一个和当前路径文件夹同名的AssetBundle文件和一个manifest文件,这两个文件是有用的,别随便删除了哈。。。这里写图片描述

但是改个名字什么的还是可以的哈。

接着来看一下文件大小,毕竟我们使用依赖打包的目的是为了减小包的大小不是嘛:

ab size
cube.ab 1.72KB
capsule.ab 1.78KB
texure.ab 147KB
emission.ab 510KB

哎,没错的吧,cube和capsule中确实剔除了材质,大小只有一点多KB,来看一下依赖信息,就是它:
图7
图7
我们可以发现里面清晰的记录每个ab的依赖资源信息。


加载方式

AssetBundle的加载方式有好几种,这里介绍两种常用的:

1.LoadFromFile:

public GameObject loadAssetBundleFiles(string filePath, string manifestBundleName, string abFileName, string prefabFileName)
    {
        /**
         * 1.首先从打包路径获取依赖
         * 这里加载的就是生成的同名文件ab,这里应该是filePath/Abs,当然会这个名称也可以修改
         */
        AssetBundle manifestBundle = AssetBundle.LoadFromFile(getManifestFilePath(filePath, manifestBundleName));
        /**
         * 2.获取依赖资源列表
         */
        if (manifestBundle != null)
        {
            try
            {
                AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");//固定加载方式,通过 assetbundle Abs加载Abs.manifest
                manifestBundle.Unload(false);
                //获取加载ab的依赖信息,参数为ab名称,如cube.ab
                string[] dependsFile = manifest.GetAllDependencies(abFileName);
                if (dependsFile.Length > 0)
                {
                    //根据获取到的依赖信息加载所有依赖资源ab
                    AssetBundle[] dependsBundle = new AssetBundle[dependsFile.Length];
                    for (int i = 0; i < dependsFile.Length; i++)
                    {
                        String fp = generateAbsoluteFile(filePath, dependsFile[i]);
                        Debug.Log(String.Format("depends:{0}:{1}", i, dependsFile[i]));
                        dependsBundle[i] = AssetBundle.LoadFromFile(fp);
                    }
                }
            }
            catch (InvalidCastException e)
            {
                Debug.LogException(e);
            }

            /**
             * 3.最后加载ab
             * 注意这里的LoadAsset的参数是Prefab的名称,无后缀,如cube而非cube.ab或cube.prefab
             */
            AssetBundle ab = AssetBundle.LoadFromFile(generateAbsoluteFile(filePath, abFileName));
            GameObject go = ab.LoadAsset(prefabFileName) as GameObject;
            ab.Unload(false);
            return go;
        }
        return null;
    }

2.WWW加载:

IEnumerator wwwGetAssetBundles(string filePath, string manifestName, string abFileName, string prefabFileName)
    {
        //WWW www = WWW.LoadFromCacheOrDownload(getManifestFilePath(filePath), 0);
        WWW www = new WWW(manifestName);
        yield return www;
        if (!string.IsNullOrEmpty(www.error))
        {
            Debug.LogError(www.error);
        }
        else
        {
            /**
             * 1.首先从打包路径获取依赖
             */
            AssetBundle manifestBundle = www.assetBundle;
            /**
             * 2.获取依赖资源列表
             */
            if (manifestBundle != null)
            {

                AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                manifestBundle.Unload(false);
                www.Dispose();
                string[] dependsFile = manifest.GetAllDependencies(abFileName);
                if (dependsFile.Length > 0)
                {
                    AssetBundle[] dependsBundle = new AssetBundle[dependsFile.Length];
                    for (int i = 0; i < dependsFile.Length; i++)
                    {
                        String fp = generateAbsoluteFile(filePath, dependsFile[i]);
                        Debug.Log(String.Format("depends:{0}:{1}", i, dependsFile[i]));
                        www = new WWW(fp);
                        yield return www;
                        if(www.error == null)
                        {
                            dependsBundle[i] = www.assetBundle;
                            www.Dispose();
                        }                        
                    }
                }

                /**
                 * 3.最后加载ab
                 */
                AssetBundle ab = AssetBundle.LoadFromFile(generateAbsoluteFile(filePath, abFileName));
                GameObject go = ab.LoadAsset(prefabFileName) as GameObject;
                if (go != null)
                {
                    GameObject.Instantiate(go);
                }
                ab.Unload(false);
            }
        }
    }

代码和第一种加载方式大致相同。

最后说一点使用时需要注意的地方,举个例子,比如说我们有一公用的资源,A、B、C三个ab都需要依赖D,那么在程序中我们需要加载A时会先加载D,然后加载A,这个时候B也需要被加载了,因为程序不知道D已经被加载了,所以再次去读取依赖信息,尝试加载D,这样就会消耗一些cpu的时间了,因此在我们实际开发中应该更加合理的去管理公用的ab资源,对于不常用的公用ab使用过后及时卸载,下次使用再进行加载,而对用经常使用需要常驻内存的ab资源,最好在程序中设置一个标识,标记其是否已经被加载,存在于内存中,然后在加载依赖该公用ab的ab资源时判断该公用ab是否已被加载,当然,也可能会存在一个ab依赖多个公用资源,这种情况就需要开发者更加谨慎的去处理依赖关系了,最好建立统一的管理,做到逻辑清晰。

Over:最后贴出
官网链接:BuildAssetBundles
Demo下载链接(Unity5.x):Demo地址
密码:m4q2


2019-01-19 16:57:09 Mr_Sun88 阅读数 1842

 

                 Unity3D AssetBundle的打包和加载


 

目录

1、博文介绍

2、具体思路和写法

(1)AB包的打包

(2)AB包的加载

(3)AB包卸载

3.资源包下载

4.推送

5.结语


1、博文介绍

       本篇博客主要起记录和学习作用,简单的介绍一下AB包的打包和几种不同方法加载AB包的方式,若各位同学有幸看到本篇博客,希望能够对你有所帮助。


2、具体思路和写法

(1)AB包的打包

        打包这里就不在做过多的介绍,直接拷贝官方的方法,很简单也很直观,这里只要注意在Unity内为资源设置好名字和后缀就好了,在资源商店里有一个免费的AB包管理软件,评价好像挺高的,博主回头抽时间研究一下,再写一篇文章介绍一下吧(完了,不知不觉又立了个flag)。

using  UnityEditor;
using  System.IO;

/// <summary>
/// time:2019/1/19
/// author:King.Sun
/// description:AB包打包
/// </summary>
public class LoadAB{


	[MenuItem("Assets/Bulid AssetBundle")]
	static void BuildAssetBundle()
	{
		//创建目录
		string abPath = "Assets/AssetBundles";
		if (!Directory.Exists(abPath))
		{
			Directory.CreateDirectory(abPath);
		}
		//打包 os/win
		#if UNITY_STANDALONE_WIN
				BuildPipeline.BuildAssetBundles(abPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
		#elif UNITY_STANDALONE_OSX
				BuildPipeline.BuildAssetBundles(abPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneOSX);
		#endif
		
	}
	
}

       这里我就直接本地加载了,考虑到同学你用的是win还是mac这里加了个宏,不同平台打包设定是不一样的,同学用到了自行查API,打包就不细说了。

博主这里将一个Cube打到一个AB包里,包名为cube.unity3d,后缀为assetbundle。

       打包后就可以看到在我们设定的目录下,生成了cube.unity3d的AB包和对应的manifest描述文件,描述文件内记载了,我们该AB包里的资源和依赖,至此打包工作完成

(2)AB包的加载

这里我们通过本地路径来加载AB包,不同平台下的路径是不同的这里需要各位同学去注意一下

                //不同平台下路径不同
		#if UNITY_ANDROID
		_abPath = "jar:file://" + Application.dataPath + "!/assets/AssetBundles/";
		#elif UNITY_IPHONE
		_abPath = Application.dataPath + "/Raw/AssetBundles/";
		#elif UNITY_STANDALONE_WIN || UNITY_EDITOR
		_abPath = Application.dataPath + "/AssetBundles/";
		#else
		_abPath = string.Empty;
		#endif

这里我们为我们的AB包路径 _abPath赋值。

 

加载方式1:  LoadFromMemoryAsync 异步加载,通过LoadFromMemoryAsync加载从服务器或本地获取的字节流来加载

StartCoroutine(LoadABPackage1(_abPath));

IEnumerator LoadABPackage1(string path)
	{
		AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path+"cube.unity3d.assetbundle"));
		yield return request;
		ab = request.assetBundle;
		Object cube = ab.LoadAsset("Cube.prefab");
		Instantiate(cube);
	}

加载方式2:LoadFromMemory 同步加载,通过LoadFromMemoryAsync加载从服务器或本地获取的字节流来加载

LoadABPackage2();


void LoadABPackage2()
{
	ab = AssetBundle.LoadFromMemory(File.ReadAllBytes(_abPath+"cube.unity3d.assetbundle"));
	Object cube = ab.LoadAsset("Cube.prefab");
	Instantiate(cube);
}

 

加载方式3:LoadFromFileAsync 直接通过本地路径异步加载,只能加载本地AB包

StartCoroutine(LoadABPackage3(_abPath));


	IEnumerator LoadABPackage3(string path)
	{
		AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path+"cube.unity3d.assetbundle");

		yield return request;

		ab = request.assetBundle;
		Object cube = ab.LoadAsset("Cube.prefab");
		Instantiate(cube);
	}

 

加载方式4:LoadFromFile 直接通过本地路径同步加载,只能加载本地AB包

LoadABPackage4(_abPath);

	void LoadABPackage4(string path)
	{
		ab = AssetBundle.LoadFromFile(path + "cube.unity3d.assetbundle");
		Object cube = ab.LoadAsset("Cube.prefab");
		Instantiate(cube);
	}

 

加载方式5:WWW异步加载方法 本地服务器均可

StartCoroutine(LoadABPackage5(_abPath));

	IEnumerator LoadABPackage5(string path)
	{
		WWW www = WWW.LoadFromCacheOrDownload(path+"cube.unity3d.assetbundle",1);
		yield return www;
		if (!string.IsNullOrEmpty(www.error))
		{
			Debug.Log(www.error);
			yield break;
		}

		ab = www.assetBundle;
		Object cube = ab.LoadAsset("Cube.prefab");
		Instantiate(cube);
	}

 

加载方式6:UnityWebRequest 异步加载方式 本地服务器均可

StartCoroutine(LoadABPackage6(_abPath));

IEnumerator LoadABPackage6(string path)
	{
		UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(path+"cube.unity3d.assetbundle");
		yield return request.Send();
		ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
		Object cube = ab.LoadAsset("Cube.prefab");
		Instantiate(cube);
	}

(3)AB包卸载

AssetBundle.Unload(false),卸载AB包资源,同时保留内存中已加载的资源; 
AssetBundle.Unload(true),卸载AB包资源,同时卸载内存中已加载的资源,会造成资源的丢失

3.资源包下载

完整工程下载:https://download.csdn.net/download/mr_sun88/10927709


4.推送

先空着


5.结语

       本篇博客只是简单的介绍了AB包的打包和加载,希望有幸看到本篇文章的同学能有所收获,另博主能力有限,文中若有错误的地方期望各位看家可以指点交流。

       QQ交流群:806091680(Chinar)

       该群为CSDN博主Chinar所创,推荐一下!我也在群里!

       本文属于原创文章,转载请著名作者出处并置顶!!!!!

2020-05-04 21:03:37 qq_34191439 阅读数 79

步骤

  • 第一种:
    1.导出AndroidStudio 的工程,然后用AS打开工程,然后下载依赖,下载一次后,就会缓存相关依赖到本地,然后就可以Unity中打包了。
    2.如果还是不行呢?
    在AS中设置挂代理IP(网上可以找或者去买或者VPN设置中的代理Ip和端口)

  • 第二种
    不行:gradle 离线模式offline 开启。 成功了,但是unity无法成功,只能在androidStudio中打包

  • 第三种:成功,unity也可以正常下载了

1.关闭AndroidStudio的代理

在 Settings -> Appearance&Behavior -> System Settings -> HTTP Proxy 选中 No Proxy。

2.项目gradle.properties下面去掉代理相关

3.在C:\Users***.gradle文件夹下gradle.properties下面去掉代理相关
参考文章

相关成功案例:

buildscript {
    repositories {
       //1.这个链接根据报错的依赖动态选择 主要就是repository/?  这个? 是那个
       //2.可以在https://maven.aliyun.com 里面先搜索一下报错的依赖库,看看在那个库链接里面。
       
       // maven{ url 'https://maven.aliyun.com/repository/central'}
        maven{ url 'https://maven.aliyun.com/repository/google'}
        mavenCentral()
        google()
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.0'
}
}

allprojects {
    repositories {
    
      //  maven{ url 'https://maven.aliyun.com/repository/central'}
        maven{ url 'https://maven.aliyun.com/repository/google'}
        mavenCentral()
        google()
        jcenter()
        flatDir {
            dirs 'libs'
        }
    }
}
2018-09-17 20:26:41 qq_28474981 阅读数 1653

1.运行环境

1.1编辑器版本

使用Unity2017.3.1f

1.2 dll

2.目的

预制体打包时会将一份资源进行多份打包,导致数据冗余,所以需要根据依赖,将被多处引用的资源独立达成一个包,建立依赖关心,优化冗余。

3.流程

1.遍历所有资源文件,设置其包名为空

2. 遍历需要打包的文件夹下的所有文件,通过Unity资源接口获取所有依赖,统计对应的依赖数

3. 将在需要打包文件夹内的包名设置为文件名,将在需要打包文件夹外的依赖数大于2的文件设置包名为文件名

4.调用Unity的打包接口,会将该工程下的所有被设置包名的资源进行AB打包。

4.代码实现

4.1核心代码

public class ABUtils
{

    /// <summary>
    /// 资源放置的文件夹路径
    /// </summary>
    private static List<string> resDircectoryPaths = new List<string>(){ "Assets/Resources" };

    private static List<string> needDependenyPaths = new List<string>() { "Assets/Resources/Materials" };





    /// <summary>
    /// 初始化打包环境,将所有的文件的ab属性都置空
    /// </summary>
    static List<string> InitBuildEnvironment()
    {
        List<string> paths=new List<string>();
        string[] files = AssetDatabase.GetAllAssetPaths();
        for (int i = 0; i < files.Length; i++)
        {
            for (int j = 0; j < resDircectoryPaths.Count; j++)
            {
                if (files[i].Contains(resDircectoryPaths[j]))
                {
                    AssetImporter importer = AssetImporter.GetAtPath(files[i]);
                    paths.Add(files[i]);
                    importer.assetBundleName = String.Empty;
                }
            }
        }

        return paths;
    }

    public static void BuildAllAbAboutDependency(string outPath)
    {
        ResourceSystem.FileUtils.PrecessDirectoryExist(outPath);
        List<string> resPaths=InitBuildEnvironment();
        string fileName;
        string relationPath;
        ResourcesData data;
        bool isNeedDependcy;
        for (int i = 0; i < resPaths.Count; i++)
        {
            isNeedDependcy = false;
            fileName = PathUtils.GetFileNameWithoutExtension(resPaths[i]);
            if (ResourceSystemFacade.Inst.ResDict.ContainsKey(fileName))
            {
                data = ResourceSystemFacade.Inst.ResDict[fileName];
                relationPath = PathUtils.GetRelativePath("Assets", resPaths[i]);
                for (int j = 0; j < needDependenyPaths.Count; j++)
                {
                    if (relationPath.Contains(needDependenyPaths[j]))
                    {
                        isNeedDependcy = true;
                        if (data.DependeciedNum > 1)
                        {
                            AssetImporter importer = AssetImporter.GetAtPath(relationPath);

                            importer.assetBundleName = data.Name;
                            importer.assetBundleVariant = ResourceSystemFacade.FileExtension;
                        }
                    }
                }

                if (!isNeedDependcy)
                {
                    AssetImporter importer = AssetImporter.GetAtPath(relationPath);

                    importer.assetBundleName = data.Name;
                    importer.assetBundleVariant = ResourceSystemFacade.FileExtension;
                }

            }
            else
            {
                DebugUtils.DebugError("未包含文件" + fileName);
            }
        }
        DebugUtils.DebugRedInfo(outPath);
        BuildPipeline.BuildAssetBundles(outPath, BuildAssetBundleOptions.UncompressedAssetBundle, BuildTarget.StandaloneWindows64);
    }

}

 

可运行工程可以看:https://blog.csdn.net/qq_28474981/article/details/82749021