AssetBundle打包好后,还需要真正地把游戏Build出来,Unity也提供了BuildPlayer的API,我们可以根据需要进行处理。比如将APK打在哪里,打包APK前需要设置哪些参数等等。这些也是公共打包机的主要工作。

文件夹准备

首先我们要在Unity工程文件夹下新建几个文件夹,注意Unity工程文件夹是Asset文件夹的父文件夹,没有meta文件。

新建BuildTarget文件夹,再在其内部新建Windows、IOS、Android三个文件夹。

新建AssetBundle文件夹。

修改BundleEditor脚本

我们将BundleEditor打AB包的路径m_BundleTargetPath给修改了,然后在BuildAssetBundle方法内添加一些代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    private static string ABCONFIGPATH = "Assets/ResFrame.Editor/Editor/Resource/ABConfig.asset";
private static string BYTEPATH = "Assets/GameData/Data/ABData/AssetBundleConfig.bytes";
private static string m_BundleTargetPath = Application.dataPath + "/../AssetBundle/" + EditorUserBuildSettings.activeBuildTarget.ToString();
//...
static void BuildAssetBundle()
{
//...
if(!Directory.Exists(m_BundleTargetPath))
{
Directory.CreateDirectory(m_BundleTargetPath);
}

DeleteAB();
WriteData(resPathDic);

AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles
(
m_BundleTargetPath,
BuildAssetBundleOptions.ChunkBasedCompression,
EditorUserBuildSettings.activeBuildTarget
);
if (manifest == null)
{
Debug.LogError("AssetBundle 打包失败");
}else
{
Debug.Log("打包完毕");
}
DeleteManifest();
}
static void DeleteManifest()
{
DirectoryInfo directoryInfo = new DirectoryInfo(m_BundleTargetPath);
FileInfo[] fileInfos = directoryInfo.GetFiles("*",SearchOption.AllDirectories);
foreach (FileInfo fileInfo in fileInfos)
{
if (fileInfo.Name.EndsWith(".manifest"))
{
File.Delete(fileInfo.FullName);
}
}
}

添加BuildApp脚本

我们在Editor文件夹下新建BuildApp脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.IO;

public class BuildApp
{
public static string m_AndroidPath = Application.dataPath + "/../BuildTarget/Android/";
public static string m_IOSPath = Application.dataPath + "/../BuildTarget/IOS/";
public static string m_WindowsPath = Application.dataPath + "/../BuildTarget/Windows/";
[MenuItem("Build/标准包")]
public static void Build()
{
//打AB包
BundleEditor.Build();
//生成可执行程序
string abPath = Application.dataPath + "/../AssetBundle/" + EditorUserBuildSettings.activeBuildTarget.ToString() + "/";
Copy(abPath, Application.streamingAssetsPath);
string savePath = "";
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
{
savePath = m_AndroidPath + PlayerSettings.productName + "_" + EditorUserBuildSettings.activeBuildTarget.ToString()+ "_" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm") + ".apk";
}
else if(EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS)
{
//IOS打包是一个XCode工程
savePath = m_IOSPath + PlayerSettings.productName + "_" + EditorUserBuildSettings.activeBuildTarget.ToString()+ "_" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm");
}
else if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64 ||
EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows)
{
//Windows的Build会先创建一个文件夹
savePath = m_WindowsPath + PlayerSettings.productName + "_" + EditorUserBuildSettings.activeBuildTarget.ToString() + string.Format("_{0:yyyy_MM_dd_HH_mm}/{1}.exe", DateTime.Now,PlayerSettings.productName);
}
BuildPipeline.BuildPlayer(FindEnabledEditorScenes(),savePath,EditorUserBuildSettings.activeBuildTarget,BuildOptions.None);

DeleteDir(Application.streamingAssetsPath);
}

private static string[] FindEnabledEditorScenes()
{
List<string> editorScenes = new List<string>();
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
{
if(!scene.enabled) continue;
editorScenes.Add(scene.path);
}
return editorScenes.ToArray();
}
private static void Copy(string srcPath, string targetPath)
{
try
{
if(!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
string srcdir = Path.Combine(targetPath,Path.GetFileName(srcPath));
//srcPath是否是个文件夹??
if (Directory.Exists(srcPath))
{
//如果是文件夹,就给srcdir加上文件夹分隔符
srcdir += Path.DirectorySeparatorChar;
}
//把srcdir创建出来
if (!Directory.Exists(srcdir))
{
Directory.CreateDirectory (srcdir);
}
//返回满足指定条件的所有文件和子目录的名称
string[] filePaths = Directory.GetFileSystemEntries(srcPath);
foreach (var filePath in filePaths)
{
//如果当前的filePath仅是个文件夹
if (Directory.Exists(filePath))
{
//用递归方法,在目标位置创建文件夹,直至没有文件夹可以创建
Copy(filePath, srcdir);
}
else//如果不是文件夹
{
File.Copy(filePath,srcdir + Path.GetFileName(filePath),true);
}
}
}
catch
{
Debug.LogError("无法复制:" + srcPath + " 到" + targetPath);
}
}

private static void DeleteDir(string path)
{
try
{
DirectoryInfo dirInfo = new DirectoryInfo(path);
FileSystemInfo[] fileSysInfos = dirInfo.GetFileSystemInfos();
foreach (FileSystemInfo fileSystemInfo in fileSysInfos)
{
if (fileSystemInfo is DirectoryInfo)
{
DirectoryInfo subdir = new DirectoryInfo(fileSystemInfo.FullName);
subdir.Delete(true);//参数表示开启递归删除,
}
else
{
File.Delete(fileSystemInfo.FullName);
}
}
}
catch (Exception e)
{

Debug.LogException(e);
}
}
}

Build流程梳理

  1. 首先在工程文件的AssetBundle文件夹内生成AB包,生成在这里是为了不让AssetBundle参与版本管理,在开发阶段让AssetBundle参与版本管理没有必要。
  2. 然后把生成的AB包复制到Asset/StreamingAssets文件夹下面(也即是BuildApp.Copy方法的用处),因为不复制进来的话Build的时候需要的资源就没有了。
  3. 根据当前的平台进行Build,注意我们每次Build的成果都会根据日期归档。
  4. 删除Asset/StreamingAssets文件夹下的AB包,还是为了不让AssetBundle参与版本管理。

修改AssetBundleManager

由于我们修改了AssetBundle的构建流程,我们的AssetBundleManager一些API就必须要修改

1
2
3
4
5
6
7
8
9
10
    public bool LoadAssetBundleConfig()
{
#if UNITY_EDITOR
if(!ResourceManager.Instance.m_LoadFromAssetBundle)//+++
return false;
#endif
m_ResourceItemDic.Clear();
//...
}