313 lines
12 KiB
C#
313 lines
12 KiB
C#
using UnityEditor;
|
|
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using UnityEditor.Experimental.SceneManagement;
|
|
|
|
namespace Editor
|
|
{
|
|
public class PrefabVariantGeneratorWindow : EditorWindow
|
|
{
|
|
private GameObject sourcePrefab;
|
|
private GameObject previousSourcePrefab;
|
|
private List<Sprite> selectedSprites = new List<Sprite>();
|
|
private Vector2 scrollPosition;
|
|
private string variantSaveFolder = "Assets/Prefabs/Variants";
|
|
private string namingPattern = "{0}_{1}"; // {0} = prefab name, {1} = sprite name
|
|
private bool userChangedSavePath = false;
|
|
|
|
[MenuItem("Tools/Prefab Variant Generator")]
|
|
public static void ShowWindow()
|
|
{
|
|
var window = GetWindow<PrefabVariantGeneratorWindow>("Prefab Variant Generator");
|
|
window.minSize = new Vector2(400, 500);
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
EditorGUILayout.LabelField("Prefab Variant Generator", EditorStyles.boldLabel);
|
|
EditorGUILayout.HelpBox("This tool generates prefab variants with different sprites assigned to a sprite renderer.", MessageType.Info);
|
|
EditorGUILayout.Space();
|
|
|
|
// Source Prefab Selection
|
|
EditorGUILayout.LabelField("Step 1: Select Source Prefab", EditorStyles.boldLabel);
|
|
|
|
// Store previous selection to detect changes
|
|
GameObject newSourcePrefab = (GameObject)EditorGUILayout.ObjectField("Source Prefab", sourcePrefab, typeof(GameObject), false);
|
|
|
|
// Check if prefab selection changed
|
|
if (newSourcePrefab != previousSourcePrefab)
|
|
{
|
|
sourcePrefab = newSourcePrefab;
|
|
previousSourcePrefab = newSourcePrefab;
|
|
|
|
// Auto-set save folder to match source prefab's directory if a valid prefab is selected
|
|
if (sourcePrefab != null && !userChangedSavePath)
|
|
{
|
|
string prefabPath = AssetDatabase.GetAssetPath(sourcePrefab);
|
|
if (!string.IsNullOrEmpty(prefabPath))
|
|
{
|
|
variantSaveFolder = Path.GetDirectoryName(prefabPath).Replace("\\", "/");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Warn if not a prefab
|
|
if (sourcePrefab != null && !PrefabUtility.IsPartOfPrefabAsset(sourcePrefab) && !PrefabUtility.IsPartOfPrefabInstance(sourcePrefab))
|
|
{
|
|
EditorGUILayout.HelpBox("Please select a prefab asset.", MessageType.Warning);
|
|
}
|
|
|
|
// Sprite Selection
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Step 2: Select Sprites", EditorStyles.boldLabel);
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
// Drag and drop area for sprites
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Height(80));
|
|
EditorGUILayout.LabelField("Drag and drop sprites here", EditorStyles.centeredGreyMiniLabel);
|
|
|
|
Rect dropArea = GUILayoutUtility.GetRect(0, 50, GUILayout.ExpandWidth(true));
|
|
Event evt = Event.current;
|
|
switch (evt.type)
|
|
{
|
|
case EventType.DragUpdated:
|
|
case EventType.DragPerform:
|
|
if (!dropArea.Contains(evt.mousePosition))
|
|
break;
|
|
|
|
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
|
|
|
if (evt.type == EventType.DragPerform)
|
|
{
|
|
DragAndDrop.AcceptDrag();
|
|
|
|
foreach (var draggedObject in DragAndDrop.objectReferences)
|
|
{
|
|
if (draggedObject is Sprite sprite)
|
|
{
|
|
if (!selectedSprites.Contains(sprite))
|
|
selectedSprites.Add(sprite);
|
|
}
|
|
else if (draggedObject is Texture2D texture)
|
|
{
|
|
// Try to get sprites from texture
|
|
string texturePath = AssetDatabase.GetAssetPath(texture);
|
|
var sprites = AssetDatabase.LoadAllAssetsAtPath(texturePath)
|
|
.OfType<Sprite>()
|
|
.ToArray();
|
|
|
|
foreach (var s in sprites)
|
|
{
|
|
if (!selectedSprites.Contains(s))
|
|
selectedSprites.Add(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
evt.Use();
|
|
}
|
|
break;
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
|
|
if (GUILayout.Button("Add Selected Sprites", GUILayout.Width(120), GUILayout.Height(80)))
|
|
{
|
|
var selectedObjects = Selection.objects;
|
|
foreach (var obj in selectedObjects)
|
|
{
|
|
if (obj is Sprite sprite)
|
|
{
|
|
if (!selectedSprites.Contains(sprite))
|
|
selectedSprites.Add(sprite);
|
|
}
|
|
else if (obj is Texture2D texture)
|
|
{
|
|
// Try to get sprites from texture
|
|
string texturePath = AssetDatabase.GetAssetPath(texture);
|
|
var sprites = AssetDatabase.LoadAllAssetsAtPath(texturePath)
|
|
.OfType<Sprite>()
|
|
.ToArray();
|
|
|
|
foreach (var s in sprites)
|
|
{
|
|
if (!selectedSprites.Contains(s))
|
|
selectedSprites.Add(s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
if (GUILayout.Button("Clear Sprites"))
|
|
{
|
|
selectedSprites.Clear();
|
|
}
|
|
|
|
// Display selected sprites
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField($"Selected Sprites ({selectedSprites.Count}):", EditorStyles.boldLabel);
|
|
|
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Height(150));
|
|
for (int i = selectedSprites.Count - 1; i >= 0; i--)
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
selectedSprites[i] = (Sprite)EditorGUILayout.ObjectField(selectedSprites[i], typeof(Sprite), false);
|
|
|
|
// Preview sprite
|
|
if (selectedSprites[i] != null)
|
|
{
|
|
GUILayout.Box(selectedSprites[i].texture, GUILayout.Width(40), GUILayout.Height(40));
|
|
}
|
|
|
|
if (GUILayout.Button("Remove", GUILayout.Width(60)))
|
|
{
|
|
selectedSprites.RemoveAt(i);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
EditorGUILayout.EndScrollView();
|
|
|
|
// Output settings
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Step 3: Output Settings", EditorStyles.boldLabel);
|
|
|
|
// Save folder
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.PrefixLabel("Save Folder");
|
|
EditorGUILayout.SelectableLabel(variantSaveFolder, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
|
{
|
|
string newFolder = PrefabEditorUtility.SelectFolder(variantSaveFolder, "Prefabs/Variants");
|
|
if (newFolder != variantSaveFolder)
|
|
{
|
|
variantSaveFolder = newFolder;
|
|
userChangedSavePath = true; // Mark that user manually changed the path
|
|
}
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
// Naming pattern
|
|
// Add a reset button if user changed the path and a valid prefab is selected
|
|
if (userChangedSavePath && sourcePrefab != null)
|
|
{
|
|
string prefabPath = AssetDatabase.GetAssetPath(sourcePrefab);
|
|
if (!string.IsNullOrEmpty(prefabPath))
|
|
{
|
|
if (GUILayout.Button("Reset Path to Prefab Directory"))
|
|
{
|
|
variantSaveFolder = Path.GetDirectoryName(prefabPath).Replace("\\", "/");
|
|
userChangedSavePath = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.PrefixLabel("Naming Pattern");
|
|
namingPattern = EditorGUILayout.TextField(namingPattern);
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.HelpBox("Use {0} for prefab name and {1} for sprite name", MessageType.Info);
|
|
|
|
// Generate button
|
|
EditorGUILayout.Space();
|
|
GUI.enabled = sourcePrefab != null && selectedSprites.Count > 0;
|
|
if (GUILayout.Button("Generate Prefab Variants", GUILayout.Height(30)))
|
|
{
|
|
GeneratePrefabVariants();
|
|
}
|
|
GUI.enabled = true;
|
|
}
|
|
|
|
private void GeneratePrefabVariants()
|
|
{
|
|
if (sourcePrefab == null)
|
|
{
|
|
EditorUtility.DisplayDialog("Error", "Please select a source prefab.", "OK");
|
|
return;
|
|
}
|
|
|
|
if (selectedSprites.Count == 0)
|
|
{
|
|
EditorUtility.DisplayDialog("Error", "Please select at least one sprite.", "OK");
|
|
return;
|
|
}
|
|
|
|
// Ensure the save folder exists
|
|
if (!AssetDatabase.IsValidFolder(variantSaveFolder))
|
|
{
|
|
string[] folderPath = variantSaveFolder.Split('/');
|
|
string currentPath = folderPath[0];
|
|
|
|
for (int i = 1; i < folderPath.Length; i++)
|
|
{
|
|
string folderName = folderPath[i];
|
|
string newPath = Path.Combine(currentPath, folderName);
|
|
|
|
if (!AssetDatabase.IsValidFolder(newPath))
|
|
{
|
|
AssetDatabase.CreateFolder(currentPath, folderName);
|
|
}
|
|
|
|
currentPath = newPath;
|
|
}
|
|
|
|
AssetDatabase.Refresh();
|
|
}
|
|
|
|
string sourcePrefabPath = AssetDatabase.GetAssetPath(sourcePrefab);
|
|
string prefabName = Path.GetFileNameWithoutExtension(sourcePrefabPath);
|
|
int successCount = 0;
|
|
|
|
// For each sprite, create a prefab variant
|
|
foreach (var sprite in selectedSprites)
|
|
{
|
|
if (sprite == null) continue;
|
|
|
|
string variantName = string.Format(namingPattern, prefabName, sprite.name);
|
|
variantName = PrefabEditorUtility.SanitizeFileName(variantName);
|
|
string variantPath = Path.Combine(variantSaveFolder, variantName + ".prefab").Replace("\\", "/");
|
|
|
|
// Create the prefab variant
|
|
GameObject prefabInstance = (GameObject)PrefabUtility.InstantiatePrefab(sourcePrefab);
|
|
|
|
try
|
|
{
|
|
// Check if it has a SpriteRenderer
|
|
SpriteRenderer spriteRenderer = prefabInstance.GetComponent<SpriteRenderer>();
|
|
if (spriteRenderer == null)
|
|
{
|
|
// Add a SpriteRenderer if it doesn't exist
|
|
spriteRenderer = prefabInstance.AddComponent<SpriteRenderer>();
|
|
}
|
|
|
|
// Assign the sprite
|
|
spriteRenderer.sprite = sprite;
|
|
|
|
// Create the prefab variant
|
|
GameObject prefabVariant = PrefabUtility.SaveAsPrefabAsset(prefabInstance, variantPath);
|
|
|
|
if (prefabVariant != null)
|
|
{
|
|
successCount++;
|
|
}
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Debug.LogError($"Error creating prefab variant: {e.Message}");
|
|
}
|
|
finally
|
|
{
|
|
// Clean up the instance
|
|
DestroyImmediate(prefabInstance);
|
|
}
|
|
}
|
|
|
|
AssetDatabase.Refresh();
|
|
EditorUtility.DisplayDialog("Prefab Variants Created", $"Successfully created {successCount} prefab variants.", "OK");
|
|
}
|
|
}
|
|
}
|