Skip to content

Instantly share code, notes, and snippets.

@shane-harper
Created November 2, 2018 15:42
Show Gist options
  • Save shane-harper/d060b1c6e2581fcd58a5932bcb537a88 to your computer and use it in GitHub Desktop.
Save shane-harper/d060b1c6e2581fcd58a5932bcb537a88 to your computer and use it in GitHub Desktop.
A handy tool for copying Unity meta data from one asset to another (Place in Editor folder)
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
/// <summary>
/// Tool for copying Unity meta data directly from one file to another
/// </summary>
public class MetaCopier : EditorWindow
{
private static Vector2 _scroll = Vector2.zero;
private static bool _showProperties = true;
private static GUIStyle _dragAreaStyle;
/// <summary>
/// List of properties to copy, editable in the UI, will reset to these values on recompile though
/// </summary>
private static readonly List<string> Properties = new List<string>
{
"alignment:",
"spritePivot:",
"spritePixelsToUnits:",
"spriteBorder:"
};
private static readonly List<Object> SourceFiles = new List<Object>(1);
private static readonly List<Object> TargetFiles = new List<Object>(1);
[MenuItem("Window/Meta Copier")]
public static void CreateWindow()
{
var window = GetWindow<MetaCopier>("Meta Copier", true);
window.Show();
}
private void OnGUI()
{
// Make sure the drag area style skin isn't null
if (_dragAreaStyle == null)
{
var target = EditorStyles.helpBox;
_dragAreaStyle = new GUIStyle(GUI.skin.button)
{
normal = {background = target.normal.background},
border = target.border,
richText = true
};
}
// Scroll view
_scroll = EditorGUILayout.BeginScrollView(_scroll, false, true,
GUIStyle.none, GUI.skin.verticalScrollbar, GUIStyle.none);
var columnWidth = GUILayout.Width((Screen.width-13)/2f);
EditorGUILayout.BeginHorizontal();
// List source files
EditorGUILayout.BeginVertical(columnWidth);
DropAreaGUI("Source Files", "Drag <b>Source</b> Files Here", SourceFiles, 50);
DrawList(SourceFiles);
EditorGUILayout.EndVertical();
// List target files
EditorGUILayout.BeginVertical(columnWidth);
DropAreaGUI("Target Files", "Drag <b>Target</b> Files Here", TargetFiles, 50);
DrawList(TargetFiles);
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
// Fill out space and end scroll
GUILayout.FlexibleSpace();
EditorGUILayout.EndScrollView();
// List properties
var propertiesLabel = string.Format("Properties ({0})", Properties.Count);
_showProperties = EditorGUILayout.Foldout(_showProperties, propertiesLabel, true);
if (_showProperties)
{
DrawList(Properties);
EditorGUILayout.Space();
}
// Disable go button if criteria are not met
var enabled = GUI.enabled;
GUI.enabled = SourceFiles.Count > 0 && SourceFiles.Count == TargetFiles.Count && Properties.Count > 0;
if (GUILayout.Button("Go", GUILayout.Height(40)))
CopyProperties(SourceFiles, TargetFiles, Properties);
GUI.enabled = enabled;
EditorGUILayout.Space();
}
/// <summary>
/// Copy properties from source meta files to target meta files
/// </summary>
/// <param name="source">List containing source assets</param>
/// <param name="target">List containing target assets</param>
/// <param name="properties">List of properties to copy</param>
public static void CopyProperties(IList<Object> source, IList<Object> target, IList<string> properties)
{
// Get project path
var dataPath = Application.dataPath;
var projectPath = dataPath.Substring(0, dataPath.LastIndexOf('/') + 1);
// Copy files
var builder = new StringBuilder();
var count = Mathf.Min(source.Count, target.Count);
for (var i = 0; i < count; ++i)
{
// Check each file exists
var sourceFile = source[i];
if (sourceFile == null) continue;
var targetFile = target[i];
if (targetFile == null) continue;
// Get file paths
builder.AppendFormat("{0}{1}.meta", projectPath, AssetDatabase.GetAssetPath(sourceFile));
var sourcePath = builder.ToString();
builder.Length = 0;
builder.AppendFormat("{0}{1}.meta", projectPath, AssetDatabase.GetAssetPath(targetFile));
var targetPath = builder.ToString();
builder.Length = 0;
// Copy!
CopyProperties(sourcePath, targetPath, properties);
}
}
/// <summary>
/// Copy properties from source meta file to target meta file
/// </summary>
/// <param name="source">Path to source asset meta file</param>
/// <param name="target">Path to target asset meta file</param>
/// <param name="properties">List of properties to copy</param>
public static void CopyProperties(string source, string target, IList<string> properties)
{
// If either file doesn't exist, skip it
if (!File.Exists(source) || !File.Exists(target)) return;
// Create array to contain values
int lineCount;
var values = GetValues(source, properties, out lineCount);
// Read existing target file and load into array
var targetLines = new List<string>(lineCount);
using (var reader = new StreamReader(target))
{
var line = reader.ReadLine();
do
{
var found = false;
for (var i = 0; i < properties.Count; ++i)
{
if (!line.StartsWith(properties[i])) continue;
if (string.IsNullOrEmpty(values[i]))
{
Debug.LogErrorFormat("No value for {0} to copy", properties[i]);
continue;
}
targetLines.Add(values[i]);
found = true;
}
if (!found) targetLines.Add(line);
line = reader.ReadLine();
} while (line != null);
reader.Close();
}
// Write new file from array
using (var writer = new StreamWriter(target, false))
{
foreach (var line in targetLines)
writer.WriteLine(line);
writer.Close();
}
}
/// <summary>
/// Read values from source file
/// </summary>
private static string[] GetValues(string source, IList<string> properties, out int lineCount)
{
var values = new string[properties.Count];
lineCount = 0;
using (var reader = new StreamReader(source))
{
var line = reader.ReadLine();
do
{
++lineCount;
for (var i = 0; i < properties.Count; ++i)
{
if (!line.StartsWith(properties[i])) continue;
values[i] = line;
break;
}
line = reader.ReadLine();
} while (line != null);
reader.Close();
}
return values;
}
/// <summary>
/// Display a list of strings
/// </summary>
private static void DrawList(IList<string> list)
{
for (var i = 0; i < list.Count; ++i)
{
EditorGUILayout.BeginHorizontal();
list[i] = EditorGUILayout.TextField(list[i]);
if (GUILayout.Button("Remove", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
list.RemoveAt(i);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Clear", EditorStyles.miniButtonLeft, GUILayout.ExpandWidth(false)))
list.Clear();
if (GUILayout.Button("+", EditorStyles.miniButtonRight))
list.Add("");
EditorGUILayout.EndHorizontal();
}
/// <summary>
/// Display an object list
/// </summary>
private static void DrawList<T>(IList<T> list) where T : Object
{
for (var i = 0; i < list.Count; ++i)
{
list[i] = (T) EditorGUILayout.ObjectField(list[i], typeof(T), true);
if (list[i] != null) continue;
list.RemoveAt(i);
--i;
}
}
/// <summary>
/// Draw a drag and drop area for an Object list
/// </summary>
private static void DropAreaGUI(string title, string message, ICollection<Object> target, float height)
{
if (!string.IsNullOrEmpty(title))
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
EditorGUILayout.LabelField(title, EditorStyles.boldLabel);
if (GUILayout.Button("Clear", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
target.Clear();
EditorGUILayout.EndHorizontal();
}
var currentEvent = Event.current;
var dropArea = GUILayoutUtility.GetRect(0.0f, height, GUILayout.ExpandWidth(true));
GUI.Box(dropArea, message, _dragAreaStyle);
switch (currentEvent.type)
{
case EventType.DragUpdated:
case EventType.DragPerform:
if (!dropArea.Contains(currentEvent.mousePosition)) return;
DragAndDrop.visualMode = DragAndDropVisualMode.Link;
if (currentEvent.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
foreach (var item in DragAndDrop.objectReferences)
target.Add(item);
}
break;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment