經(jīng)常需要從Assets面板復(fù)制文件,每次都要 右鍵 - Show in Exlorer ,然后復(fù)制,巨麻煩。寫了個工具,直接右鍵Asset面板在Unity不同項目間快速復(fù)制粘貼,或者直接復(fù)制到剪切板。
主要分成兩種,一種是從一個編輯器到另外一個編輯器拷貝,一種是想拷貝到系統(tǒng)剪切板。

從編輯器到剪切板
Unity編輯器里沒辦法像C# Winform 一樣直接向系統(tǒng)剪切板添加文件夾,只能復(fù)制文本,但是PowerShell可以,在UnityEditor里又可以執(zhí)行Powershell。所以通過執(zhí)行PowerShell來向系統(tǒng)剪切板加入要復(fù)制的文件列表。powershell使用剪切板 見 官方文檔 。
執(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菜單,獲取選中的目錄拼接一串命令運行即可。
[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());
}
注意事項:Assets路徑需要轉(zhuǎn)換成絕對路徑。這樣粘貼時才有效。
從編輯器到編輯器
雖然可以通過powershell可以加入文件列表到剪切板, 但是沒發(fā)現(xiàn)通過powershell粘貼,這邊比較坑爹。不過可以通過代碼獲取到復(fù)制的文本:
GUIUtility.systemCopyBuffer
所以采取的辦法是,如果在編輯器間復(fù)制,把文件列表路徑,存起來序列化后復(fù)制到文本,然后粘貼時讀取系統(tǒng)的剪切板 然后解析下路徑列表,再通過C#執(zhí)行復(fù)制粘貼擦操作。
此外,有時需要通過導(dǎo)出導(dǎo)入包來復(fù)制,以便識別到依賴。道理相同,在一邊導(dǎo)出,把列表文本寫到剪切板。
直接復(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();
}
}
通過導(dǎo)包
和上面唯一的區(qū)別就是導(dǎo)出一個包到臨時目錄存起來
[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("沒有從剪切板解析到有效的數(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);
}
}
}
需要注意的是,在編輯器里直接通過腳本粘貼之前,需要禁用掉Unity的自動刷新,否則可能沒復(fù)制完線程便中斷了。Unity設(shè)置里的自動刷新配置保存在 EditorPrefs,Key:kAutoRefresh。
以上的代碼并不完整,另外還有一些細節(jié),比如導(dǎo)入包時 可以導(dǎo)入指定Assets目錄等一些細節(jié)上的沒貼出來。代碼在這里 QuickCopy.cs ,直接放到項目中Editor文件夾下就能用。
無關(guān)緊要的部分
從一個項目復(fù)制到另一個項目,都得需要上面的代碼,意味著每次都得先拷貝下這個代碼才行,也相當麻煩??梢宰龀赡K化,這樣自己電腦上隨便打開一個項目不用復(fù)制代碼也能用了。參見之前的博客:UnityEditor Unity的模塊
為了方便自己,打包了一份。在這里 UnityQuickCopyModule.dll ,把該文件放到任意Unity項目,用管理員權(quán)限啟動Unity,菜單欄安裝即可,然后就可以全局通用了。

以上所有腳本和文件 https://github.com/liangddyy/UnityClipboard