Files
AppleHillsProduction/Assets/Editor/PrefabVariantGeneratorWindow.cs

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");
}
}
}