AssetInspectorGUI.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using UnityEditor.AddressableAssets.Build;
  6. using UnityEditor.AddressableAssets.Settings;
  7. using UnityEngine;
  8. namespace UnityEditor.AddressableAssets.GUI
  9. {
  10. using Object = UnityEngine.Object;
  11. [InitializeOnLoad]
  12. internal static class AddressableAssetInspectorGUI
  13. {
  14. static GUIStyle s_ToggleMixed;
  15. static GUIContent s_AddressableAssetToggleText;
  16. static AddressableAssetInspectorGUI()
  17. {
  18. s_ToggleMixed = null;
  19. s_AddressableAssetToggleText = new GUIContent("Addressable", "Check this to mark this asset as an Addressable Asset, which includes it in the bundled data and makes it loadable via script by its address.");
  20. Editor.finishedDefaultHeaderGUI += OnPostHeaderGUI;
  21. }
  22. static void SetAaEntry(AddressableAssetSettings aaSettings, List<TargetInfo> targetInfos, bool create)
  23. {
  24. if (create && aaSettings.DefaultGroup.ReadOnly)
  25. {
  26. Debug.LogError("Current default group is ReadOnly. Cannot add addressable assets to it");
  27. return;
  28. }
  29. Undo.RecordObject(aaSettings, "AddressableAssetSettings");
  30. if (!create)
  31. {
  32. List<AddressableAssetEntry> removedEntries = new List<AddressableAssetEntry>(targetInfos.Count);
  33. for (int i = 0; i < targetInfos.Count; ++i)
  34. {
  35. AddressableAssetEntry e = aaSettings.FindAssetEntry(targetInfos[i].Guid);
  36. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(e.parentGroup);
  37. removedEntries.Add(e);
  38. aaSettings.RemoveAssetEntry(removedEntries[i], false);
  39. }
  40. if (removedEntries.Count > 0)
  41. aaSettings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryRemoved, removedEntries, true, false);
  42. }
  43. else
  44. {
  45. AddressableAssetGroup parentGroup = aaSettings.DefaultGroup;
  46. var resourceTargets = targetInfos.Where(ti => AddressableAssetUtility.IsInResources(ti.Path));
  47. if (resourceTargets.Any())
  48. {
  49. var resourcePaths = resourceTargets.Select(t => t.Path).ToList();
  50. var resourceGuids = resourceTargets.Select(t => t.Guid).ToList();
  51. AddressableAssetUtility.SafeMoveResourcesToGroup(aaSettings, parentGroup, resourcePaths, resourceGuids);
  52. }
  53. var otherTargetInfos = targetInfos.Except(resourceTargets);
  54. List<string> otherTargetGuids = new List<string>(targetInfos.Count);
  55. foreach (var info in otherTargetInfos)
  56. otherTargetGuids.Add(info.Guid);
  57. var entriesCreated = new List<AddressableAssetEntry>();
  58. var entriesMoved = new List<AddressableAssetEntry>();
  59. aaSettings.CreateOrMoveEntries(otherTargetGuids, parentGroup, entriesCreated, entriesMoved, false, false);
  60. bool openedInVC = false;
  61. if (entriesMoved.Count > 0)
  62. {
  63. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(parentGroup);
  64. openedInVC = true;
  65. aaSettings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, entriesMoved, true, false);
  66. }
  67. if (entriesCreated.Count > 0)
  68. {
  69. if (!openedInVC)
  70. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(parentGroup);
  71. aaSettings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryAdded, entriesCreated, true, false);
  72. }
  73. }
  74. }
  75. static void OnPostHeaderGUI(Editor editor)
  76. {
  77. var aaSettings = AddressableAssetSettingsDefaultObject.Settings;
  78. if (editor.targets.Length > 0)
  79. {
  80. foreach (var t in editor.targets)
  81. {
  82. if (t is AddressableAssetGroup || t is AddressableAssetGroupSchema)
  83. {
  84. GUILayout.BeginHorizontal();
  85. GUILayout.Label("Profile: " + AddressableAssetSettingsDefaultObject.GetSettings(true).profileSettings.
  86. GetProfileName(AddressableAssetSettingsDefaultObject.GetSettings(true).activeProfileId));
  87. GUILayout.FlexibleSpace();
  88. if (GUILayout.Button("System Settings", "MiniButton"))
  89. {
  90. EditorGUIUtility.PingObject(AddressableAssetSettingsDefaultObject.Settings);
  91. Selection.activeObject = AddressableAssetSettingsDefaultObject.Settings;
  92. }
  93. GUILayout.EndHorizontal();
  94. return;
  95. }
  96. }
  97. List<TargetInfo> targetInfos = GatherTargetInfos(editor.targets, aaSettings);
  98. if (targetInfos.Count == 0)
  99. return;
  100. bool targetHasAddressableSubObject = false;
  101. int mainAssetsAddressable = 0;
  102. int subAssetsAddressable = 0;
  103. foreach (TargetInfo info in targetInfos)
  104. {
  105. if (info.MainAssetEntry == null)
  106. continue;
  107. if (info.MainAssetEntry.IsSubAsset)
  108. subAssetsAddressable++;
  109. else
  110. mainAssetsAddressable++;
  111. if (!info.IsMainAsset)
  112. targetHasAddressableSubObject = true;
  113. }
  114. // Overrides a DisabledScope in the EditorElement.cs that disables GUI drawn in the header when the asset cannot be edited.
  115. bool prevEnabledState = UnityEngine.GUI.enabled;
  116. if (targetHasAddressableSubObject)
  117. UnityEngine.GUI.enabled = false;
  118. else
  119. {
  120. UnityEngine.GUI.enabled = true;
  121. foreach (var info in targetInfos)
  122. {
  123. if (!info.IsMainAsset)
  124. {
  125. UnityEngine.GUI.enabled = false;
  126. break;
  127. }
  128. }
  129. }
  130. int totalAddressableCount = mainAssetsAddressable + subAssetsAddressable;
  131. if (totalAddressableCount == 0) // nothing is addressable
  132. {
  133. if (GUILayout.Toggle(false, s_AddressableAssetToggleText, GUILayout.ExpandWidth(false)))
  134. SetAaEntry(AddressableAssetSettingsDefaultObject.GetSettings(true), targetInfos, true);
  135. }
  136. else if (totalAddressableCount == editor.targets.Length) // everything is addressable
  137. {
  138. var entryInfo = targetInfos[targetInfos.Count - 1];
  139. if (entryInfo == null || entryInfo.MainAssetEntry == null)
  140. throw new NullReferenceException("EntryInfo incorrect for Addressables content.");
  141. GUILayout.BeginHorizontal();
  142. if (mainAssetsAddressable > 0 && subAssetsAddressable > 0)
  143. {
  144. if (s_ToggleMixed == null)
  145. s_ToggleMixed = new GUIStyle("ToggleMixed");
  146. if (GUILayout.Toggle(false, s_AddressableAssetToggleText, s_ToggleMixed, GUILayout.ExpandWidth(false)))
  147. SetAaEntry(aaSettings, targetInfos, true);
  148. }
  149. else if (mainAssetsAddressable > 0)
  150. {
  151. if (!GUILayout.Toggle(true, s_AddressableAssetToggleText, GUILayout.ExpandWidth(false)))
  152. {
  153. SetAaEntry(aaSettings, targetInfos, false);
  154. UnityEngine.GUI.enabled = prevEnabledState;
  155. GUIUtility.ExitGUI();
  156. }
  157. }
  158. else if (GUILayout.Toggle(false, s_AddressableAssetToggleText, GUILayout.ExpandWidth(false)))
  159. SetAaEntry(aaSettings, targetInfos, true);
  160. if (editor.targets.Length == 1)
  161. {
  162. if (!entryInfo.IsMainAsset || entryInfo.MainAssetEntry.IsSubAsset)
  163. {
  164. bool preAddressPrevEnabledState = UnityEngine.GUI.enabled;
  165. UnityEngine.GUI.enabled = false;
  166. string address = entryInfo.Address + (entryInfo.IsMainAsset ? "" : $"[{entryInfo.TargetObject.name}]");
  167. EditorGUILayout.DelayedTextField(address, GUILayout.ExpandWidth(true));
  168. UnityEngine.GUI.enabled = preAddressPrevEnabledState;
  169. }
  170. else
  171. {
  172. string newAddress = EditorGUILayout.DelayedTextField(entryInfo.Address, GUILayout.ExpandWidth(true));
  173. if (newAddress != entryInfo.Address)
  174. {
  175. if (newAddress.Contains("[") && newAddress.Contains("]"))
  176. Debug.LogErrorFormat("Rename of address '{0}' cannot contain '[ ]'.", entryInfo.Address);
  177. else
  178. {
  179. entryInfo.MainAssetEntry.address = newAddress;
  180. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(entryInfo.MainAssetEntry.parentGroup, true);
  181. }
  182. }
  183. }
  184. }
  185. else
  186. {
  187. FindUniqueAssetGuids(targetInfos, out var uniqueAssetGuids, out var uniqueAddressableAssetGuids);
  188. EditorGUILayout.LabelField(uniqueAddressableAssetGuids.Count + " out of " + uniqueAssetGuids.Count + " assets are addressable.");
  189. }
  190. DrawSelectEntriesButton(targetInfos);
  191. GUILayout.EndHorizontal();
  192. }
  193. else // mixed addressable selected
  194. {
  195. GUILayout.BeginHorizontal();
  196. if (s_ToggleMixed == null)
  197. s_ToggleMixed = new GUIStyle("ToggleMixed");
  198. if (GUILayout.Toggle(false, s_AddressableAssetToggleText, s_ToggleMixed, GUILayout.ExpandWidth(false)))
  199. SetAaEntry(AddressableAssetSettingsDefaultObject.GetSettings(true), targetInfos, true);
  200. FindUniqueAssetGuids(targetInfos, out var uniqueAssetGuids, out var uniqueAddressableAssetGuids);
  201. EditorGUILayout.LabelField(uniqueAddressableAssetGuids.Count + " out of " + uniqueAssetGuids.Count + " assets are addressable.");
  202. DrawSelectEntriesButton(targetInfos);
  203. GUILayout.EndHorizontal();
  204. }
  205. UnityEngine.GUI.enabled = prevEnabledState;
  206. }
  207. }
  208. internal static List<TargetInfo> GatherTargetInfos(Object[] targets, AddressableAssetSettings aaSettings)
  209. {
  210. var targetInfos = new List<TargetInfo>();
  211. AddressableAssetEntry entry;
  212. foreach (var t in targets)
  213. {
  214. if (AddressableAssetUtility.TryGetPathAndGUIDFromTarget(t, out var path, out var guid))
  215. {
  216. var mainAssetType = AssetDatabase.GetMainAssetTypeAtPath(path);
  217. // Is asset
  218. if (mainAssetType != null && !BuildUtility.IsEditorAssembly(mainAssetType.Assembly))
  219. {
  220. bool isMainAsset = t is AssetImporter || AssetDatabase.IsMainAsset(t);
  221. var info = new TargetInfo() {TargetObject = t, Guid = guid, Path = path, IsMainAsset = isMainAsset};
  222. if (aaSettings != null)
  223. {
  224. entry = aaSettings.FindAssetEntry(guid, true);
  225. if (entry != null)
  226. info.MainAssetEntry = entry;
  227. }
  228. targetInfos.Add(info);
  229. }
  230. }
  231. }
  232. return targetInfos;
  233. }
  234. internal static void FindUniqueAssetGuids(List<TargetInfo> targetInfos, out HashSet<string> uniqueAssetGuids, out HashSet<string> uniqueAddressableAssetGuids)
  235. {
  236. uniqueAssetGuids = new HashSet<string>();
  237. uniqueAddressableAssetGuids = new HashSet<string>();
  238. foreach (TargetInfo info in targetInfos)
  239. {
  240. uniqueAssetGuids.Add(info.Guid);
  241. if (info.MainAssetEntry != null)
  242. uniqueAddressableAssetGuids.Add(info.Guid);
  243. }
  244. }
  245. static void DrawSelectEntriesButton(List<TargetInfo> targets)
  246. {
  247. var prevGuiEnabled = UnityEngine.GUI.enabled;
  248. UnityEngine.GUI.enabled = true;
  249. if (GUILayout.Button("Select"))
  250. {
  251. AddressableAssetsWindow.Init();
  252. var window = EditorWindow.GetWindow<AddressableAssetsWindow>();
  253. List<AddressableAssetEntry> entries = new List<AddressableAssetEntry>(targets.Count);
  254. foreach (TargetInfo info in targets)
  255. {
  256. if (info.MainAssetEntry != null)
  257. {
  258. if (info.IsMainAsset == false && ProjectConfigData.ShowSubObjectsInGroupView)
  259. {
  260. List<AddressableAssetEntry> subs = new List<AddressableAssetEntry>();
  261. info.MainAssetEntry.GatherAllAssets(subs, false, true, true);
  262. foreach (AddressableAssetEntry sub in subs)
  263. {
  264. if (sub.TargetAsset == info.TargetObject)
  265. {
  266. entries.Add(sub);
  267. break;
  268. }
  269. }
  270. }
  271. else
  272. entries.Add(info.MainAssetEntry);
  273. }
  274. }
  275. if (entries.Count > 0)
  276. window.SelectAssetsInGroupEditor(entries);
  277. }
  278. UnityEngine.GUI.enabled = prevGuiEnabled;
  279. }
  280. internal class TargetInfo
  281. {
  282. public UnityEngine.Object TargetObject;
  283. public string Guid;
  284. public string Path;
  285. public bool IsMainAsset;
  286. public AddressableAssetEntry MainAssetEntry;
  287. public string Address
  288. {
  289. get
  290. {
  291. if (MainAssetEntry == null)
  292. throw new NullReferenceException("No Entry set for Target info with AssetPath " + Path);
  293. return MainAssetEntry.address;
  294. }
  295. }
  296. }
  297. }
  298. }