赞
踩
加载场景有两种方法,一种是常规的build settings添加scene,一种是通过AssetBundle加载Scene。
- using UnityEngine;
- using UnityEngine.SceneManagement;
-
- public class SceneTest : MonoBehaviour
- {
- void Start()
- {
- SceneManager.LoadSceneAsync("xxx1", LoadSceneMode.Additive);
- SceneManager.LoadSceneAsync("xxx2", LoadSceneMode.Additive);
- SceneManager.LoadSceneAsync("xxx3", LoadSceneMode.Additive);
- }
- }
上面是标准的异步加载场景,如果不在build settings中添加场景,直接加载scene,就会报下面的错。
因此对于需要热更新的项目,如果场景.unity资源变更了,无法在build settings中重新设置,只能用Assetbundle加载场景。
下面是打ab包的工具(BuildAssetBundleTool放到Scripts/Editor文件夹下)和标准化路径工具。
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEditor;
- using System.IO;
-
- /// <summary>
- /// 构建工具类:
- /// 创建了三种构建方法windows android ios。
- /// Build方法:从所有路径查找文件,排除meta后,把每个文件名作为被打包资源名和bundle名(当然一个bundle可以打包多个文件)
- /// </summary>
- public class BuildAssetBundleTool : Editor
- {
- //如何使用Build呢,直接添加工具栏
- [MenuItem("Tools/Build Windows Bundle")]
- static void BundleWindowsBuild()
- {
- Build(BuildTarget.StandaloneWindows);
- }
-
- //如何使用Build呢,直接添加工具栏
- [MenuItem("Tools/Build Android Bundle")]
- static void BundleAndroidBuild()
- {
- Build(BuildTarget.Android);
- }
-
- //如何使用Build呢,直接添加工具栏
- [MenuItem("Tools/Build IOS Bundle")]
- static void BundleIOSBuild()
- {
- Build(BuildTarget.iOS);
- }
-
- //为了能够构建多平台,需要把目标平台作为参数传入。
- static void Build(BuildTarget target)
- {
- //主要目的是收集这个build信息,需要打哪些文件,需要给bundle包用一个什么样的名字,BuildAssetBundles函数用到这个Build数组
- List<AssetBundleBuild> assetBundleBuilds = new List<AssetBundleBuild>();
-
- //第一步搜索出我们这个所有文件的文件名Directory.GetDirectories和Directory.GetFiles对应两种打包策略一个获取文件夹一个获取文件,GetFiles比较简单
- //searchPattern通配符,*是默认 https://www.cnblogs.com/ost/archive/2006/08/20/481625.html
- string[] files = Directory.GetFiles(PathUtil.BuildResourcesPath, "*", SearchOption.AllDirectories);
- //所有文件都找出来了,需要排除调meta文件
- for (int i = 0; i < files.Length; i++)
- {
- if (files[i].EndsWith(".meta"))
- {
- continue;
- }
- //创建一个需要build的Bundle
- AssetBundleBuild assetBundle = new AssetBundleBuild();
-
- //处理出来的路径斜杠可能不同。需要规范一下
- string fileName = PathUtil.GetStandardPath(files[i]);
-
- string assetName = PathUtil.GetUnityPath(fileName);//获取unity相对路径
- //一个assetBundle可以打包多个文件,这里只放一个文件
- assetBundle.assetNames = new string[] { assetName };//assetBundle是一个相对路径文件名
-
- //创建包名
- string bundleName = fileName.Replace(PathUtil.BuildResourcesPath, "").ToLower();
- assetBundle.assetBundleName = bundleName + ".ab";//Bundle需要后缀是.ab,,,,,,,,至此,Bundle的信息收集完了,需要放进list
-
- assetBundleBuilds.Add(assetBundle);
- }
-
- //为什么不用另一个重载BuildAssetBundles(string outputPath, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform),是因为需要自己去资源设置bundle名打标签,很麻烦
- //第二个参数把list转为array数组
- //第三个参数是压缩格式,选择默认
- //第四个参数是目标平台,先选择win
- if(Directory.Exists(PathUtil.BundleOutPath))
- {
- //判断是否有路径,如果有这个文件夹,就删掉文件,,递归recursive删掉所有文件和子文件。
- Directory.Delete(PathUtil.BundleOutPath, true);
- }
- Directory.CreateDirectory(PathUtil.BundleOutPath);//删除路径后,创建路径
-
- BuildPipeline.BuildAssetBundles(PathUtil.BundleOutPath, assetBundleBuilds.ToArray(), BuildAssetBundleOptions.None, target);
- }
- }
- /// <summary>
- /// 路径工具类:
- /// 1定义了所有用到的路径
- /// 2返回标准路径或返回unity下的几个文件夹的相对路径
- /// </summary>
- //因为所有路径都要用到,所以写入一个只读变量中,用来后期访问
- public class PathUtil
- {
- //为什么要把Application定义出来,因为每一次访问都需要GC一次,定义出来就访问一次;
- public static readonly string AssetPath = Application.dataPath;
-
- //只读的,需要打Bundle的目录
- public static readonly string BuildResourcesPath = AssetPath + "/BuildResources/";
-
- //Bundle输出目录
- public static readonly string BundleOutPath = Application.streamingAssetsPath;
-
- /// <summary>
- /// 获取Unity的相对路径
- /// </summary>
- /// <param name="path">绝对路径</param>
- /// <returns></returns>
- public static string GetUnityPath(string path)
- {
- if(string.IsNullOrEmpty(path))
- {
- return string.Empty;
- }
- //从Assets位置拿到相对目录
- return path.Substring(path.IndexOf("Assets"));
- }
-
- /// <summary>
- /// 获取标准路径
- /// </summary>
- /// <param name="path">路径</param>
- /// <returns></returns>
- public static string GetStandardPath(string path)
- {
- if (string.IsNullOrEmpty(path))
- {
- return string.Empty;
- }
- //先处理空格,在处理反斜杠
- return path.Trim().Replace("\\", "/");
- }
- }
这两个工具放到文件夹后,在unity工具栏的位置出现Editor新工具,选择第一个构建Bundle,就可以把创建好的场景打包到StreamingAssets路径下,,,在SceneTest中通过这个路径,加载AssetBundle包,就可以动态加载场景了。
重新编写SceneTest场景加载的脚本,挂到起始场景的任意物体上,就可以动态加载场景了。
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.SceneManagement;
- public class sceneTest : MonoBehaviour
- {
- // Start is called before the first frame update
- IEnumerator Start()
- {
- yield return LoadSceneAB("xxx1");
- yield return LoadSceneAB("xxx2");
- yield return LoadSceneAB("xxx3");
- }
-
- IEnumerator LoadSceneAB(string sceneName)
- {
- string assetBundleName = "scenes/" + sceneName +".unity.ab";
- string path = Application.dataPath + "/StreamingAssets/" + assetBundleName;
-
- AssetBundleCreateRequest myLoadedAssetBundle = AssetBundle.LoadFromFileAsync(path);
-
- yield return myLoadedAssetBundle;
-
- if (myLoadedAssetBundle != null)
- {
- SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
- }
- }
- }
运行前后
如果该场景中依赖于其他物体、材质等,也是通过打包才能获取的,那么需要将其他物体也通过AssetBundle获取,获取方法需要编写一个依赖文件,说明此资源依赖于哪些资源,并通过递归的方法,加载这些依赖资源的ab包,下次再写这个地方的实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。