AssetBundleBuildTab.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. using UnityEditor;
  2. using UnityEngine;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Runtime.Serialization.Formatters.Binary;
  6. using AssetBundleBrowser.AssetBundleDataSource;
  7. namespace AssetBundleBrowser
  8. {
  9. [System.Serializable]
  10. internal class AssetBundleBuildTab
  11. {
  12. const string k_BuildPrefPrefix = "ABBBuild:";
  13. private string m_streamingPath = "Assets/StreamingAssets";
  14. [SerializeField]
  15. private bool m_AdvancedSettings;
  16. [SerializeField]
  17. private Vector2 m_ScrollPosition;
  18. class ToggleData
  19. {
  20. internal ToggleData(bool s,
  21. string title,
  22. string tooltip,
  23. List<string> onToggles,
  24. BuildAssetBundleOptions opt = BuildAssetBundleOptions.None)
  25. {
  26. if (onToggles.Contains(title))
  27. state = true;
  28. else
  29. state = s;
  30. content = new GUIContent(title, tooltip);
  31. option = opt;
  32. }
  33. //internal string prefsKey
  34. //{ get { return k_BuildPrefPrefix + content.text; } }
  35. internal bool state;
  36. internal GUIContent content;
  37. internal BuildAssetBundleOptions option;
  38. }
  39. private AssetBundleInspectTab m_InspectTab;
  40. [SerializeField]
  41. private BuildTabData m_UserData;
  42. List<ToggleData> m_ToggleData;
  43. ToggleData m_ForceRebuild;
  44. ToggleData m_CopyToStreaming;
  45. GUIContent m_TargetContent;
  46. GUIContent m_CompressionContent;
  47. internal enum CompressOptions
  48. {
  49. Uncompressed = 0,
  50. StandardCompression,
  51. ChunkBasedCompression,
  52. }
  53. GUIContent[] m_CompressionOptions =
  54. {
  55. new GUIContent("No Compression"),
  56. new GUIContent("Standard Compression (LZMA)"),
  57. new GUIContent("Chunk Based Compression (LZ4)")
  58. };
  59. int[] m_CompressionValues = { 0, 1, 2 };
  60. internal AssetBundleBuildTab()
  61. {
  62. m_AdvancedSettings = false;
  63. m_UserData = new BuildTabData();
  64. m_UserData.m_OnToggles = new List<string>();
  65. m_UserData.m_UseDefaultPath = true;
  66. }
  67. internal void OnDisable()
  68. {
  69. var dataPath = System.IO.Path.GetFullPath(".");
  70. dataPath = dataPath.Replace("\\", "/");
  71. dataPath += "/Library/AssetBundleBrowserBuild.dat";
  72. BinaryFormatter bf = new BinaryFormatter();
  73. FileStream file = File.Create(dataPath);
  74. bf.Serialize(file, m_UserData);
  75. file.Close();
  76. }
  77. internal void OnEnable(EditorWindow parent)
  78. {
  79. m_InspectTab = (parent as AssetBundleBrowserMain).m_InspectTab;
  80. //LoadData...
  81. var dataPath = System.IO.Path.GetFullPath(".");
  82. dataPath = dataPath.Replace("\\", "/");
  83. dataPath += "/Library/AssetBundleBrowserBuild.dat";
  84. if (File.Exists(dataPath))
  85. {
  86. BinaryFormatter bf = new BinaryFormatter();
  87. FileStream file = File.Open(dataPath, FileMode.Open);
  88. var data = bf.Deserialize(file) as BuildTabData;
  89. if (data != null)
  90. m_UserData = data;
  91. file.Close();
  92. }
  93. m_ToggleData = new List<ToggleData>();
  94. m_ToggleData.Add(new ToggleData(
  95. false,
  96. "Exclude Type Information",
  97. "Do not include type information within the asset bundle (don't write type tree).",
  98. m_UserData.m_OnToggles,
  99. BuildAssetBundleOptions.DisableWriteTypeTree));
  100. m_ToggleData.Add(new ToggleData(
  101. false,
  102. "Force Rebuild",
  103. "Force rebuild the asset bundles",
  104. m_UserData.m_OnToggles,
  105. BuildAssetBundleOptions.ForceRebuildAssetBundle));
  106. m_ToggleData.Add(new ToggleData(
  107. false,
  108. "Ignore Type Tree Changes",
  109. "Ignore the type tree changes when doing the incremental build check.",
  110. m_UserData.m_OnToggles,
  111. BuildAssetBundleOptions.IgnoreTypeTreeChanges));
  112. m_ToggleData.Add(new ToggleData(
  113. false,
  114. "Append Hash",
  115. "Append the hash to the assetBundle name.",
  116. m_UserData.m_OnToggles,
  117. BuildAssetBundleOptions.AppendHashToAssetBundleName));
  118. m_ToggleData.Add(new ToggleData(
  119. false,
  120. "Strict Mode",
  121. "Do not allow the build to succeed if any errors are reporting during it.",
  122. m_UserData.m_OnToggles,
  123. BuildAssetBundleOptions.StrictMode));
  124. m_ToggleData.Add(new ToggleData(
  125. false,
  126. "Dry Run Build",
  127. "Do a dry run build.",
  128. m_UserData.m_OnToggles,
  129. BuildAssetBundleOptions.DryRunBuild));
  130. m_ForceRebuild = new ToggleData(
  131. false,
  132. "Clear Folders",
  133. "Will wipe out all contents of build directory as well as StreamingAssets/AssetBundles if you are choosing to copy build there.",
  134. m_UserData.m_OnToggles);
  135. m_CopyToStreaming = new ToggleData(
  136. false,
  137. "Copy to StreamingAssets",
  138. "After build completes, will copy all build content to " + m_streamingPath + " for use in stand-alone player.",
  139. m_UserData.m_OnToggles);
  140. m_TargetContent = new GUIContent("Build Target", "Choose target platform to build for.");
  141. m_CompressionContent = new GUIContent("Compression", "Choose no compress, standard (LZMA), or chunk based (LZ4)");
  142. if(m_UserData.m_UseDefaultPath)
  143. {
  144. ResetPathToDefault();
  145. }
  146. }
  147. internal void OnGUI()
  148. {
  149. m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition);
  150. bool newState = false;
  151. var centeredStyle = new GUIStyle(GUI.skin.GetStyle("Label"));
  152. centeredStyle.alignment = TextAnchor.UpperCenter;
  153. GUILayout.Label(new GUIContent("Example build setup"), centeredStyle);
  154. //basic options
  155. EditorGUILayout.Space();
  156. GUILayout.BeginVertical();
  157. // build target
  158. using (new EditorGUI.DisabledScope (!AssetBundleModel.Model.DataSource.CanSpecifyBuildTarget)) {
  159. ValidBuildTarget tgt = (ValidBuildTarget)EditorGUILayout.EnumPopup(m_TargetContent, m_UserData.m_BuildTarget);
  160. if (tgt != m_UserData.m_BuildTarget)
  161. {
  162. m_UserData.m_BuildTarget = tgt;
  163. if(m_UserData.m_UseDefaultPath)
  164. {
  165. m_UserData.m_OutputPath = "AssetBundles/";
  166. m_UserData.m_OutputPath += m_UserData.m_BuildTarget.ToString();
  167. //EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
  168. }
  169. }
  170. }
  171. ////output path
  172. using (new EditorGUI.DisabledScope (!AssetBundleModel.Model.DataSource.CanSpecifyBuildOutputDirectory)) {
  173. EditorGUILayout.Space();
  174. GUILayout.BeginHorizontal();
  175. var newPath = EditorGUILayout.TextField("Output Path", m_UserData.m_OutputPath);
  176. if (!System.String.IsNullOrEmpty(newPath) && newPath != m_UserData.m_OutputPath)
  177. {
  178. m_UserData.m_UseDefaultPath = false;
  179. m_UserData.m_OutputPath = newPath;
  180. //EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
  181. }
  182. GUILayout.EndHorizontal();
  183. GUILayout.BeginHorizontal();
  184. GUILayout.FlexibleSpace();
  185. if (GUILayout.Button("Browse", GUILayout.MaxWidth(75f)))
  186. BrowseForFolder();
  187. if (GUILayout.Button("Reset", GUILayout.MaxWidth(75f)))
  188. ResetPathToDefault();
  189. //if (string.IsNullOrEmpty(m_OutputPath))
  190. // m_OutputPath = EditorUserBuildSettings.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath");
  191. GUILayout.EndHorizontal();
  192. EditorGUILayout.Space();
  193. newState = GUILayout.Toggle(
  194. m_ForceRebuild.state,
  195. m_ForceRebuild.content);
  196. if (newState != m_ForceRebuild.state)
  197. {
  198. if (newState)
  199. m_UserData.m_OnToggles.Add(m_ForceRebuild.content.text);
  200. else
  201. m_UserData.m_OnToggles.Remove(m_ForceRebuild.content.text);
  202. m_ForceRebuild.state = newState;
  203. }
  204. newState = GUILayout.Toggle(
  205. m_CopyToStreaming.state,
  206. m_CopyToStreaming.content);
  207. if (newState != m_CopyToStreaming.state)
  208. {
  209. if (newState)
  210. m_UserData.m_OnToggles.Add(m_CopyToStreaming.content.text);
  211. else
  212. m_UserData.m_OnToggles.Remove(m_CopyToStreaming.content.text);
  213. m_CopyToStreaming.state = newState;
  214. }
  215. }
  216. // advanced options
  217. using (new EditorGUI.DisabledScope (!AssetBundleModel.Model.DataSource.CanSpecifyBuildOptions)) {
  218. EditorGUILayout.Space();
  219. m_AdvancedSettings = EditorGUILayout.Foldout(m_AdvancedSettings, "Advanced Settings");
  220. if(m_AdvancedSettings)
  221. {
  222. var indent = EditorGUI.indentLevel;
  223. EditorGUI.indentLevel = 1;
  224. CompressOptions cmp = (CompressOptions)EditorGUILayout.IntPopup(
  225. m_CompressionContent,
  226. (int)m_UserData.m_Compression,
  227. m_CompressionOptions,
  228. m_CompressionValues);
  229. if (cmp != m_UserData.m_Compression)
  230. {
  231. m_UserData.m_Compression = cmp;
  232. }
  233. foreach (var tog in m_ToggleData)
  234. {
  235. newState = EditorGUILayout.ToggleLeft(
  236. tog.content,
  237. tog.state);
  238. if (newState != tog.state)
  239. {
  240. if (newState)
  241. m_UserData.m_OnToggles.Add(tog.content.text);
  242. else
  243. m_UserData.m_OnToggles.Remove(tog.content.text);
  244. tog.state = newState;
  245. }
  246. }
  247. EditorGUILayout.Space();
  248. EditorGUI.indentLevel = indent;
  249. }
  250. }
  251. // build.
  252. EditorGUILayout.Space();
  253. if (GUILayout.Button("Build") )
  254. {
  255. EditorApplication.delayCall += ExecuteBuild;
  256. }
  257. GUILayout.EndVertical();
  258. EditorGUILayout.EndScrollView();
  259. }
  260. private void ExecuteBuild()
  261. {
  262. if (AssetBundleModel.Model.DataSource.CanSpecifyBuildOutputDirectory) {
  263. if (string.IsNullOrEmpty(m_UserData.m_OutputPath))
  264. BrowseForFolder();
  265. if (string.IsNullOrEmpty(m_UserData.m_OutputPath)) //in case they hit "cancel" on the open browser
  266. {
  267. Debug.LogError("AssetBundle Build: No valid output path for build.");
  268. return;
  269. }
  270. if (m_ForceRebuild.state)
  271. {
  272. string message = "Do you want to delete all files in the directory " + m_UserData.m_OutputPath;
  273. if (m_CopyToStreaming.state)
  274. message += " and " + m_streamingPath;
  275. message += "?";
  276. if (EditorUtility.DisplayDialog("File delete confirmation", message, "Yes", "No"))
  277. {
  278. try
  279. {
  280. if (Directory.Exists(m_UserData.m_OutputPath))
  281. Directory.Delete(m_UserData.m_OutputPath, true);
  282. if (m_CopyToStreaming.state)
  283. if (Directory.Exists(m_streamingPath))
  284. Directory.Delete(m_streamingPath, true);
  285. }
  286. catch (System.Exception e)
  287. {
  288. Debug.LogException(e);
  289. }
  290. }
  291. }
  292. if (!Directory.Exists(m_UserData.m_OutputPath))
  293. Directory.CreateDirectory(m_UserData.m_OutputPath);
  294. }
  295. BuildAssetBundleOptions opt = BuildAssetBundleOptions.None;
  296. if (AssetBundleModel.Model.DataSource.CanSpecifyBuildOptions) {
  297. if (m_UserData.m_Compression == CompressOptions.Uncompressed)
  298. opt |= BuildAssetBundleOptions.UncompressedAssetBundle;
  299. else if (m_UserData.m_Compression == CompressOptions.ChunkBasedCompression)
  300. opt |= BuildAssetBundleOptions.ChunkBasedCompression;
  301. foreach (var tog in m_ToggleData)
  302. {
  303. if (tog.state)
  304. opt |= tog.option;
  305. }
  306. }
  307. ABBuildInfo buildInfo = new ABBuildInfo();
  308. buildInfo.outputDirectory = m_UserData.m_OutputPath;
  309. buildInfo.options = opt;
  310. buildInfo.buildTarget = (BuildTarget)m_UserData.m_BuildTarget;
  311. buildInfo.onBuild = (assetBundleName) =>
  312. {
  313. if (m_InspectTab == null)
  314. return;
  315. m_InspectTab.AddBundleFolder(buildInfo.outputDirectory);
  316. m_InspectTab.RefreshBundles();
  317. };
  318. AssetBundleModel.Model.DataSource.BuildAssetBundles (buildInfo);
  319. AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
  320. if(m_CopyToStreaming.state)
  321. DirectoryCopy(m_UserData.m_OutputPath, m_streamingPath);
  322. }
  323. private static void DirectoryCopy(string sourceDirName, string destDirName)
  324. {
  325. // If the destination directory doesn't exist, create it.
  326. if (!Directory.Exists(destDirName))
  327. {
  328. Directory.CreateDirectory(destDirName);
  329. }
  330. foreach (string folderPath in Directory.GetDirectories(sourceDirName, "*", SearchOption.AllDirectories))
  331. {
  332. if (!Directory.Exists(folderPath.Replace(sourceDirName, destDirName)))
  333. Directory.CreateDirectory(folderPath.Replace(sourceDirName, destDirName));
  334. }
  335. foreach (string filePath in Directory.GetFiles(sourceDirName, "*.*", SearchOption.AllDirectories))
  336. {
  337. var fileDirName = Path.GetDirectoryName(filePath).Replace("\\", "/");
  338. var fileName = Path.GetFileName(filePath);
  339. string newFilePath = Path.Combine(fileDirName.Replace(sourceDirName, destDirName), fileName);
  340. File.Copy(filePath, newFilePath, true);
  341. }
  342. }
  343. private void BrowseForFolder()
  344. {
  345. m_UserData.m_UseDefaultPath = false;
  346. var newPath = EditorUtility.OpenFolderPanel("Bundle Folder", m_UserData.m_OutputPath, string.Empty);
  347. if (!string.IsNullOrEmpty(newPath))
  348. {
  349. var gamePath = System.IO.Path.GetFullPath(".");
  350. gamePath = gamePath.Replace("\\", "/");
  351. if (newPath.StartsWith(gamePath) && newPath.Length > gamePath.Length)
  352. newPath = newPath.Remove(0, gamePath.Length+1);
  353. m_UserData.m_OutputPath = newPath;
  354. //EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
  355. }
  356. }
  357. private void ResetPathToDefault()
  358. {
  359. m_UserData.m_UseDefaultPath = true;
  360. m_UserData.m_OutputPath = "AssetBundles/";
  361. m_UserData.m_OutputPath += m_UserData.m_BuildTarget.ToString();
  362. //EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
  363. }
  364. //Note: this is the provided BuildTarget enum with some entries removed as they are invalid in the dropdown
  365. internal enum ValidBuildTarget
  366. {
  367. //NoTarget = -2, --doesn't make sense
  368. //iPhone = -1, --deprecated
  369. //BB10 = -1, --deprecated
  370. //MetroPlayer = -1, --deprecated
  371. StandaloneOSXUniversal = 2,
  372. StandaloneOSXIntel = 4,
  373. StandaloneWindows = 5,
  374. WebPlayer = 6,
  375. WebPlayerStreamed = 7,
  376. iOS = 9,
  377. PS3 = 10,
  378. XBOX360 = 11,
  379. Android = 13,
  380. StandaloneLinux = 17,
  381. StandaloneWindows64 = 19,
  382. WebGL = 20,
  383. WSAPlayer = 21,
  384. StandaloneLinux64 = 24,
  385. StandaloneLinuxUniversal = 25,
  386. WP8Player = 26,
  387. StandaloneOSXIntel64 = 27,
  388. BlackBerry = 28,
  389. Tizen = 29,
  390. PSP2 = 30,
  391. PS4 = 31,
  392. PSM = 32,
  393. XboxOne = 33,
  394. SamsungTV = 34,
  395. N3DS = 35,
  396. WiiU = 36,
  397. tvOS = 37,
  398. Switch = 38
  399. }
  400. [System.Serializable]
  401. internal class BuildTabData
  402. {
  403. internal List<string> m_OnToggles;
  404. internal ValidBuildTarget m_BuildTarget = ValidBuildTarget.StandaloneWindows;
  405. internal CompressOptions m_Compression = CompressOptions.StandardCompression;
  406. internal string m_OutputPath = string.Empty;
  407. internal bool m_UseDefaultPath = true;
  408. }
  409. }
  410. }