Add a sprite variant generator and create monster prefabs for heads

This commit is contained in:
2025-09-19 13:14:47 +02:00
parent d110616907
commit 40c3867d29
9 changed files with 902 additions and 162 deletions

View File

@@ -3,34 +3,74 @@ using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor.Experimental.SceneManagement;
using System.Text;
namespace Editor
{
public class PrefabVariantGeneratorWindow : EditorWindow
{
// Renderer configuration class to track sprite renderers and their assigned sprites
[System.Serializable]
private class RendererConfig
{
public string Path; // Hierarchy path to the renderer
public string Name; // Display name for the renderer
public SpriteRenderer Renderer; // Reference to the actual renderer
public Sprite CurrentSprite; // Current sprite in the renderer
public List<Sprite> AssignedSprites = new List<Sprite>(); // Sprites to use for variants
public bool Enabled = true; // Whether to include in variant generation
public bool Expanded = true; // UI expanded state
public Vector2 ScrollPosition; // Scroll position for sprite list
}
// Main fields
private GameObject sourcePrefab;
private GameObject previousSourcePrefab;
private List<Sprite> selectedSprites = new List<Sprite>();
private Vector2 scrollPosition;
private List<RendererConfig> detectedRenderers = new List<RendererConfig>();
private Vector2 mainScrollPosition;
private string variantSaveFolder = "Assets/Prefabs/Variants";
private string namingPattern = "{0}_{1}"; // {0} = prefab name, {1} = sprite name
private string namingPattern = "{0}_{1}"; // Default: {0} = prefab name, {1} = first renderer sprite
private bool userChangedSavePath = false;
private int estimatedVariantCount = 0;
private int maxSafeVariantCount = 100; // Warn above this number
private GUIStyle boldFoldoutStyle;
private bool showDefaultHelp = true;
[MenuItem("Tools/Prefab Variant Generator")]
// Editor window setup
[MenuItem("Tools/Sprite Variant Generator")]
public static void ShowWindow()
{
var window = GetWindow<PrefabVariantGeneratorWindow>("Prefab Variant Generator");
window.minSize = new Vector2(400, 500);
window.minSize = new Vector2(500, 600);
}
private void OnEnable()
{
// Initialize styles on enable to avoid null reference issues
boldFoldoutStyle = new GUIStyle(EditorStyles.foldout)
{
fontStyle = FontStyle.Bold
};
}
private void OnGUI()
{
// Initialize styles if needed
if (boldFoldoutStyle == null)
{
boldFoldoutStyle = new GUIStyle(EditorStyles.foldout)
{
fontStyle = FontStyle.Bold
};
}
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();
EditorGUILayout.HelpBox("Create multiple prefab variants with different sprites assigned to renderers.", MessageType.Info);
mainScrollPosition = EditorGUILayout.BeginScrollView(mainScrollPosition);
// Source Prefab Selection
EditorGUILayout.Space();
EditorGUILayout.LabelField("Step 1: Select Source Prefab", EditorStyles.boldLabel);
// Store previous selection to detect changes
@@ -51,6 +91,15 @@ namespace Editor
variantSaveFolder = Path.GetDirectoryName(prefabPath).Replace("\\", "/");
}
}
// Find sprite renderers in the prefab
FindRenderersInPrefab();
// Clear default help once a prefab is selected
if (sourcePrefab != null)
{
showDefaultHelp = false;
}
}
// Warn if not a prefab
@@ -59,16 +108,244 @@ namespace Editor
EditorGUILayout.HelpBox("Please select a prefab asset.", MessageType.Warning);
}
// Sprite Selection
EditorGUILayout.Space();
EditorGUILayout.LabelField("Step 2: Select Sprites", EditorStyles.boldLabel);
// Display default help if no prefab selected
if (showDefaultHelp && sourcePrefab == null)
{
EditorGUILayout.HelpBox(
"This tool lets you create prefab variants with different sprites.\n\n" +
"1. Select a source prefab\n" +
"2. Assign sprites to each detected sprite renderer\n" +
"3. Generate all combinations as prefab variants",
MessageType.Info
);
}
// Only show the rest if a valid prefab is selected
if (sourcePrefab != null)
{
// Renderer sections
EditorGUILayout.Space();
EditorGUILayout.LabelField("Step 2: Configure Sprite Renderers", EditorStyles.boldLabel);
if (detectedRenderers.Count == 0)
{
EditorGUILayout.HelpBox("No sprite renderers found in prefab. A new renderer will be created.", MessageType.Info);
}
else
{
EditorGUILayout.HelpBox($"{detectedRenderers.Count} sprite renderer{(detectedRenderers.Count > 1 ? "s" : "")} found in prefab.", MessageType.Info);
}
// Display each renderer configuration
for (int i = 0; i < detectedRenderers.Count; i++)
{
DrawRendererSection(detectedRenderers[i], i);
}
// Update estimated variant count
UpdateVariantCount();
// 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();
// 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;
}
}
}
// Naming pattern field
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Naming Pattern");
namingPattern = EditorGUILayout.TextField(namingPattern);
EditorGUILayout.EndHorizontal();
// Help text for naming pattern
StringBuilder helpText = new StringBuilder("Naming placeholders:\n");
helpText.AppendLine("{0} = Prefab name");
for (int i = 0; i < detectedRenderers.Count; i++)
{
helpText.AppendLine($"{{{i+1}}} = {detectedRenderers[i].Name} sprite name");
}
EditorGUILayout.HelpBox(helpText.ToString(), MessageType.Info);
// Variant count display
string variantCountText = $"Will generate {estimatedVariantCount} variant{(estimatedVariantCount != 1 ? "s" : "")}";
if (estimatedVariantCount > maxSafeVariantCount)
{
EditorGUILayout.HelpBox($"Warning: {variantCountText}. This might take some time.", MessageType.Warning);
}
else if (estimatedVariantCount > 0)
{
EditorGUILayout.HelpBox(variantCountText, MessageType.Info);
}
else
{
EditorGUILayout.HelpBox("Please assign at least one sprite to each enabled renderer to generate variants.", MessageType.Warning);
}
// Generate button
EditorGUILayout.Space();
GUI.enabled = estimatedVariantCount > 0;
if (GUILayout.Button("Generate Prefab Variants", GUILayout.Height(30)))
{
// Show warning for large numbers of variants
if (estimatedVariantCount > maxSafeVariantCount)
{
bool proceed = EditorUtility.DisplayDialog(
"Generate Many Variants?",
$"You are about to generate {estimatedVariantCount} prefab variants. This might take some time and use significant disk space. Continue?",
"Generate",
"Cancel"
);
if (!proceed) return;
}
GeneratePrefabVariants();
}
GUI.enabled = true;
}
EditorGUILayout.EndScrollView();
}
private void DrawRendererSection(RendererConfig config, int index)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal();
// Expand/collapse button
config.Expanded = EditorGUILayout.Foldout(config.Expanded, "", boldFoldoutStyle);
// Drag and drop area for sprites
EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Height(80));
EditorGUILayout.LabelField("Drag and drop sprites here", EditorStyles.centeredGreyMiniLabel);
// Enable/disable toggle
bool newEnabled = EditorGUILayout.Toggle(config.Enabled, GUILayout.Width(20));
if (newEnabled != config.Enabled)
{
config.Enabled = newEnabled;
UpdateVariantCount();
}
Rect dropArea = GUILayoutUtility.GetRect(0, 50, GUILayout.ExpandWidth(true));
// Renderer name/title
EditorGUILayout.LabelField(config.Name, EditorStyles.boldLabel);
// Current sprite preview if available
if (config.CurrentSprite != null)
{
GUILayout.Box(
AssetPreview.GetAssetPreview(config.CurrentSprite),
GUILayout.Width(40),
GUILayout.Height(40)
);
}
EditorGUILayout.EndHorizontal();
// Only show contents if expanded
if (config.Expanded)
{
// Path display
if (!string.IsNullOrEmpty(config.Path))
{
EditorGUILayout.LabelField($"Path: {config.Path}", EditorStyles.miniLabel);
}
EditorGUI.BeginDisabledGroup(!config.Enabled);
// Sprite selection controls
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
// Drag and drop area for sprites
EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Height(60));
EditorGUILayout.LabelField("Drag and drop sprites here", EditorStyles.centeredGreyMiniLabel);
Rect dropArea = GUILayoutUtility.GetRect(0, 40, GUILayout.ExpandWidth(true));
HandleDragAndDrop(dropArea, config);
EditorGUILayout.EndVertical();
if (GUILayout.Button("Add Selected", GUILayout.Width(100), GUILayout.Height(60)))
{
AddSelectedSpritesToConfig(config);
}
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("Clear Sprites"))
{
config.AssignedSprites.Clear();
UpdateVariantCount();
}
// Display selected sprites
EditorGUILayout.Space();
EditorGUILayout.LabelField($"Selected Sprites ({config.AssignedSprites.Count}):", EditorStyles.miniBoldLabel);
// Sprite list
config.ScrollPosition = EditorGUILayout.BeginScrollView(config.ScrollPosition, GUILayout.Height(120));
for (int i = config.AssignedSprites.Count - 1; i >= 0; i--)
{
EditorGUILayout.BeginHorizontal();
config.AssignedSprites[i] = (Sprite)EditorGUILayout.ObjectField(
config.AssignedSprites[i],
typeof(Sprite),
false,
GUILayout.ExpandWidth(true)
);
// Preview sprite
if (config.AssignedSprites[i] != null)
{
GUILayout.Box(
AssetPreview.GetAssetPreview(config.AssignedSprites[i]),
GUILayout.Width(40),
GUILayout.Height(40)
);
}
if (GUILayout.Button("Remove", GUILayout.Width(60)))
{
config.AssignedSprites.RemoveAt(i);
UpdateVariantCount();
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.EndVertical();
}
private void HandleDragAndDrop(Rect dropArea, RendererConfig config)
{
Event evt = Event.current;
switch (evt.type)
{
@@ -82,13 +359,17 @@ namespace Editor
if (evt.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
bool added = false;
foreach (var draggedObject in DragAndDrop.objectReferences)
{
if (draggedObject is Sprite sprite)
{
if (!selectedSprites.Contains(sprite))
selectedSprites.Add(sprite);
if (!config.AssignedSprites.Contains(sprite))
{
config.AssignedSprites.Add(sprite);
added = true;
}
}
else if (draggedObject is Texture2D texture)
{
@@ -100,125 +381,200 @@ namespace Editor
foreach (var s in sprites)
{
if (!selectedSprites.Contains(s))
selectedSprites.Add(s);
if (!config.AssignedSprites.Contains(s))
{
config.AssignedSprites.Add(s);
added = true;
}
}
}
}
if (added)
{
UpdateVariantCount();
}
evt.Use();
}
break;
}
EditorGUILayout.EndVertical();
}
if (GUILayout.Button("Add Selected Sprites", GUILayout.Width(120), GUILayout.Height(80)))
private void AddSelectedSpritesToConfig(RendererConfig config)
{
var selectedObjects = Selection.objects;
bool added = false;
foreach (var obj in selectedObjects)
{
var selectedObjects = Selection.objects;
foreach (var obj in selectedObjects)
if (obj is Sprite sprite)
{
if (obj is Sprite sprite)
if (!config.AssignedSprites.Contains(sprite))
{
if (!selectedSprites.Contains(sprite))
selectedSprites.Add(sprite);
config.AssignedSprites.Add(sprite);
added = true;
}
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();
}
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)
foreach (var s in sprites)
{
if (!config.AssignedSprites.Contains(s))
{
if (!selectedSprites.Contains(s))
selectedSprites.Add(s);
config.AssignedSprites.Add(s);
added = true;
}
}
}
}
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("Clear Sprites"))
if (added)
{
selectedSprites.Clear();
UpdateVariantCount();
}
}
// Display selected sprites
EditorGUILayout.Space();
EditorGUILayout.LabelField($"Selected Sprites ({selectedSprites.Count}):", EditorStyles.boldLabel);
private void FindRenderersInPrefab()
{
detectedRenderers.Clear();
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Height(150));
for (int i = selectedSprites.Count - 1; i >= 0; i--)
if (sourcePrefab == null) return;
// Get all renderers in prefab (including children)
GameObject instance = null;
try
{
EditorGUILayout.BeginHorizontal();
selectedSprites[i] = (Sprite)EditorGUILayout.ObjectField(selectedSprites[i], typeof(Sprite), false);
instance = (GameObject)PrefabUtility.InstantiatePrefab(sourcePrefab);
SpriteRenderer[] renderers = instance.GetComponentsInChildren<SpriteRenderer>(true);
// Preview sprite
if (selectedSprites[i] != null)
for (int i = 0; i < renderers.Length; i++)
{
GUILayout.Box(selectedSprites[i].texture, GUILayout.Width(40), GUILayout.Height(40));
}
var renderer = renderers[i];
string path = GetRelativePath(instance.transform, renderer.transform);
string name = renderer.gameObject.name;
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"))
// For root object, use "Main"
if (string.IsNullOrEmpty(path))
{
variantSaveFolder = Path.GetDirectoryName(prefabPath).Replace("\\", "/");
userChangedSavePath = false;
name = "Main";
}
// For objects with the same name, add index
else if (renderers.Count(r => r.gameObject.name == renderer.gameObject.name) > 1)
{
name = $"{name} ({i+1})";
}
detectedRenderers.Add(new RendererConfig
{
Path = path,
Name = name,
Renderer = renderer,
CurrentSprite = renderer.sprite,
AssignedSprites = renderer.sprite != null ?
new List<Sprite> { renderer.sprite } :
new List<Sprite>()
});
}
}
finally
{
if (instance != null)
DestroyImmediate(instance);
}
// If no renderers found, create a default entry
if (detectedRenderers.Count == 0)
{
detectedRenderers.Add(new RendererConfig
{
Path = "",
Name = "Main",
Renderer = null,
CurrentSprite = null,
AssignedSprites = new List<Sprite>()
});
}
UpdateVariantCount();
}
private string GetRelativePath(Transform root, Transform target)
{
if (target == root) return "";
string path = target.name;
Transform parent = target.parent;
while (parent != null && parent != root)
{
path = parent.name + "/" + path;
parent = parent.parent;
}
return path;
}
private void UpdateVariantCount()
{
// Calculate estimated variants
estimatedVariantCount = 0;
// Get only enabled renderers with at least one sprite
var enabledConfigs = detectedRenderers
.Where(r => r.Enabled && r.AssignedSprites.Count > 0)
.ToList();
if (enabledConfigs.Count > 0)
{
// Start with count of first renderer's sprites
estimatedVariantCount = enabledConfigs[0].AssignedSprites.Count;
// Multiply by subsequent renderers' sprite counts
for (int i = 1; i < enabledConfigs.Count; i++)
{
estimatedVariantCount *= enabledConfigs[i].AssignedSprites.Count;
}
}
}
private List<List<Sprite>> GenerateAllCombinations()
{
var enabledConfigs = detectedRenderers
.Where(r => r.Enabled && r.AssignedSprites.Count > 0)
.ToList();
if (enabledConfigs.Count == 0)
return new List<List<Sprite>>();
// Initialize with first renderer's sprites
var combinations = enabledConfigs[0].AssignedSprites
.Select(s => new List<Sprite> { s })
.ToList();
// Add each subsequent renderer's sprites
for (int i = 1; i < enabledConfigs.Count; i++)
{
var newCombinations = new List<List<Sprite>>();
foreach (var combo in combinations)
{
foreach (var sprite in enabledConfigs[i].AssignedSprites)
{
var newCombo = new List<Sprite>(combo) { sprite };
newCombinations.Add(newCombo);
}
}
combinations = newCombinations;
}
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;
return combinations;
}
private void GeneratePrefabVariants()
@@ -229,21 +585,180 @@ namespace Editor
return;
}
if (selectedSprites.Count == 0)
// Get enabled renderer configurations
var enabledConfigs = detectedRenderers
.Where(r => r.Enabled && r.AssignedSprites.Count > 0)
.ToList();
if (enabledConfigs.Count == 0)
{
EditorUtility.DisplayDialog("Error", "Please select at least one sprite.", "OK");
EditorUtility.DisplayDialog("Error", "Please assign at least one sprite to a renderer.", "OK");
return;
}
// Ensure the save folder exists
if (!AssetDatabase.IsValidFolder(variantSaveFolder))
{
string[] folderPath = variantSaveFolder.Split('/');
string currentPath = folderPath[0];
EnsureFolderExists(variantSaveFolder);
for (int i = 1; i < folderPath.Length; i++)
// Generate all sprite combinations
var combinations = GenerateAllCombinations();
string sourcePrefabPath = AssetDatabase.GetAssetPath(sourcePrefab);
string prefabName = Path.GetFileNameWithoutExtension(sourcePrefabPath);
int successCount = 0;
// Show progress bar
EditorUtility.DisplayProgressBar("Generating Prefab Variants", "Preparing...", 0f);
try
{
// For each combination, create a prefab variant
for (int i = 0; i < combinations.Count; i++)
{
string folderName = folderPath[i];
// Update progress
if (i % 5 == 0 || i == combinations.Count - 1)
{
float progress = (float)i / combinations.Count;
if (EditorUtility.DisplayCancelableProgressBar(
"Generating Prefab Variants",
$"Creating variant {i+1} of {combinations.Count}",
progress))
{
// User canceled
break;
}
}
var combination = combinations[i];
// Generate variant name
string variantName = prefabName;
string[] spriteNames = new string[combination.Count];
for (int j = 0; j < combination.Count; j++)
{
spriteNames[j] = combination[j].name;
}
// Format with the naming pattern
object[] formatArgs = new object[spriteNames.Length + 1];
formatArgs[0] = prefabName;
for (int j = 0; j < spriteNames.Length; j++)
{
formatArgs[j + 1] = spriteNames[j];
}
try
{
variantName = string.Format(namingPattern, formatArgs);
}
catch (System.FormatException)
{
// Fallback if format fails
variantName = $"{prefabName}_{string.Join("_", spriteNames)}";
}
variantName = PrefabEditorUtility.SanitizeFileName(variantName);
string variantPath = Path.Combine(variantSaveFolder, variantName + ".prefab").Replace("\\", "/");
// Create the prefab variant
GameObject prefabInstance = (GameObject)PrefabUtility.InstantiatePrefab(sourcePrefab);
try
{
// Apply sprites to renderers
for (int j = 0; j < enabledConfigs.Count; j++)
{
SpriteRenderer renderer = null;
// Find the corresponding renderer in the instance
if (string.IsNullOrEmpty(enabledConfigs[j].Path))
{
// Root object
renderer = prefabInstance.GetComponent<SpriteRenderer>();
if (renderer == null)
{
renderer = prefabInstance.AddComponent<SpriteRenderer>();
}
}
else
{
// Child object
Transform child = prefabInstance.transform.Find(enabledConfigs[j].Path);
if (child != null)
{
renderer = child.GetComponent<SpriteRenderer>();
}
}
// Apply sprite if renderer was found
if (renderer != null)
{
renderer.sprite = combination[j];
}
}
// 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);
}
}
}
finally
{
EditorUtility.ClearProgressBar();
}
AssetDatabase.Refresh();
if (successCount > 0)
{
EditorUtility.DisplayDialog(
"Prefab Variants Created",
$"Successfully created {successCount} prefab variants in {variantSaveFolder}.",
"OK"
);
// Open the folder in Project view
var folderObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(variantSaveFolder);
if (folderObject != null)
{
Selection.activeObject = folderObject;
EditorGUIUtility.PingObject(folderObject);
}
}
else
{
EditorUtility.DisplayDialog(
"Prefab Variants",
"No prefab variants were created. Please check the console for errors.",
"OK"
);
}
}
private void EnsureFolderExists(string folderPath)
{
if (!AssetDatabase.IsValidFolder(folderPath))
{
string[] folderParts = folderPath.Split('/');
string currentPath = folderParts[0];
for (int i = 1; i < folderParts.Length; i++)
{
string folderName = folderParts[i];
string newPath = Path.Combine(currentPath, folderName);
if (!AssetDatabase.IsValidFolder(newPath))
@@ -256,57 +771,6 @@ namespace Editor
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");
}
}
}

View File

@@ -0,0 +1,59 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &7921038058497292108
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 2015720985618639356, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_Name
value: QuarryMonster_monster_head1_1
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.x
value: 2.46
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.y
value: -1.79
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 7754d064d743d1b419acb859c2db6121, type: 3}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: cd5c27c38c427524988b8932e7205d81
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &7839076539202245770
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 2015720985618639356, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_Name
value: QuarryMonster_monster_head2_0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.x
value: 2.46
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.y
value: -1.79
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8447572436637192077, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_Sprite
value:
objectReference: {fileID: -7812277834941893986, guid: e9ac6c3e349f9b247a0ea03665da57ce, type: 3}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 7754d064d743d1b419acb859c2db6121, type: 3}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7c45910be7187144b8d2af12ff772353
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &4541086625678702007
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 2015720985618639356, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_Name
value: QuarryMonster_monster_head3_0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.x
value: 2.46
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.y
value: -1.79
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8447572436637192077, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_Sprite
value:
objectReference: {fileID: 4139348639081821586, guid: 1043ceca06eb2ac48b22c20d281278b0, type: 3}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 7754d064d743d1b419acb859c2db6121, type: 3}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c0d4b6d8a3b9a2447b883a78c568826c
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &1190377006705176201
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 2015720985618639356, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_Name
value: QuarryMonster_monster_head4_0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.x
value: 2.46
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.y
value: -1.79
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6779310478082390115, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8447572436637192077, guid: 7754d064d743d1b419acb859c2db6121, type: 3}
propertyPath: m_Sprite
value:
objectReference: {fileID: -3471612407960728276, guid: c85ce41979ed896429c62330c546d3ce, type: 3}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 7754d064d743d1b419acb859c2db6121, type: 3}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5d23561cb618e86409a2b7ccbf08e37d
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: