經(jīng)常需要從Assets面板復(fù)制文件,每次都要 右鍵 - Show in Exlorer 霜瘪,然后復(fù)制珠插,巨麻煩。寫(xiě)了個(gè)工具颖对,直接右鍵Asset面板在Unity不同項(xiàng)目間快速?gòu)?fù)制粘貼捻撑,或者直接復(fù)制到剪切板。
主要分成兩種,一種是從一個(gè)編輯器到另外一個(gè)編輯器拷貝顾患,一種是想拷貝到系統(tǒng)剪切板番捂。
從編輯器到剪切板
Unity編輯器里沒(méi)辦法像C# Winform 一樣直接向系統(tǒng)剪切板添加文件夾,只能復(fù)制文本江解,但是PowerShell可以设预,在UnityEditor里又可以執(zhí)行Powershell。所以通過(guò)執(zhí)行PowerShell來(lái)向系統(tǒng)剪切板加入要復(fù)制的文件列表犁河。powershell使用剪切板 見(jiàn) 官方文檔 絮缅。
執(zhí)行powershell
public static bool RunCommand(string command)
{
using (Process process = new Process())
{
process.StartInfo.FileName = "powershell";
process.StartInfo.Arguments = command;
process.StartInfo.CreateNoWindow = true; // 不顯示窗口
process.StartInfo.ErrorDialog = true;
process.StartInfo.UseShellExecute = false;
try
{
process.Start();
process.WaitForExit();
process.Close();
}
catch (Exception e)
{
Debug.LogError(e);
return false;
}
}
return true;
}
然后加入Assets菜單,獲取選中的目錄拼接一串命令運(yùn)行即可呼股。
[MenuItem("Assets/復(fù)制 - 剪切板復(fù)制", false, 21)]
private static void CopyToClipboard()
{
StringBuilder stringBuilder = new StringBuilder("Set-Clipboard -Path ");
for (int i = 0; i < Selection.assetGUIDs.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[i]);
if (i != 0) stringBuilder.Append(",");
stringBuilder.Append("\"");
stringBuilder.Append(AssetPath2FullPath(path));
stringBuilder.Append("\"");
}
RunCommand(stringBuilder.Replace("/", "\\").ToString());
}
注意事項(xiàng):Assets路徑需要轉(zhuǎn)換成絕對(duì)路徑耕魄。這樣粘貼時(shí)才有效。
從編輯器到編輯器
雖然可以通過(guò)powershell可以加入文件列表到剪切板彭谁, 但是沒(méi)發(fā)現(xiàn)通過(guò)powershell粘貼吸奴,這邊比較坑爹。不過(guò)可以通過(guò)代碼獲取到復(fù)制的文本:
GUIUtility.systemCopyBuffer
所以采取的辦法是缠局,如果在編輯器間復(fù)制则奥,把文件列表路徑,存起來(lái)序列化后復(fù)制到文本狭园,然后粘貼時(shí)讀取系統(tǒng)的剪切板 然后解析下路徑列表读处,再通過(guò)C#執(zhí)行復(fù)制粘貼擦操作。
此外唱矛,有時(shí)需要通過(guò)導(dǎo)出導(dǎo)入包來(lái)復(fù)制罚舱,以便識(shí)別到依賴。道理相同绎谦,在一邊導(dǎo)出管闷,把列表文本寫(xiě)到剪切板。
直接復(fù)制
[MenuItem("Assets/復(fù)制 - 編輯器復(fù)制", false, 21)]
private static void CopyToEditor()
{
ClipItem item = new ClipItem(ContentType.File);
foreach (var guiD in Selection.assetGUIDs)
{
string path = AssetDatabase.GUIDToAssetPath(guiD);
item.Values.Add(AssetPath2FullPath(path));
}
CopyClipboardItem(item);
Debug.Log("已復(fù)制" + Selection.assetGUIDs.Length + "條數(shù)據(jù)窃肠,可在其他 Unity 編輯器里粘貼包个!");
}
序列化并復(fù)制文本
public static void CopyClipboardItem(ClipItem item)
{
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
formatter.Serialize(stream, item);
TextEditor te = new TextEditor {text = Convert.ToBase64String(stream.ToArray())};
te.OnFocus();
te.Copy();
}
}
通過(guò)導(dǎo)包
和上面唯一的區(qū)別就是導(dǎo)出一個(gè)包到臨時(shí)目錄存起來(lái)
[MenuItem("Assets/復(fù)制 - 導(dǎo)出包復(fù)制", false, 21)]
private static void CopyAsPackage()
{
string[] assetPaths = new string[Selection.assetGUIDs.Length];
for (int i = 0; i < Selection.assetGUIDs.Length; i++)
{
assetPaths[i] = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[i]);
}
string outPath = Path.Combine(Application.temporaryCachePath,
Random.Range(0, 1024) + ".unitypackage");
AssetDatabase.ExportPackage(assetPaths, outPath,
ExportPackageOptions.Recurse | ExportPackageOptions.IncludeDependencies);
ClipItem item = new ClipItem(ContentType.Package);
item.Values.Add(outPath);
CopyClipboardItem(item);
}
粘貼
[MenuItem("Assets/粘貼", false, 21)]
private static void Paste()
{
ClipItem item;
try
{
byte[] bytes = Convert.FromBase64String(GUIUtility.systemCopyBuffer);
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream(bytes))
{
item = formatter.Deserialize(stream) as ClipItem;
}
}
catch (FormatException e)
{
throw new FormatException("沒(méi)有從剪切板解析到有效的數(shù)據(jù)!");
}
string assetPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
switch (item.Type)
{
case ContentType.File:
CopyListFileInEditor(item.Values, AssetPath2FullPath(assetPath));
break;
case ContentType.Package:
if (item.Values.Count > 0 && Path.GetExtension(item.Values[0]).Equals(".unitypackage"))
Package2Folder.ImportPackageToFolder(item.Values[0], assetPath, true);
break;
default:
break;
}
}
public static void CopyListFileInEditor(List<string> sourcePaths, string targetPath)
{
bool isAuto = EditorPrefs.GetBool(KeyAutoRefresh, true);
if (isAuto) EditorPrefs.SetBool(KeyAutoRefresh, false);
foreach (var path in sourcePaths)
{
string destName = Path.Combine(targetPath, Path.GetFileName(path));
if (File.Exists(path))
{
File.Copy(path, destName);
}
else
{
CopyDir(path, destName);
}
}
if (isAuto) EditorPrefs.SetBool(KeyAutoRefresh, true);
AssetDatabase.Refresh();
}
public static void CopyDir(string sourcePath, string destinationPath)
{
DirectoryInfo info = new DirectoryInfo(sourcePath);
if (!Directory.Exists(destinationPath))
Directory.CreateDirectory(destinationPath);
foreach (FileSystemInfo fsi in info.GetFileSystemInfos())
{
string destName = Path.Combine(destinationPath, fsi.Name);
if (fsi is FileInfo)
{
File.Copy(fsi.FullName, destName);
}
else
{
Directory.CreateDirectory(destName);
CopyDir(fsi.FullName, destName);
}
}
}
需要注意的是,在編輯器里直接通過(guò)腳本粘貼之前冤留,需要禁用掉Unity的自動(dòng)刷新碧囊,否則可能沒(méi)復(fù)制完線程便中斷了。Unity設(shè)置里的自動(dòng)刷新配置保存在 EditorPrefs纤怒,Key:kAutoRefresh糯而。
以上的代碼并不完整,另外還有一些細(xì)節(jié)肪跋,比如導(dǎo)入包時(shí) 可以導(dǎo)入指定Assets目錄等一些細(xì)節(jié)上的沒(méi)貼出來(lái)歧蒋。代碼在這里 QuickCopy.cs ,直接放到項(xiàng)目中Editor文件夾下就能用。
無(wú)關(guān)緊要的部分
從一個(gè)項(xiàng)目復(fù)制到另一個(gè)項(xiàng)目谜洽,都得需要上面的代碼萝映,意味著每次都得先拷貝下這個(gè)代碼才行,也相當(dāng)麻煩阐虚⌒虮郏可以做成模塊化,這樣自己電腦上隨便打開(kāi)一個(gè)項(xiàng)目不用復(fù)制代碼也能用了实束。參見(jiàn)之前的博客:UnityEditor Unity的模塊
為了方便自己奥秆,打包了一份。在這里 UnityQuickCopyModule.dll 咸灿,把該文件放到任意Unity項(xiàng)目构订,用管理員權(quán)限啟動(dòng)Unity,菜單欄安裝即可避矢,然后就可以全局通用了悼瘾。
以上所有腳本和文件 https://github.com/liangddyy/UnityClipboard