UnityEditor 編輯器間快速?gòu)?fù)制粘貼

經(jīng)常需要從Assets面板復(fù)制文件,每次都要 右鍵 - Show in Exlorer 霜瘪,然后復(fù)制珠插,巨麻煩。寫(xiě)了個(gè)工具颖对,直接右鍵Asset面板在Unity不同項(xiàng)目間快速?gòu)?fù)制粘貼捻撑,或者直接復(fù)制到剪切板。

主要分成兩種,一種是從一個(gè)編輯器到另外一個(gè)編輯器拷貝顾患,一種是想拷貝到系統(tǒng)剪切板番捂。

menu

從編輯器到剪切板

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,菜單欄安裝即可避矢,然后就可以全局通用了悼瘾。

menu2

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市审胸,隨后出現(xiàn)的幾起案子亥宿,更是在濱河造成了極大的恐慌,老刑警劉巖砂沛,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烫扼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡碍庵,警方通過(guò)查閱死者的電腦和手機(jī)映企,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)怎抛,“玉大人卑吭,你說(shuō)我怎么就攤上這事芽淡÷砭” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵挣菲,是天一觀的道長(zhǎng)富稻。 經(jīng)常有香客問(wèn)我,道長(zhǎng)白胀,這世上最難降的妖魔是什么椭赋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮或杠,結(jié)果婚禮上哪怔,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好认境,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布胚委。 她就那樣靜靜地躺著,像睡著了一般叉信。 火紅的嫁衣襯著肌膚如雪亩冬。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天硼身,我揣著相機(jī)與錄音硅急,去河邊找鬼。 笑死佳遂,一個(gè)胖子當(dāng)著我的面吹牛营袜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丑罪,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼连茧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了巍糯?” 一聲冷哼從身側(cè)響起啸驯,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎祟峦,沒(méi)想到半個(gè)月后罚斗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宅楞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年针姿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片厌衙。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡距淫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出婶希,到底是詐尸還是另有隱情榕暇,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布喻杈,位于F島的核電站彤枢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏筒饰。R本人自食惡果不足惜缴啡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瓷们。 院中可真熱鬧业栅,春花似錦秒咐、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至娘汞,卻和暖如春歹茶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背你弦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工堂飞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留馍惹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像桨武,于是被迫代替她去往敵國(guó)和親川背。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砚作,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容