有时候需要复制一个场景目录制作新的场景,打包场景也是独立资源,不希望资源复用。我们直接使用Ctrl+D复制资源,里面的预设,材质等都还是指向原有的,所以废话不多说,直接上代码。
操作窗口
首先是制作一个复制资源窗口
下面是脚本和菜单调用
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class ResCopyWin : EditorWindow
{
public static ResCopyWin copyWin;
//[MenuItem("JFrameWork/Resources/复制文件夹(复制依赖关系) %#D", false, 0)] Ctrl+Shift+D
[MenuItem("JFrameWork/Resources/复制文件夹(复制依赖关系)", false, 0)]
public static void CopyIt()
{
//否则就弹出面板设置
copyWin = EditorWindow.GetWindow(typeof(ResCopyWin), true, "复制资源目录") as ResCopyWin;
copyWin.ShowModalUtility();
}
string sourcePath = "Assets/";
string toPath = "Assets/";
private void OnGUI()
{
using (new GUILayout.VerticalScope())
{
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("原始路径:");
sourcePath = EditorGUILayout.TextField(sourcePath, GUILayout.Width(180));
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("目标路径:");
toPath = EditorGUILayout.TextField(toPath, GUILayout.Width(180));
EditorGUILayout.Space(10);
if (GUILayout.Button("开始复制", GUILayout.Width(150), GUILayout.Height(20)))
{
OnOk();
}
}
}
void OnOk()
{
CopyFolderKeepAssetsUsingEditor.CopyDirectoryDeep(sourcePath, toPath);
}
}
原始路径就是要复制的目录,目标路径就是复制出来的目录。
复制函数
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class CopyFolderKeepAssetsUsingEditor
{
//[MenuItem("Tools/复制文件夹(复制依赖关系) %#D", false, 0)]
//public static void CopyIt()
//{
// CopyDirectoryDeep("Assets/scenes/1", "Assets/scenes/2");
//}
public static void CopyDirectoryDeep(string sourcePath, string destinationPath)
{
CopyDirectoryRecursively(sourcePath, destinationPath);
List<string> metaFiles = GetFilesRecursively(destinationPath, (f) => f.EndsWith(".meta"));
List<(string originalGuid, string newGuid)> guidTable = new List<(string originalGuid, string newGuid)>();
foreach (string metaFile in metaFiles)
{
StreamReader file = new StreamReader(metaFile);
file.ReadLine();
string guidLine = file.ReadLine();
file.Close();
string originalGuid = guidLine.Substring(6, guidLine.Length - 6);
string newGuid = GUID.Generate().ToString().Replace("-", "");
guidTable.Add((originalGuid, newGuid));
}
List<string> allFiles = GetFilesRecursively(destinationPath);
foreach (string fileToModify in allFiles)
{
string content = File.ReadAllText(fileToModify);
foreach (var guidPair in guidTable)
{
content = content.Replace(guidPair.originalGuid, guidPair.newGuid);
}
File.WriteAllText(fileToModify, content);
}
AssetDatabase.Refresh();
}
private static void CopyDirectoryRecursively(string sourceDirName, string destDirName)
{
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
DirectoryInfo[] dirs = dir.GetDirectories();
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, false);
}
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
CopyDirectoryRecursively(subdir.FullName, temppath);
}
}
private static List<string> GetFilesRecursively(string path, Func<string, bool> criteria = null, List<string> files = null)
{
if (files == null)
{
files = new List<string>();
}
files.AddRange(Directory.GetFiles(path).Where(f => criteria == null || criteria(f)));
foreach (string directory in Directory.GetDirectories(path))
{
GetFilesRecursively(directory, criteria, files);
}
return files;
}
}
这里就结束了。
改进问题
使用了几次发现几个问题:
1,首先ReadAllText和Write的时候二进制非纯文本文件会出现问题,例如图片变大,所以我们判断是不是二进制文件再进行修改替换guid。
2,一些文件例如脚本不能复制了,例如cs文件两份会报错,inputactions文件也不需要两份。增加对一些扩展名进行排除。
输入复制也不方便,改为了拖动的方式复制。
直接贴上代码
using Sirenix.Utilities.Editor;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
public class CopyFolderKeepAssetsUsingEditor
{
//[MenuItem("Tools/复制文件夹(复制依赖关系) %#D", false, 0)]
//public static void CopyIt()
//{
// CopyDirectoryDeep("Assets/scenes/1", "Assets/scenes/2");
//}
//-------------文件操作---------------//
public static bool CopyFileDeep(string sourceFile, string destinationFile)
{
FileInfo file;
List<string> efile = new List<string>(ResCopyWin.efile.Split(';'));
string expname = Path.GetExtension(sourceFile).Trim();
if(efile.IndexOf(expname) != -1 )
{
Debug.LogError("这个文件被排除了类型:" + sourceFile + ",无法复制。");
return false;
}
string toPath = Path.GetDirectoryName(destinationFile);
if (!Directory.Exists(toPath))
{
Directory.CreateDirectory(toPath);
}
Debug.Log($"资源文件:{
sourceFile} -> {
destinationFile},准备开始复制。");
if (File.Exists(sourceFile))
{
file = new FileInfo(sourceFile);
file.CopyTo(destinationFile, true);
}
//拷贝meta
string sourceFileMeta = sourceFile + ".meta";
string destinationFileMeta = destinationFile + ".meta";
if (File.Exists(sourceFileMeta))
{
file = new FileInfo(sourceFileMeta);
file.CopyTo(destinationFileMeta, true);
}
//替换meta中的guid
StreamReader filestream = new StreamReader(destinationFileMeta);
filestream.ReadLine();
string guidLine = filestream.ReadLine();
filestream.Close();
string originalGuid = guidLine.Substring(6, guidLine.Length - 6);
string newGuid = GUID.Generate().ToString().Replace("-", "");
string content = File.ReadAllText(destinationFileMeta);
content = content.Replace(originalGuid, newGuid);
File.WriteAllText(destinationFileMeta, content);
Debug.Log($"资源文件复制完毕:{
sourceFile} -> {
destinationFile}");
AssetDatabase.Refresh();
return true;
}
//--------------文件夹操作-----------------//
public static bool CopyDirectoryDeep(string sourcePath, string destinationPath)
{
Debug.Log($"资源目录复制开始:{
sourcePath} -> {
destinationPath}");
CopyDirectoryRecursively(sourcePath, destinationPath);
List<string> metaFiles = GetFilesRecursively(destinationPath, (f) => f.ToLower().EndsWith(".meta"));
List<(string originalGuid, string newGuid)> guidTable = new List<(string originalGuid, string newGuid)>();
foreach (string metaFile in metaFiles)
{
StreamReader file = new StreamReader(metaFile);
file.ReadLine();
string guidLine = file.ReadLine();
file.Close();
string originalGuid = guidLine.Substring(6, guidLine.Length - 6);
string newGuid = GUID.Generate().ToString().Replace("-", "");
guidTable.Add((originalGuid, newGuid));
}
List<string> allFiles = GetFilesRecursively(destinationPath);
foreach (string fileToModify in allFiles)
{
bool binfile = CheckForBinary(fileToModify);
if (binfile)
{
Debug.Log("<color=#111111>二进制文件不用修改引用</color>:" + fileToModify);
continue;
}
//string newname = fileToModify.ToLower();
//if (newname.EndsWith(".png"))
// continue;
//if (newname.EndsWith(".jpg"))
// continue;
//if (newname.EndsWith(".bmp"))
// continue;
//if (newname.EndsWith(".tif"))
// continue;
//if (newname.EndsWith(".tga"))
// continue;
//if (newname.EndsWith(".psd"))
// continue;
string content = File.ReadAllText(fileToModify);
foreach (var guidPair in guidTable)
{
string oldstr = guidPair.originalGuid;
string newstr = guidPair.newGuid;
//Debug.Log(content.Length+","+ oldstr + ":" + newstr);
content = content.Replace(oldstr, newstr);
//Debug.Log(content.Length );
}
Debug.Log("修改文件引用:" + fileToModify);
File.WriteAllText(fileToModify, content);
}
Debug.Log($"<color=#d9f5d6>目录文件复制完毕</color>:{
sourcePath} -> {
destinationPath}。");
/// <summary>
/// This method checks whether selected file is Binary file or not.
/// </summary>
static bool CheckForBinary(string file)
{
Stream objStream = new FileStream(file, FileMode.Open, FileAccess.Read);
bool bFlag = true;
// Iterate through stream & check ASCII value of each byte.
for (int nPosition = 0; nPosition < objStream.Length; nPosition++)
{
int a = objStream.ReadByte();
if (!(a >= 0 && a <= 127))
{
break; // Binary File
}
else if (objStream.Position == (objStream.Length))
{
bFlag = false; // Text File
}
}
objStream.Dispose();
return bFlag;
}
//AssetDatabase.Refresh();
return true;
}
private static void CopyDirectoryRecursively(string sourceDirName, string destDirName)
{
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
List<string> efile = new List<string>(ResCopyWin.efile.Split(';'));
DirectoryInfo[] dirs = dir.GetDirectories();
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string expname = file.Extension.Trim().ToLower();
if (efile.IndexOf(expname) != -1)
{
Debug.Log("这个文件被排除了类型:" + file.FullName + "");
continue;
}
if (expname == ".meta")
{
string newname = file.FullName.Replace(".meta", "");
expname = Path.GetExtension(newname).ToLower();
if (efile.IndexOf(expname) != -1)
{
Debug.Log("这个文件被排除了类型:" + file.FullName + "");
continue;
}
}
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, true);
Debug.Log("复制文件:" + temppath + "");
}
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
CopyDirectoryRecursively(subdir.FullName, temppath);
}
}
private static List<string> GetFilesRecursively(string path, Func<string, bool> criteria = null, List<string> files = null)
{
if (files == null)
{
files = new List<string>();
}
files.AddRange(Directory.GetFiles(path).Where(f => criteria == null || criteria(f)));
foreach (string directory in Directory.GetDirectories(path))
{
GetFilesRecursively(directory, criteria, files);
}
return files;
}
}
using JFrameWork.ResEditor;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.Rendering.Universal;
public class ResCopyWin : EditorWindow
{
public static ResCopyWin copyWin;
//[MenuItem("JFrameWork/Resources/复制文件夹(复制依赖关系) %#D", false, 0)] Ctrl+Shift+D
[MenuItem("JFrameWork/Resources/复制文件夹(复制依赖关系)", false, 0)]
public static void CopyIt()
{
//否则就弹出面板设置
copyWin = EditorWindow.GetWindow(typeof(ResCopyWin), true, "复制资源目录") as ResCopyWin;
copyWin.ShowUtility();
}
string sourcePath = "Assets/";
string toPath = "Assets/";
public static string efile = ".cs;.shader;.inputactions";
string sOut = "";
Object targetGo;
int copytype = 0;
private void OnGUI()
{
copytype = 0;
using (new GUILayout.VerticalScope())
{
EditorGUILayout.LabelField("拖入要复制的目录或者Prefab:");
EditorGUILayout.Space(10);
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField("资源:", GUILayout.Width(50));
targetGo = (Object)EditorGUILayout.ObjectField(targetGo, typeof(Object), true);
GUILayout.EndHorizontal();
EditorGUILayout.Space(10);
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField("排除:", GUILayout.Width(50));
efile = EditorGUILayout.TextField(efile.ToLower());
GUILayout.EndHorizontal();
if (targetGo != null)
{
sourcePath = AssetDatabase.GetAssetPath(targetGo);
if (File.Exists(sourcePath))
{
copytype = 1;
string filename = Path.GetFileName(sourcePath);
//string expname = Path.GetFileNameWithoutExtension(sourcePath);
toPath = sourcePath.Replace(filename, "") +"_Copy/";
toPath += filename;
}
else if (Directory.Exists(sourcePath))
{
copytype = 2;
toPath = sourcePath + "_Copy";
}
EditorGUILayout.Space(10);
if (copytype > 0)
{
string showtxt = copytype == 1 ? "开始复制资源" : "开始复制文件夹";
if (GUILayout.Button(showtxt, GUILayout.Width(150), GUILayout.Height(20)))
{
OnOk();
}
}
EditorGUILayout.Space(10);
EditorGUILayout.LabelField(sOut);
}
//sourcePath = EditorGUILayout.TextField(sourcePath, GUILayout.Width(180));
//GUILayout.EndHorizontal();
//EditorGUILayout.Space(10);
//EditorGUILayout.LabelField("目标路径:");
//toPath = EditorGUILayout.TextField(toPath, GUILayout.Width(180));
}
}
void OnOk()
{
sOut = "";
if (copytype == 1)
{
bool bok = CopyFolderKeepAssetsUsingEditor.CopyFileDeep(sourcePath, toPath);
if (bok)
{
sOut = "复制完成 !!!";
}
else
{
sOut = "复制失败,请检查日志 !!!";
}
}
else if (copytype == 2)
{
bool bok = CopyFolderKeepAssetsUsingEditor.CopyDirectoryDeep(sourcePath, toPath);
if (bok)
{
sOut = "复制完成 !!!";
}
else
{
sOut = "复制失败,请检查日志 !!!";
}
}
}
}