UnityEditor 编辑器间快速复制粘贴

经常需要从Assets面板复制文件,每次都要 右键 - Show in Exlorer ,然后复制,巨麻烦。写了个工具,直接右键Asset面板在Unity不同项目间快速复制粘贴,或者直接复制到剪切板。

主要分成两种,一种是从一个编辑器到另外一个编辑器拷贝,一种是想拷贝到系统剪切板。

menu

从编辑器到剪切板

Unity编辑器里没办法像C# Winform 一样直接向系统剪切板添加文件夹,只能复制文本,但是PowerShell可以,在UnityEditor里又可以执行Powershell。所以通过执行PowerShell来向系统剪切板加入要复制的文件列表。powershell使用剪切板 见 官方文档

执行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/复制 - 剪切板复制", 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路径需要转换成绝对路径。这样粘贴时才有效。

从编辑器到编辑器

虽然可以通过powershell可以加入文件列表到剪切板, 但是没发现通过powershell粘贴,这边比较坑爹。不过可以通过代码获取到复制的文本:

GUIUtility.systemCopyBuffer

所以采取的办法是,如果在编辑器间复制,把文件列表路径,存起来序列化后复制到文本,然后粘贴时读取系统的剪切板 然后解析下路径列表,再通过C#执行复制粘贴擦操作。

此外,有时需要通过导出导入包来复制,以便识别到依赖。道理相同,在一边导出,把列表文本写到剪切板。

直接复制

[MenuItem("Assets/复制 - 编辑器复制", 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("已复制" + Selection.assetGUIDs.Length + "条数据,可在其他 Unity 编辑器里粘贴!");
}

序列化并复制文本

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

通过导包

和上面唯一的区别就是导出一个包到临时目录存起来

[MenuItem("Assets/复制 - 导出包复制", 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("没有从剪切板解析到有效的数据!");
    }
    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的自动刷新,否则可能没复制完线程便中断了。Unity设置里的自动刷新配置保存在 EditorPrefs,Key:kAutoRefresh。


以上的代码并不完整,另外还有一些细节,比如导入包时 可以导入指定Assets目录等一些细节上的没贴出来。代码在这里 QuickCopy.cs ,直接放到项目中Editor文件夹下就能用。

无关紧要的部分

从一个项目复制到另一个项目,都得需要上面的代码,意味着每次都得先拷贝下这个代码才行,也相当麻烦。可以做成模块化,这样自己电脑上随便打开一个项目不用复制代码也能用了。参见之前的博客:UnityEditor Unity的模块

为了方便自己,打包了一份。在这里 UnityQuickCopyModule.dll ,把该文件放到任意Unity项目,用管理员权限启动Unity,菜单栏安装即可,然后就可以全局通用了。

menu2

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

博客原文 https://539go.com/2018/02/04/Unity-QuickCopy/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容