Storyline+Dialog初步
This commit is contained in:
44
Packages/io.continis.subassets/Editor/AssetRelocator.cs
Normal file
44
Packages/io.continis.subassets/Editor/AssetRelocator.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// When the user creates a new asset via the right-click menu and the selected asset is a main asset,
|
||||
/// this class adds it to the target asset as a sub-asset.
|
||||
/// </summary>
|
||||
public class AssetRelocator : AssetModificationProcessor
|
||||
{
|
||||
public static Object LastSelectionOnRightClick;
|
||||
|
||||
static void OnWillCreateAsset(string path)
|
||||
{
|
||||
// Used to check if the creation happened as a right-click on an asset
|
||||
if (LastSelectionOnRightClick == null) return;
|
||||
|
||||
string newParentPath = AssetDatabase.GetAssetPath(LastSelectionOnRightClick);
|
||||
|
||||
if (TypeChecker.HasDefaultAction(path, newParentPath)) return; // There's already a default action
|
||||
if (!TypeChecker.ValidateSubAssetType(path, false)) return; // Can't be a sub-asset
|
||||
if (!TypeChecker.ValidateDestinationType(newParentPath)) return; // Can't contain a sub-asset
|
||||
|
||||
EditorApplication.delayCall += () => RelocateAsset(path);
|
||||
}
|
||||
|
||||
private static void RelocateAsset(string path)
|
||||
{
|
||||
Type t = AssetDatabase.GetMainAssetTypeAtPath(path);
|
||||
Object justCreatedAsset = AssetDatabase.LoadAssetAtPath(path, t);
|
||||
string parentObjectPath = AssetDatabase.GetAssetPath(LastSelectionOnRightClick);
|
||||
|
||||
SubAssetsToolbox.AddSubAsset(parentObjectPath, justCreatedAsset, out _);
|
||||
|
||||
AssetDatabase.DeleteAsset(path);
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Packages/io.continis.subassets/Editor/AssetRelocator.cs.meta
Normal file
18
Packages/io.continis.subassets/Editor/AssetRelocator.cs.meta
Normal file
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e535f19b37134abe8c379ec1e5a87d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/AssetRelocator.cs
|
||||
uploadId: 869944
|
||||
90
Packages/io.continis.subassets/Editor/ContextualActions.cs
Normal file
90
Packages/io.continis.subassets/Editor/ContextualActions.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public static class ContextualActions
|
||||
{
|
||||
static ContextualActions()
|
||||
{
|
||||
EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemGUI;
|
||||
}
|
||||
|
||||
private static void OnProjectWindowItemGUI(string guid, Rect selectionRect)
|
||||
{
|
||||
Event current = Event.current;
|
||||
Object obj;
|
||||
switch (current.type)
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
ClearLastSelection();
|
||||
break;
|
||||
|
||||
case EventType.KeyUp when current.keyCode == KeyCode.Delete ||
|
||||
current.keyCode == KeyCode.Backspace && current.command:
|
||||
|
||||
obj = Selection.activeObject;
|
||||
if (obj == null) return;
|
||||
|
||||
if(AssetDatabase.IsSubAsset(obj))
|
||||
{
|
||||
// It's a sub-asset
|
||||
bool decision = EditorUtility.DisplayDialog("Delete Sub-Asset",
|
||||
string.Format(Constants.ConfirmDeleteSubAsset, obj.name), "Delete",
|
||||
"Cancel");
|
||||
|
||||
if (decision)
|
||||
{
|
||||
string parentObjectPath = AssetDatabase.GetAssetPath(obj);
|
||||
SubAssetsToolbox.RemoveSubAsset(parentObjectPath, obj);
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
ClearLastSelection();
|
||||
current.Use();
|
||||
break;
|
||||
|
||||
case EventType.KeyUp when
|
||||
#if UNITY_EDITOR_WIN
|
||||
current.keyCode == KeyCode.F2:
|
||||
#else
|
||||
current.keyCode == KeyCode.Return:
|
||||
#endif
|
||||
obj = Selection.activeObject;
|
||||
if (obj == null || Selection.objects.Length > 1) return;
|
||||
|
||||
if (AssetDatabase.IsSubAsset(obj))
|
||||
{
|
||||
RenameSubAssetWindow.Open(obj);
|
||||
current.Use();
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.ContextClick when selectionRect.Contains(current.mousePosition):
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (AssetDatabase.IsValidFolder(path))
|
||||
{
|
||||
ClearLastSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
obj = AssetDatabase.LoadAssetAtPath<Object>(path);
|
||||
if (obj != null)
|
||||
{
|
||||
AssetRelocator.LastSelectionOnRightClick = obj;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClearLastSelection()
|
||||
{
|
||||
AssetRelocator.LastSelectionOnRightClick = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f285d3fcf48e4fda8094f38477ff8f0b
|
||||
timeCreated: 1719562257
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/ContextualActions.cs
|
||||
uploadId: 869944
|
||||
241
Packages/io.continis.subassets/Editor/DragAndDropAddon.cs
Normal file
241
Packages/io.continis.subassets/Editor/DragAndDropAddon.cs
Normal file
@@ -0,0 +1,241 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SubAssetsToolbox.Utilities;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
internal static class DragAndDropAddon
|
||||
{
|
||||
#if UNITY_6000_3_OR_NEWER
|
||||
[InitializeOnLoadMethod]
|
||||
public static void AddDragProjectHandler()
|
||||
{
|
||||
DragAndDrop.AddDropHandlerV2(ProjectWindowHandler);
|
||||
}
|
||||
|
||||
private static DragAndDropVisualMode ProjectWindowHandler(EntityId id, string destinationPath, bool released)
|
||||
{
|
||||
#else
|
||||
[InitializeOnLoadMethod]
|
||||
public static void AddDragProjectHandler() => DragAndDrop.AddDropHandler(ProjectWindowHandler);
|
||||
|
||||
private static DragAndDropVisualMode ProjectWindowHandler(int id, string destinationPath, bool released)
|
||||
{
|
||||
#endif
|
||||
if (DragAndDrop.paths.Length == 0 || DragAndDrop.objectReferences.Length == 0)
|
||||
// Was not dragging from another window
|
||||
return DragAndDropVisualMode.None;
|
||||
|
||||
bool destinationIsFolder = AssetDatabase.IsValidFolder(destinationPath);
|
||||
|
||||
if (released)
|
||||
{
|
||||
bool isDraggingSubAssets = DragAndDrop.objectReferences.Any(AssetDatabase.IsSubAsset);
|
||||
|
||||
foreach (string path in DragAndDrop.paths)
|
||||
{
|
||||
if (string.Equals(path, destinationPath)) return DragAndDropVisualMode.None;
|
||||
if (destinationIsFolder) continue;
|
||||
if (isDraggingSubAssets) continue;
|
||||
|
||||
if (!TypeChecker.ValidateSubAssetType(path)) return DragAndDropVisualMode.Rejected;
|
||||
if (!TypeChecker.ValidateDestinationType(destinationPath)) return DragAndDropVisualMode.Rejected;
|
||||
}
|
||||
|
||||
if (!destinationIsFolder)
|
||||
{
|
||||
// Adding sub-asset(s)
|
||||
// (drag destination is an asset)
|
||||
|
||||
string objectName = Path.GetFileNameWithoutExtension(destinationPath);
|
||||
|
||||
bool isOne = DragAndDrop.objectReferences.Length == 1;
|
||||
|
||||
int choice = EditorUtility.DisplayDialogComplex(
|
||||
isOne ? "Add as Sub-Asset" : "Add as Sub-Assets",
|
||||
isOne
|
||||
? string.Format(Constants.ConfirmAddAsSubAsset, DragAndDrop.objectReferences[0].name,
|
||||
objectName)
|
||||
: string.Format(Constants.ConfirmAddAsSubAssets, DragAndDrop.objectReferences[0].name,
|
||||
DragAndDrop.objectReferences[1].name, objectName),
|
||||
isOne ? "Add" : "Add all",
|
||||
"Cancel",
|
||||
isOne ? "Add and Keep Original" : "Add and Keep Originals");
|
||||
|
||||
int redirectRefChoice = -1;
|
||||
|
||||
if (choice == 0)
|
||||
{
|
||||
ReferencePatchingMode mode = SubAssetsToolboxSettings.PatchReferences.value;
|
||||
if (mode == ReferencePatchingMode.AutoApply)
|
||||
redirectRefChoice = 0;
|
||||
else if (mode == ReferencePatchingMode.AskEachTime)
|
||||
redirectRefChoice = EditorUtility.DisplayDialogComplex("Patch references",
|
||||
Constants.PatchRefsToSubAssetPrompt,
|
||||
"Patch References", "Don't Patch", "Cancel Moving Assets");
|
||||
}
|
||||
|
||||
// Cancelled the operation
|
||||
if (choice == 1 || redirectRefChoice == 2) return DragAndDropVisualMode.Rejected;
|
||||
|
||||
foreach (Object draggedObject in DragAndDrop.objectReferences)
|
||||
{
|
||||
string draggedObjectPath = AssetDatabase.GetAssetPath(draggedObject);
|
||||
string draggedObjectName = draggedObject.name;
|
||||
bool wasMainAsset = AssetDatabase.IsMainAsset(draggedObject);
|
||||
|
||||
// TODO: preserve selection if the object selected was the one being destroyed
|
||||
SubAssetsToolbox.AddSubAsset(destinationPath, draggedObject, out Object newSubasset);
|
||||
|
||||
if (choice == 2) continue; // Keep previous objects
|
||||
|
||||
if (redirectRefChoice == 0)
|
||||
{
|
||||
AssetDatabase.SaveAssets();
|
||||
if (!AssetReferenceRedirector.RedirectToSubAsset(draggedObject, newSubasset))
|
||||
{
|
||||
Debug.LogWarning(string.Format(Constants.RedirectionFailed, draggedObjectName));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not keep previous
|
||||
if (wasMainAsset)
|
||||
{
|
||||
#if ADDRESSABLES_INSTALLED
|
||||
AddressableEntryMigrator.MigrateOrRemoveEntry(
|
||||
AssetDatabase.AssetPathToGUID(draggedObjectPath),
|
||||
AssetDatabase.AssetPathToGUID(destinationPath),
|
||||
objectName);
|
||||
#endif
|
||||
AssetDatabase.DeleteAsset(draggedObjectPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// After redirect, Refresh() may have invalidated the original reference.
|
||||
// Reload the sub-asset from disk if needed.
|
||||
Object objectToRemove = draggedObject;
|
||||
if (objectToRemove == null)
|
||||
{
|
||||
Object[] subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(draggedObjectPath);
|
||||
objectToRemove = subAssets.FirstOrDefault(a => a.name == draggedObjectName);
|
||||
}
|
||||
|
||||
if (objectToRemove != null)
|
||||
SubAssetsToolbox.RemoveSubAsset(draggedObjectPath, objectToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
return DragAndDropVisualMode.Link;
|
||||
}
|
||||
|
||||
// Removing sub-asset(s)
|
||||
// (drag destination is a folder or an empty space)
|
||||
|
||||
if (!DragAndDrop.objectReferences.Any(AssetDatabase.IsSubAsset))
|
||||
return Path.GetDirectoryName(DragAndDrop.paths[0]) == destinationPath
|
||||
? DragAndDropVisualMode.Move
|
||||
: DragAndDropVisualMode.None;
|
||||
|
||||
ReferencePatchingMode redirectMode = SubAssetsToolboxSettings.PatchReferences.value;
|
||||
int redirectRefsChoice;
|
||||
if (redirectMode == ReferencePatchingMode.AutoApply)
|
||||
redirectRefsChoice = 0;
|
||||
else if (redirectMode == ReferencePatchingMode.AskEachTime)
|
||||
redirectRefsChoice = EditorUtility.DisplayDialogComplex("Patch references",
|
||||
Constants.PatchRefsToStandalonePrompt,
|
||||
"Patch References", "Don't Patch", "Cancel Sub-Asset Removal");
|
||||
else
|
||||
redirectRefsChoice = 1;
|
||||
|
||||
if (redirectRefsChoice == 2) return DragAndDropVisualMode.Rejected;
|
||||
|
||||
int rejected = 0;
|
||||
|
||||
foreach (Object draggedObject in DragAndDrop.objectReferences)
|
||||
{
|
||||
string draggedObjectPath = AssetDatabase.GetAssetPath(draggedObject);
|
||||
|
||||
if (AssetDatabase.IsSubAsset(draggedObject))
|
||||
{
|
||||
// Was dragging a sub-asset: un-parent
|
||||
|
||||
// Check if its parent is in selection too
|
||||
Object mainAssetAtPath = AssetDatabase.LoadMainAssetAtPath(draggedObjectPath);
|
||||
string fileExtension = ExtensionMap.GetFileExtensionByType(draggedObject.GetType());
|
||||
if (DragAndDrop.objectReferences.Contains(mainAssetAtPath)) continue;
|
||||
|
||||
// Verify if there's an object with the same name
|
||||
string desiredName = Path.Combine(destinationPath, $"{draggedObject.name}.{fileExtension}");
|
||||
if (AssetDatabase.LoadAssetAtPath<Object>(desiredName) != null)
|
||||
{
|
||||
Debug.LogWarning(string.Format(Constants.ImpossibleToExtract, draggedObject.name));
|
||||
|
||||
rejected++;
|
||||
continue;
|
||||
}
|
||||
|
||||
string draggedObjectName = draggedObject.name;
|
||||
Object newObject = Object.Instantiate(draggedObject);
|
||||
newObject.name = draggedObjectName;
|
||||
AssetDatabase.CreateAsset(newObject, desiredName);
|
||||
|
||||
if (redirectRefsChoice == 0)
|
||||
{
|
||||
AssetDatabase.SaveAssets();
|
||||
if (!AssetReferenceRedirector.RedirectToStandaloneAsset(draggedObject, newObject))
|
||||
{
|
||||
Debug.LogWarning(string.Format(Constants.RedirectionFailed, draggedObjectName));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// After redirect, Refresh() may have invalidated the original reference.
|
||||
// Reload the sub-asset from disk if needed.
|
||||
Object objectToRemove = draggedObject;
|
||||
if (objectToRemove == null)
|
||||
{
|
||||
Object[] subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(draggedObjectPath);
|
||||
objectToRemove = subAssets.FirstOrDefault(a => a.name == draggedObjectName);
|
||||
}
|
||||
|
||||
if (objectToRemove != null)
|
||||
SubAssetsToolbox.RemoveSubAsset(draggedObjectPath, objectToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
if (rejected != 0) return DragAndDropVisualMode.Rejected;
|
||||
if (Path.GetDirectoryName(DragAndDrop.paths[0]) != destinationPath)
|
||||
return DragAndDropVisualMode.None; // Allows relocation of main assets
|
||||
|
||||
return DragAndDropVisualMode.Move; // Avoid confirmation popup when drag/dropping in the same folder
|
||||
}
|
||||
|
||||
// While dragging
|
||||
|
||||
if (destinationIsFolder)
|
||||
return DragAndDropVisualMode.Generic;
|
||||
|
||||
if (DragAndDrop.objectReferences.Any(AssetDatabase.IsSubAsset))
|
||||
return DragAndDropVisualMode.Link;
|
||||
|
||||
foreach (string path in DragAndDrop.paths)
|
||||
{
|
||||
if (string.Equals(path, destinationPath)) return DragAndDropVisualMode.None;
|
||||
if (!TypeChecker.ValidateSubAssetType(path, true)) return DragAndDropVisualMode.Rejected;
|
||||
if (!TypeChecker.ValidateDestinationType(destinationPath, true)) return DragAndDropVisualMode.Rejected;
|
||||
}
|
||||
|
||||
return DragAndDropVisualMode.Link;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e7efb048b08b4c78807019c63dfda0e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/DragAndDropAddon.cs
|
||||
uploadId: 869944
|
||||
8
Packages/io.continis.subassets/Editor/Settings.meta
Normal file
8
Packages/io.continis.subassets/Editor/Settings.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61c997c4c033849a6ac3e750b0b07069
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
public enum ReferencePatchingMode
|
||||
{
|
||||
Disabled = 0,
|
||||
AskEachTime = 1,
|
||||
AutoApply = 2
|
||||
}
|
||||
|
||||
public class SubAssetsToolboxSettings
|
||||
{
|
||||
public static PackageSetting<bool> WelcomeWindowSeen = new("general.welcomeWindowSeen", false);
|
||||
|
||||
public static readonly string[] DefaultExtensionsToScan =
|
||||
{
|
||||
"unity", "prefab", "asset", "mat", "controller", "overrideController",
|
||||
"playable", "signal", "mixer", "preset", "lighting", "giparams",
|
||||
"spriteatlas", "spriteatlasv2", "terrainlayer", "brush", "anim",
|
||||
"guiskin", "fontsettings", "flare", "renderTexture", "cubemap"
|
||||
};
|
||||
|
||||
public static PackageSetting<ReferencePatchingMode> PatchReferences =
|
||||
new("referencePatching.mode", ReferencePatchingMode.AskEachTime);
|
||||
|
||||
public static PackageSetting<string[]> PatchExtensions =
|
||||
new("referencePatching.extensions", DefaultExtensionsToScan);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e40b070acac1a4ec488641be6b410275
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/Settings/SubAssetsToolboxSettings.cs
|
||||
uploadId: 869944
|
||||
@@ -0,0 +1,33 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.SettingsManagement;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
static class SubAssetsToolboxSettingsManager
|
||||
{
|
||||
private static Settings instance;
|
||||
|
||||
internal static Settings settings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new Settings(Constants.PackageName, "Settings");
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PackageSetting<T> : UserSetting<T>
|
||||
{
|
||||
public PackageSetting(string key, T value)
|
||||
: base(SubAssetsToolboxSettingsManager.settings, key, value, SettingsScope.Project) { }
|
||||
}
|
||||
|
||||
public class UserPref<T> : UserSetting<T>
|
||||
{
|
||||
public UserPref(string key, T value)
|
||||
: base(SubAssetsToolboxSettingsManager.settings, key, value, SettingsScope.User) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 866ebc740692e48a6818f6a8a101bb8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/Settings/SubAssetsToolboxSettingsManager.cs
|
||||
uploadId: 869944
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
internal static class SubAssetsToolboxSettingsProvider
|
||||
{
|
||||
private const string SettingsPath = "Project/SubAssets Toolbox";
|
||||
private const string UxmlPath = "Packages/io.continis.subassets/UI/UIToolkit/ProjectSettings.uxml";
|
||||
|
||||
private static List<string> _extensions;
|
||||
private static ListView _listView;
|
||||
private static EnumField _redirectionModeField;
|
||||
private static Button _resetFileTypesBtn;
|
||||
|
||||
[SettingsProvider]
|
||||
private static SettingsProvider CreateSettingsProvider()
|
||||
{
|
||||
return new SettingsProvider(SettingsPath, SettingsScope.Project)
|
||||
{
|
||||
activateHandler = (_, rootElement) => CreateUI(rootElement),
|
||||
keywords = new[] { "subassets", "sub-assets", "redirect", "extensions" }
|
||||
};
|
||||
}
|
||||
|
||||
private static void SyncToSetting()
|
||||
{
|
||||
SubAssetsToolboxSettings.PatchExtensions.SetValue(_extensions.ToArray(), true);
|
||||
}
|
||||
|
||||
private static void CreateUI(VisualElement root)
|
||||
{
|
||||
ReferencePatchingMode currentMode = SubAssetsToolboxSettings.PatchReferences.value;
|
||||
bool redirectionIsEnabled = currentMode != ReferencePatchingMode.Disabled;
|
||||
_extensions = new List<string>(SubAssetsToolboxSettings.PatchExtensions.value);
|
||||
|
||||
VisualTreeAsset visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(UxmlPath);
|
||||
VisualElement template = visualTree.Instantiate();
|
||||
template.style.flexGrow = 1f;
|
||||
|
||||
_redirectionModeField = template.Q<EnumField>("RedirectionModeField");
|
||||
_redirectionModeField.Init(ReferencePatchingMode.Disabled);
|
||||
_redirectionModeField.SetValueWithoutNotify(currentMode);
|
||||
_redirectionModeField.RegisterValueChangedCallback(OnRedirectionModeChanged);
|
||||
|
||||
_listView = template.Q<ListView>("ExtensionsList");
|
||||
_listView.itemsSource = _extensions;
|
||||
_listView.SetEnabled(redirectionIsEnabled);
|
||||
_listView.makeItem = MakeItem;
|
||||
_listView.bindItem = BindItem;
|
||||
_listView.unbindItem = UnbindItem;
|
||||
|
||||
_listView.itemsAdded += indices =>
|
||||
{
|
||||
foreach (int i in indices)
|
||||
_extensions[i] = "ext";
|
||||
_listView.RefreshItems();
|
||||
SyncToSetting();
|
||||
};
|
||||
|
||||
_listView.itemsRemoved += _ => SyncToSetting();
|
||||
_listView.itemIndexChanged += (_, _) => SyncToSetting();
|
||||
|
||||
_resetFileTypesBtn = template.Q<Button>("ResetBtn");
|
||||
_resetFileTypesBtn.SetEnabled(redirectionIsEnabled);
|
||||
_resetFileTypesBtn.clicked += ResetExtensionsToDefaults;
|
||||
|
||||
root.Add(template);
|
||||
}
|
||||
|
||||
private static void OnRedirectionModeChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
ReferencePatchingMode mode = (ReferencePatchingMode)evt.newValue;
|
||||
SubAssetsToolboxSettings.PatchReferences.SetValue(mode, true);
|
||||
bool enabled = mode != ReferencePatchingMode.Disabled;
|
||||
_listView.SetEnabled(enabled);
|
||||
_resetFileTypesBtn.SetEnabled(enabled);
|
||||
}
|
||||
|
||||
private static void ResetExtensionsToDefaults()
|
||||
{
|
||||
_extensions.Clear();
|
||||
_extensions.AddRange(SubAssetsToolboxSettings.DefaultExtensionsToScan);
|
||||
_listView.RefreshItems();
|
||||
SyncToSetting();
|
||||
}
|
||||
|
||||
private static VisualElement MakeItem()
|
||||
{
|
||||
return new TextField();
|
||||
}
|
||||
|
||||
private static void BindItem(VisualElement element, int index)
|
||||
{
|
||||
TextField field = (TextField)element;
|
||||
field.SetValueWithoutNotify(_extensions[index]);
|
||||
field.userData = index;
|
||||
field.RegisterValueChangedCallback(OnExtensionChanged);
|
||||
}
|
||||
|
||||
private static void UnbindItem(VisualElement element, int index)
|
||||
{
|
||||
TextField field = (TextField)element;
|
||||
field.UnregisterValueChangedCallback(OnExtensionChanged);
|
||||
}
|
||||
|
||||
private static void OnExtensionChanged(ChangeEvent<string> evt)
|
||||
{
|
||||
TextField field = (TextField)evt.target;
|
||||
int index = (int)field.userData;
|
||||
_extensions[index] = evt.newValue;
|
||||
SyncToSetting();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9fe270b76b734b60a6b7c24fcf38c43
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/Settings/SubAssetsToolboxSettingsProvider.cs
|
||||
uploadId: 869944
|
||||
3
Packages/io.continis.subassets/Editor/SubAssetAware.meta
Normal file
3
Packages/io.continis.subassets/Editor/SubAssetAware.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15039554cb524e5e8b2c5e8a3f858acc
|
||||
timeCreated: 1720124815
|
||||
@@ -0,0 +1,26 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SubAssetsToolbox.Editor.SubAssetAware
|
||||
{
|
||||
static class SubAssetAwareMethods
|
||||
{
|
||||
public static void NotifyAwareObjectOfAddition(string parentObjectPath, Object newObject)
|
||||
{
|
||||
Object newParentObject = AssetDatabase.LoadAssetAtPath(parentObjectPath, typeof(Object));
|
||||
if (newParentObject is ISubAssetAware awareObject)
|
||||
{
|
||||
awareObject.AddSubAsset(newObject);
|
||||
}
|
||||
}
|
||||
|
||||
public static void NotifyAwareObjectOfRemoval(string parentObjectPath, Object objectToRemove)
|
||||
{
|
||||
Object oldParentObject = AssetDatabase.LoadAssetAtPath(parentObjectPath, typeof(Object));
|
||||
if (oldParentObject is ISubAssetAware awareObject)
|
||||
{
|
||||
awareObject.RemoveSubAsset(objectToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33b5b9d4670b4206bc714f3c45a52c59
|
||||
timeCreated: 1720022287
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/SubAssetAware/SubAssetAwareMethods.cs
|
||||
uploadId: 869944
|
||||
58
Packages/io.continis.subassets/Editor/SubAssetContextMenu.cs
Normal file
58
Packages/io.continis.subassets/Editor/SubAssetContextMenu.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
public static class SubAssetContextMenu
|
||||
{
|
||||
#if UNITY_6000
|
||||
private const int MenuPriority = 20;
|
||||
#else
|
||||
private const int MenuPriority = 19;
|
||||
#endif
|
||||
|
||||
[MenuItem("Assets/Delete Sub-Asset(s)", false, MenuPriority)]
|
||||
private static void DeleteSubAsset()
|
||||
{
|
||||
bool isOne = Selection.objects.Length == 1;
|
||||
|
||||
bool decision = EditorUtility.DisplayDialog(
|
||||
isOne ? "Delete Sub-Asset" : "Delete Sub-Assets",
|
||||
isOne
|
||||
? string.Format(Constants.ConfirmDeleteSubAsset, Selection.objects[0].name)
|
||||
: string.Format(Constants.ConfirmDeleteSubAssets, Selection.objects[0].name, Selection.objects[1].name),
|
||||
isOne ? "Delete" : "Delete all",
|
||||
"Cancel");
|
||||
|
||||
if (!decision) return;
|
||||
|
||||
foreach (Object obj in Selection.objects)
|
||||
{
|
||||
string parentObjectPath = AssetDatabase.GetAssetPath(obj);
|
||||
SubAssetsToolbox.RemoveSubAsset(parentObjectPath, obj);
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Delete Sub-Asset(s)", true)]
|
||||
private static bool ValidateDeleteSubAsset()
|
||||
{
|
||||
return Selection.objects.Length > 0 && Selection.objects.All(AssetDatabase.IsSubAsset);
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Rename Sub-Asset", false, MenuPriority)]
|
||||
private static void RenameSubAsset()
|
||||
{
|
||||
RenameSubAssetWindow.Open(Selection.activeObject);
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Rename Sub-Asset", true)]
|
||||
private static bool ValidateRenameSubAsset()
|
||||
{
|
||||
return Selection.objects.Length == 1 && AssetDatabase.IsSubAsset(Selection.activeObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ed8576868e8473292b2e9e96fa41ac0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/SubAssetContextMenu.cs
|
||||
uploadId: 869944
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "SubAssetsToolbox.Editor",
|
||||
"rootNamespace": "SubAssetsToolbox",
|
||||
"references": [
|
||||
"GUID:4fe8e68b5bc1841d0906774ad72212e0",
|
||||
"GUID:49818357e697641afb75d2f8acaf1861",
|
||||
"GUID:69448af7b92c7f342b298e06a37122aa"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.addressables",
|
||||
"expression": "1.0.0",
|
||||
"define": "ADDRESSABLES_INSTALLED"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33f3227df0b844d9ebd60340b54d45ae
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/SubAssetsToolbox.Editor.asmdef
|
||||
uploadId: 869944
|
||||
25
Packages/io.continis.subassets/Editor/SubAssetsToolbox.cs
Normal file
25
Packages/io.continis.subassets/Editor/SubAssetsToolbox.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using SubAssetsToolbox.Editor.SubAssetAware;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
static class SubAssetsToolbox
|
||||
{
|
||||
public static void AddSubAsset(string destinationPath, Object objectToClone, out Object newClone)
|
||||
{
|
||||
newClone = Object.Instantiate(objectToClone);
|
||||
newClone.name = objectToClone.name;
|
||||
AssetDatabase.AddObjectToAsset(newClone, destinationPath);
|
||||
|
||||
SubAssetAwareMethods.NotifyAwareObjectOfAddition(destinationPath, newClone);
|
||||
}
|
||||
|
||||
public static void RemoveSubAsset(string oldParentPath, Object objectToRemove)
|
||||
{
|
||||
SubAssetAwareMethods.NotifyAwareObjectOfRemoval(oldParentPath, objectToRemove);
|
||||
|
||||
AssetDatabase.RemoveObjectFromAsset(objectToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af033a8f131d48e3a34167d2ff27a519
|
||||
timeCreated: 1719906421
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/SubAssetsToolbox.cs
|
||||
uploadId: 869944
|
||||
8
Packages/io.continis.subassets/Editor/Utilities.meta
Normal file
8
Packages/io.continis.subassets/Editor/Utilities.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd9b62119243e4abb9b82c65372d038f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
#if ADDRESSABLES_INSTALLED
|
||||
using System.Collections.Generic;
|
||||
using SubAssetsToolbox.Editor;
|
||||
using UnityEditor.AddressableAssets;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SubAssetsToolbox.Utilities
|
||||
{
|
||||
internal static class AddressableEntryMigrator
|
||||
{
|
||||
internal static void MigrateOrRemoveEntry(string oldGuid, string parentGuid, string parentName)
|
||||
{
|
||||
AddressableAssetSettings settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
if (settings == null) return;
|
||||
|
||||
AddressableAssetEntry oldEntry = settings.FindAssetEntry(oldGuid);
|
||||
if (oldEntry == null) return;
|
||||
|
||||
string oldName = oldEntry.MainAsset != null ? oldEntry.MainAsset.name : oldGuid;
|
||||
string address = oldEntry.address;
|
||||
AddressableAssetGroup group = oldEntry.parentGroup;
|
||||
HashSet<string> labels = new(oldEntry.labels);
|
||||
|
||||
AddressableAssetEntry parentEntry = settings.FindAssetEntry(parentGuid);
|
||||
|
||||
if (parentEntry != null)
|
||||
{
|
||||
// New host asset was already an Addressable, so the new sub-asset
|
||||
// is now addressable thanks to its parent – and the old entry can be removed.
|
||||
settings.RemoveAssetEntry(oldGuid);
|
||||
Debug.Log(string.Format(Constants.AddressableEntryRemoved, oldName, address, parentName));
|
||||
return;
|
||||
}
|
||||
|
||||
// The new host asset wasn't an Addressable, so the new host needs to become one
|
||||
settings.RemoveAssetEntry(oldGuid);
|
||||
AddressableAssetEntry newEntry = settings.CreateOrMoveEntry(parentGuid, group, false, false);
|
||||
newEntry.address = address;
|
||||
foreach (string label in labels)
|
||||
newEntry.SetLabel(label, true, true, false);
|
||||
settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, newEntry, true);
|
||||
|
||||
Debug.Log(string.Format(Constants.AddressableEntryMigrated, oldName, address, parentName));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2573a7e815d364c93b34c2a9efda007f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/Utilities/AddressableEntryMigrator.cs
|
||||
uploadId: 869944
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using SubAssetsToolbox.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SubAssetsToolbox.Utilities
|
||||
{
|
||||
public static class AssetReferenceRedirector
|
||||
{
|
||||
public static bool RedirectToSubAsset(Object sourceAsset, Object destinationSubAsset)
|
||||
{
|
||||
string sourceFileID = GetFileID(sourceAsset);
|
||||
string destFileID = GetFileID(destinationSubAsset);
|
||||
if (sourceFileID == null || destFileID == null) return false;
|
||||
|
||||
string sourceGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(sourceAsset));
|
||||
string destGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(destinationSubAsset));
|
||||
|
||||
RedirectReferencesInYaml(sourceGuid, sourceFileID, destGuid, destFileID);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool RedirectToStandaloneAsset(Object sourceSubAsset, Object destinationAsset)
|
||||
{
|
||||
string sourceFileID = GetFileID(sourceSubAsset);
|
||||
string destFileID = GetFileID(destinationAsset);
|
||||
if (sourceFileID == null || destFileID == null) return false;
|
||||
|
||||
string sourceGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(sourceSubAsset));
|
||||
string destGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(destinationAsset));
|
||||
|
||||
RedirectReferencesInYaml(sourceGuid, sourceFileID, destGuid, destFileID);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void RedirectReferencesInYaml(string oldGuid, string oldFileID, string newGuid, string newFileID)
|
||||
{
|
||||
string[] extensions = SubAssetsToolboxSettings.PatchExtensions.value;
|
||||
string[] allFiles = Directory.GetFiles("Assets", "*.*", SearchOption.AllDirectories)
|
||||
.Where(f => extensions.Any(ext => f.EndsWith("." + ext)))
|
||||
.ToArray();
|
||||
|
||||
// Save and cache open scenes, if needed
|
||||
bool needToRestoreScenes = false;
|
||||
SceneSetup[] sceneManagerSetup = EditorSceneManager.GetSceneManagerSetup();
|
||||
string[] sceneFiles = allFiles.Where(f => f.EndsWith(".unity")).ToArray();
|
||||
if (sceneFiles.Length > 0)
|
||||
{
|
||||
needToRestoreScenes = sceneManagerSetup.Any(sceneSetup => sceneFiles.Any(f => f == sceneSetup.path));
|
||||
|
||||
if (needToRestoreScenes)
|
||||
{
|
||||
EditorSceneManager.SaveOpenScenes();
|
||||
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string filePath in allFiles)
|
||||
{
|
||||
string content = File.ReadAllText(filePath);
|
||||
if (!content.Contains(oldGuid)) continue;
|
||||
|
||||
string oldPattern = $"{{fileID: {oldFileID}, guid: {oldGuid}";
|
||||
string replacement = $"{{fileID: {newFileID}, guid: {newGuid}";
|
||||
|
||||
string newContent = content.Replace(oldPattern, replacement);
|
||||
|
||||
if (newContent == content) continue;
|
||||
|
||||
//Debug.Log($"Updated references in {filePath}");
|
||||
File.WriteAllText(filePath, newContent);
|
||||
}
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
if (needToRestoreScenes)
|
||||
EditorApplication.delayCall += () =>
|
||||
EditorSceneManager.RestoreSceneManagerSetup(sceneManagerSetup);
|
||||
}
|
||||
|
||||
static string GetFileID(Object obj)
|
||||
{
|
||||
long id = (long)Unsupported.GetLocalIdentifierInFileForPersistentObject(obj);
|
||||
if (id != 0) return id.ToString();
|
||||
|
||||
// Reflection fallback
|
||||
SerializedObject so = new(obj);
|
||||
PropertyInfo inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
inspectorModeInfo?.SetValue(so, InspectorMode.Debug, null);
|
||||
|
||||
SerializedProperty localIdProp = so.FindProperty("m_LocalIdentifierInFile");
|
||||
if (localIdProp != null && localIdProp.longValue != 0)
|
||||
return localIdProp.longValue.ToString();
|
||||
|
||||
Debug.LogError(string.Format(Constants.FileIdRetrievalFailed, obj.name));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e19698ca5817474fbc5e211dae150119
|
||||
timeCreated: 1770839624
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/Utilities/AssetReferenceRedirector.cs
|
||||
uploadId: 869944
|
||||
64
Packages/io.continis.subassets/Editor/Utilities/Constants.cs
Normal file
64
Packages/io.continis.subassets/Editor/Utilities/Constants.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const string MenuItemBaseName = "Tools/SubAssets Toolbox/";
|
||||
|
||||
// URLs
|
||||
public const string ReviewUrl = "https://assetstore.unity.com/packages/tools/utilities/subassets-toolbox-284154#reviews";
|
||||
public const string DiscordUrl = "https://discord.com/invite/rCRug7Szr8";
|
||||
public const string DocumentationUrl = "https://tools.continis.io/v/subassets-toolbox";
|
||||
public const string OtherToolsUrl = "https://assetstore.unity.com/publishers/87819";
|
||||
public const string SupportEmail = "mailto:buoybase@gmail.com";
|
||||
|
||||
public const string PackageName = "io.continis.subassets-toolbox";
|
||||
public const string PackageAssetsFolder = "Packages/io.continis.subassets-toolbox";
|
||||
public const string PackageReadableName = "SubAssets Toolbox";
|
||||
|
||||
// Error messages - Sub-asset type restrictions
|
||||
internal static readonly string AddingPrefabAsSubAsset = $"({PackageReadableName}) Adding a Prefab as a SubAsset is not allowed.";
|
||||
internal static readonly string AddingFolderAsSubAsset = $"({PackageReadableName}) Adding a Folder as a SubAsset is not allowed.";
|
||||
internal static readonly string AddingScriptAsSubAsset = $"({PackageReadableName}) Adding a script as a SubAsset is not allowed.";
|
||||
internal static readonly string Adding3DModelAsSubAsset = $"({PackageReadableName}) Adding a 3D model as a SubAsset is not allowed.";
|
||||
internal static readonly string AddingShaderAsSubAsset = $"({PackageReadableName}) Adding a shader as a SubAsset is not allowed.";
|
||||
|
||||
// Error messages - Destination type restrictions
|
||||
internal static readonly string AddingSubAssetToScript = $"({PackageReadableName}) Adding SubAssets to a script is not allowed.";
|
||||
internal static readonly string AddingSubAssetToShader = $"({PackageReadableName}) Adding SubAssets to a Shader is not allowed.";
|
||||
internal static readonly string AddingSubAssetTo3DModel = $"({PackageReadableName}) Adding SubAssets to a 3D model is not allowed.";
|
||||
internal static readonly string AddingSubAssetToMixer = $"({PackageReadableName}) Adding SubAssets to an AudioMixer is not allowed.";
|
||||
internal static readonly string AddingSubAssetToScene = $"({PackageReadableName}) Adding SubAssets to a scene is not allowed.";
|
||||
|
||||
// Warnings
|
||||
internal static readonly string ExtensionDefaultedToAsset = $"({PackageReadableName}) It was impossible to determine the removed sub-assets extension, so it has defaulted to \".asset\". " +
|
||||
"We recommend to change it now in your file system, if it matters.";
|
||||
|
||||
internal static readonly string ImpossibleToExtract =
|
||||
$"({PackageReadableName}) Impossible to extract {{0}} from its main asset. An asset with the same name already exists in the same location.";
|
||||
internal static readonly string RedirectionFailed = $"({PackageReadableName}) Reference patching failed for {{0}}. The original asset was kept to avoid broken references.";
|
||||
internal static readonly string FileIdRetrievalFailed = $"({PackageReadableName}) Could not retrieve fileID for {{0}}. Reference redirection skipped for this object.";
|
||||
|
||||
// Dialog messages
|
||||
internal const string LossOfDataWarning = "If you choose not to, you will have to " +
|
||||
"fix those references manually.\n\n" +
|
||||
"This process might lead to loss of data, " +
|
||||
"so make sure you have your project under version control.";
|
||||
|
||||
internal const string ConfirmAddAsSubAsset = "Are you sure you want to add {0} as a sub-asset to {1}?";
|
||||
internal const string ConfirmAddAsSubAssets = "Are you sure you want to add {0}, {1}, (...) as sub-assets to {2}?";
|
||||
|
||||
internal const string ConfirmDeleteSubAsset = "Are you sure you want to delete {0}?";
|
||||
internal const string ConfirmDeleteSubAssets = "Are you sure you want to delete {0}, {1}, (...)?";
|
||||
|
||||
internal const string PatchRefsToSubAssetPrompt =
|
||||
"Do you want to patch all references to the dragged asset(s) to point to the newly-created sub-asset(s)? " + LossOfDataWarning;
|
||||
internal const string PatchRefsToStandalonePrompt =
|
||||
"Do you want to patch all references to the dragged sub-asset(s) to point to the newly-created asset(s)? " + LossOfDataWarning;
|
||||
|
||||
// Addressable messages
|
||||
internal static readonly string AddressableEntryMigrated =
|
||||
$"({PackageReadableName}) Addressable entry '{{0}}' (address: {{1}}) was migrated to the parent asset {{2}}.";
|
||||
internal static readonly string AddressableEntryRemoved =
|
||||
$"({PackageReadableName}) Addressable entry '{{0}}' (address: {{1}}) was removed. The parent asset {{2}} is already Addressable.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebbd0fca8514146f8bcbc90035ba23d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/Utilities/Constants.cs
|
||||
uploadId: 869944
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SubAssetsToolbox.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Audio;
|
||||
using UnityEngine.U2D;
|
||||
|
||||
namespace SubAssetsToolbox.Utilities
|
||||
{
|
||||
public static class ExtensionMap
|
||||
{
|
||||
public static string GetFileExtensionByType(Type t)
|
||||
{
|
||||
if (_extensions.TryGetValue(t, out string ext))
|
||||
return ext;
|
||||
else
|
||||
{
|
||||
if(!typeof(ScriptableObject).IsAssignableFrom(t))
|
||||
Debug.LogWarning(Constants.ExtensionDefaultedToAsset);
|
||||
|
||||
return "asset";
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<Type, string> _extensions = new()
|
||||
{
|
||||
// Rendering / Shaders
|
||||
{ typeof(Material), "mat" },
|
||||
{ typeof(Shader), "shader" },
|
||||
{ typeof(ComputeShader), "compute" },
|
||||
{ typeof(ShaderVariantCollection), "shadervariants" },
|
||||
{ typeof(LightmapParameters), "giparams" },
|
||||
{ typeof(LightingSettings), "lighting" },
|
||||
|
||||
// Animation
|
||||
{ typeof(AnimationClip), "anim" },
|
||||
{ typeof(AnimatorController), "controller" },
|
||||
{ typeof(AnimatorOverrideController), "overrideController" },
|
||||
{ typeof(AvatarMask), "mask" },
|
||||
{ typeof(Avatar), "avatar" },
|
||||
|
||||
// Physics
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
{ typeof(PhysicsMaterial), "physicsMaterial" },
|
||||
#else
|
||||
{ typeof(PhysicMaterial), "physicMaterial" },
|
||||
#endif
|
||||
{ typeof(PhysicsMaterial2D), "physicsMaterial2D" },
|
||||
|
||||
// Audio
|
||||
{ typeof(AudioMixer), "mixer" },
|
||||
|
||||
// 2D
|
||||
{ typeof(SpriteAtlas), "spriteatlas" },
|
||||
|
||||
// Textures
|
||||
{ typeof(Cubemap), "cubemap" },
|
||||
{ typeof(RenderTexture), "rendertexture" },
|
||||
|
||||
// 3D
|
||||
{ typeof(Mesh), "mesh" },
|
||||
{ typeof(TerrainLayer), "terrainlayer" },
|
||||
|
||||
// GUI
|
||||
{ typeof(Font), "fontsettings" },
|
||||
{ typeof(GUISkin), "guiskin" },
|
||||
|
||||
// Effects
|
||||
{ typeof(Flare), "flare" },
|
||||
{ typeof(LightingDataAsset), "lighting" }
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3732dc6b75842c8a5d9f63611840216
|
||||
timeCreated: 1757267868
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/Utilities/ExtensionMap.cs
|
||||
uploadId: 869944
|
||||
130
Packages/io.continis.subassets/Editor/Utilities/TypeChecker.cs
Normal file
130
Packages/io.continis.subassets/Editor/Utilities/TypeChecker.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
public static class TypeChecker
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if a path contains a type that can be made into a sub-asset
|
||||
/// </summary>
|
||||
public static bool ValidateSubAssetType(string path, bool silent = false)
|
||||
{
|
||||
if (AssetDatabase.IsValidFolder(path))
|
||||
{
|
||||
if (!silent) Debug.LogError(Constants.AddingFolderAsSubAsset);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path.EndsWith(".prefab"))
|
||||
{
|
||||
if (!silent) Debug.LogError(Constants.AddingPrefabAsSubAsset);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path.EndsWith(".cs"))
|
||||
{
|
||||
if (!silent) Debug.LogError(Constants.AddingScriptAsSubAsset);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsShader(path))
|
||||
{
|
||||
if (!silent) Debug.LogError(Constants.AddingShaderAsSubAsset);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Is3DModel(path))
|
||||
{
|
||||
if (!silent) Debug.LogError(Constants.Adding3DModelAsSubAsset);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path.EndsWith(".meta"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a path contains a type that can accept sub-assets
|
||||
/// </summary>
|
||||
public static bool ValidateDestinationType(string destinationPath, bool silent = false)
|
||||
{
|
||||
if (destinationPath.EndsWith(".cs"))
|
||||
{
|
||||
if (!silent) Debug.LogError(Constants.AddingSubAssetToScript);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (destinationPath.EndsWith(".unity"))
|
||||
{
|
||||
if (!silent) Debug.LogError(Constants.AddingSubAssetToScene);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsShader(destinationPath))
|
||||
{
|
||||
if (!silent) Debug.LogError(Constants.AddingSubAssetToShader);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Is3DModel(destinationPath))
|
||||
{
|
||||
if (!silent) Debug.LogError(Constants.AddingSubAssetTo3DModel);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (destinationPath.EndsWith(".mixer"))
|
||||
{
|
||||
if (!silent) Debug.LogError(Constants.AddingSubAssetToMixer);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (destinationPath.EndsWith(".meta"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a path contains a type where the right-click is already doing something else
|
||||
/// i.e. when right-clicking on a Shader and creating a Material, it should create a Material
|
||||
/// with that Shader and not a Material as a sub-object of a Shader.
|
||||
/// </summary>
|
||||
public static bool HasDefaultAction(string newObjectPath, string destinationPath)
|
||||
{
|
||||
// Prefab variants
|
||||
if (newObjectPath.EndsWith(".prefab") && destinationPath.EndsWith(".prefab"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Variant of a 3D model
|
||||
bool destinationIs3DModel = Is3DModel(destinationPath);
|
||||
if (newObjectPath.EndsWith(".prefab") && destinationIs3DModel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Material from shader
|
||||
bool destinationIsShader = IsShader(destinationPath);
|
||||
if (newObjectPath.EndsWith(".mat") && destinationIsShader)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsShader(string path) => path.EndsWith(".shader") || path.EndsWith(".shadergraph");
|
||||
|
||||
private static bool Is3DModel(string path) =>
|
||||
path.EndsWith(".fbx") || path.EndsWith(".blend") ||
|
||||
path.EndsWith(".obj") || path.EndsWith(".3ds");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e015603f942f4b20bafb85236f2ac50d
|
||||
timeCreated: 1741992985
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/Utilities/TypeChecker.cs
|
||||
uploadId: 869944
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 626be9248eedc46f8933a153c87205f4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
public static class MenuItems
|
||||
{
|
||||
[InitializeOnLoadMethod]
|
||||
public static void CheckShowWelcomeScreen()
|
||||
{
|
||||
if (!SubAssetsToolboxSettings.WelcomeWindowSeen.value)
|
||||
{
|
||||
EditorApplication.delayCall += WelcomeScreen.Init;
|
||||
SubAssetsToolboxSettings.WelcomeWindowSeen.SetValue(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem(Constants.MenuItemBaseName + "Documentation", false, 100)]
|
||||
public static void OpenDocumentation() => Application.OpenURL(Constants.DocumentationUrl);
|
||||
|
||||
[MenuItem(Constants.MenuItemBaseName + "Email support", false, 101)]
|
||||
public static void EmailSupport() => Application.OpenURL(Constants.SupportEmail);
|
||||
|
||||
[MenuItem(Constants.MenuItemBaseName + "Join Discord", false, 102)]
|
||||
public static void JoinDiscord() => Application.OpenURL(Constants.ReviewUrl);
|
||||
|
||||
[MenuItem(Constants.MenuItemBaseName + "Write a review", false, 105)]
|
||||
public static void LeaveAReview() => Application.OpenURL(Constants.ReviewUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fcfb5b3277a0f43c292486d6198f002b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/WindowsAndMenus/MenuItems.cs
|
||||
uploadId: 869944
|
||||
@@ -0,0 +1,87 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
internal class RenameSubAssetWindow : EditorWindow
|
||||
{
|
||||
[SerializeField] private VisualTreeAsset _visualTreeAsset;
|
||||
|
||||
private Object _target;
|
||||
private TextField _nameField;
|
||||
private Button _renameButton;
|
||||
|
||||
public static void Open(Object subAsset)
|
||||
{
|
||||
RenameSubAssetWindow window = GetWindow<RenameSubAssetWindow>(true, "Rename Sub-Asset");
|
||||
window._target = subAsset;
|
||||
window.minSize = new Vector2(300f, 70f);
|
||||
window.maxSize = new Vector2(300f, 70f);
|
||||
window.Populate();
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void CreateGUI()
|
||||
{
|
||||
VisualElement root = rootVisualElement;
|
||||
VisualElement template = _visualTreeAsset.Instantiate();
|
||||
template.style.flexGrow = 1f;
|
||||
|
||||
_nameField = template.Q<TextField>("NameField");
|
||||
_nameField.selectAllOnFocus = true;
|
||||
_nameField.RegisterValueChangedCallback(_ => UpdateRenameButton());
|
||||
_nameField.RegisterCallback<KeyUpEvent>(OnKeyUp);
|
||||
|
||||
template.Q<Button>("CancelBtn").clicked += Close;
|
||||
_renameButton = template.Q<Button>("RenameBtn");
|
||||
_renameButton.clicked += PerformRename;
|
||||
|
||||
root.Add(template);
|
||||
UpdateRenameButton();
|
||||
}
|
||||
|
||||
private void OnKeyUp(KeyUpEvent evt)
|
||||
{
|
||||
switch (evt.keyCode)
|
||||
{
|
||||
case KeyCode.Return:
|
||||
case KeyCode.KeypadEnter:
|
||||
PerformRename();
|
||||
break;
|
||||
|
||||
case KeyCode.Escape:
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Populate()
|
||||
{
|
||||
_nameField.value = _target != null ? _target.name : "";
|
||||
UpdateRenameButton();
|
||||
_nameField.schedule.Execute(() =>
|
||||
{
|
||||
_nameField.Focus();
|
||||
_nameField.SelectAll();
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateRenameButton()
|
||||
{
|
||||
bool valid = _target != null
|
||||
&& !string.IsNullOrWhiteSpace(_nameField.value)
|
||||
&& _nameField.value != _target.name;
|
||||
|
||||
_renameButton.SetEnabled(valid);
|
||||
}
|
||||
|
||||
private void PerformRename()
|
||||
{
|
||||
_target.name = _nameField.value;
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6c1e8c8d4e4648ff89db9289140ebc1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- m_ViewDataDictionary: {instanceID: 0}
|
||||
- _visualTreeAsset: {fileID: 9197481963319205126, guid: b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7,
|
||||
type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/WindowsAndMenus/RenameSubAssetWindow.cs
|
||||
uploadId: 869944
|
||||
@@ -0,0 +1,37 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace SubAssetsToolbox.Editor
|
||||
{
|
||||
public class WelcomeScreen : EditorWindow
|
||||
{
|
||||
[SerializeField] private VisualTreeAsset _visualTreeAsset;
|
||||
private static WelcomeScreen _window;
|
||||
|
||||
[MenuItem(Constants.MenuItemBaseName + "Welcome screen", false, 0)]
|
||||
public static void Init()
|
||||
{
|
||||
_window = GetWindow<WelcomeScreen>(true);
|
||||
_window.titleContent = new GUIContent("SubAssets Toolbox");
|
||||
_window.minSize = new Vector2(300f, 400f);
|
||||
_window.maxSize = new Vector2(300f, 400f);
|
||||
_window.Show();
|
||||
}
|
||||
|
||||
private void CreateGUI()
|
||||
{
|
||||
VisualElement root = rootVisualElement;
|
||||
VisualElement templateWindow = _visualTreeAsset.Instantiate();
|
||||
templateWindow.style.flexGrow = 1f;
|
||||
|
||||
templateWindow.Q<Button>("DocsBtn").clicked += () => Application.OpenURL(Constants.DocumentationUrl);
|
||||
templateWindow.Q<Button>("EmailBtn").clicked += () => Application.OpenURL(Constants.SupportEmail);
|
||||
templateWindow.Q<Button>("DiscordBtn").clicked += () => Application.OpenURL(Constants.DiscordUrl);
|
||||
templateWindow.Q<Button>("OtherToolsBtn").clicked += () => Application.OpenURL(Constants.OtherToolsUrl);
|
||||
templateWindow.Q<Button>("ReviewBtn").clicked += () => Application.OpenURL(Constants.ReviewUrl);
|
||||
|
||||
root.Add(templateWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a17879cf1b9d14c45a1c43136fa833e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- m_ViewDataDictionary: {instanceID: 0}
|
||||
- _visualTreeAsset: {fileID: 9197481963319205126, guid: 1ad4f2b6f50fc4b74a7886c0f754da60,
|
||||
type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284154
|
||||
packageName: SubAssets Toolbox
|
||||
packageVersion: 1.1.0
|
||||
assetPath: Packages/io.continis.subassets/Editor/WindowsAndMenus/WelcomeScreen.cs
|
||||
uploadId: 869944
|
||||
Reference in New Issue
Block a user