工作中经常需要同时开启两个unity项目,并且在两个项目之前通过拖拽的方式互相传输文件。但我发现这个方案不是很稳定。有时候直接拖拽到另一个项目时候,预制体及材质球跟贴图互相之间挂载和引用是正确的,有时候会丢失引用。这边制作了一个插件,让我们可以稳定的迁移文件,且不丢失索引。
脚本使用完整指南
第一步:创建脚本
-
打开你的 目标项目(即接收资源的那个项目)。
-
在
Project窗口的Assets文件夹下,检查是否有名为 Editor 的文件夹。如果没有,请右键Create > Folder命名为Editor(名称必须精确)。 -
在
Editor文件夹内,右键Create > C# Script,命名为CrossProjectImporter。 -
双击打开,将上一条回复中的完整代码覆盖粘贴进去并保存。
第二步:打开工具窗口
-
回到 Unity 编辑器,等待脚本编译。
-
在顶部菜单栏你会看到多出一个选项:Tools。
-
点击 Tools > 结构化强导工具(完整版)。
第三步:设置与导入流程
1. 设置目标存放路径
在工具窗口的第一个输入框内,填写你希望资源存放的相对路径。
-
例子 :根据你的需求,填写
Arts/Avatar/DefaultSkin/001/001。 -
注意 :不需要写开头的
Assets/,脚本会自动补全。如果路径不存在,脚本会自动创建文件夹。
2. 添加源资源(跨项目选择)
-
点击 "添加文件或文件夹" 按钮。
-
在弹出的系统文件选择框中,横跨硬盘找到你另一个项目(源项目)的路径。
- 例子 :定位到
D:\Unity_002\...\002这个文件夹。
- 例子 :定位到
-
选中文件夹后点击"确定(选择文件夹)"。
3. 执行强力导入
-
核对下方列表,确认路径正确。
-
点击绿色的 "执行重定向导入" 按钮。
-
脚本会开始物理拷贝文件及其对应的
.meta。 -
完成后会弹出"导入成功"的提示。
using UnityEngine; using UnityEditor; using System.IO; using System.Collections.Generic; public class CrossProjectImporter : EditorWindow { // 默认目标路径(根据你的需求设定) private string targetPathInAssets = "请按需求输入被搬运文件的路径"; private List<string> selectedFolders = new List<string>(); [MenuItem("Tools/结构化强导工具(完整版)")] public static void ShowWindow() { CrossProjectImporter window = GetWindow<CrossProjectImporter>("结构化导入"); window.minSize = new Vector2(450, 500); } void OnGUI() { EditorGUILayout.Space(10); GUILayout.Label("1. 目标存放位置 (目标项目内)", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Assets/", GUILayout.Width(50)); targetPathInAssets = EditorGUILayout.TextField(targetPathInAssets); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(10); GUILayout.Label("2. 选择源文件夹 (跨项目选择整个文件夹)", EditorStyles.boldLabel); if (GUILayout.Button("添加源文件夹 (Add Folder)", GUILayout.Height(30))) { string path = EditorUtility.OpenFolderPanel("选择要搬运的【S13】特效文件夹", "", ""); if (!string.IsNullOrEmpty(path) && !selectedFolders.Contains(path)) { selectedFolders.Add(path); } } // 显示已选列表 EditorGUILayout.BeginVertical("box"); if (selectedFolders.Count == 0) EditorGUILayout.LabelField("列表为空,请先添加文件夹..."); for (int i = 0; i < selectedFolders.Count; i++) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(selectedFolders[i], EditorStyles.miniLabel); if (GUILayout.Button("移除", GUILayout.Width(40))) selectedFolders.RemoveAt(i); EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(20); if (selectedFolders.Count > 0) { GUI.backgroundColor = Color.green; if (GUILayout.Button("🚀 执行完整搬运 (保持结构+预制体+GUID)", GUILayout.Height(50))) { ExecuteFullImport(); } GUI.backgroundColor = Color.white; } EditorGUILayout.HelpBox("提示:\n1. 脚本会连带 .meta 物理拷贝,保留所有引用。\n2. 若导入后看不见文件,请在 Project 窗口右键 Reimport。", MessageType.Info); } void ExecuteFullImport() { // 1. 确保目标根目录存在 string rootDestDir = Path.GetFullPath(Path.Combine(Application.dataPath, targetPathInAssets)); if (!Directory.Exists(rootDestDir)) Directory.CreateDirectory(rootDestDir); try { AssetDatabase.StartAssetEditing(); // 暂停资源数据库自动刷新,提高拷贝效率 foreach (string sourcePath in selectedFolders) { if (!Directory.Exists(sourcePath)) continue; string folderName = Path.GetFileName(sourcePath); string finalFolderDest = Path.Combine(rootDestDir, folderName); // 执行递归拷贝 CopyDirectoryWithStructure(sourcePath, finalFolderDest); } } finally { AssetDatabase.StopAssetEditing(); // 恢复刷新 } // 2. 核心:强制同步刷新,解决"看不见预制体"的问题 EditorUtility.DisplayProgressBar("同步中", "正在强力刷新 AssetDatabase...", 0.5f); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport); EditorUtility.ClearProgressBar(); Debug.Log($"<color=green>搬运成功!</color> 目标路径: Assets/{targetPathInAssets}"); EditorUtility.DisplayDialog("搬运完成", "文件夹结构及预制体已物理搬运成功。\n\n关于FBX报错:由于模型动画曲线异常导致,通常不影响预制体使用。", "确定"); selectedFolders.Clear(); } void CopyDirectoryWithStructure(string sourceDir, string destDir) { // 创建目录 if (!Directory.Exists(destDir)) Directory.CreateDirectory(destDir); // 拷贝当前文件夹自身的 .meta CopyMetaFile(sourceDir, destDir); // 拷贝所有文件 string[] files = Directory.GetFiles(sourceDir); foreach (string file in files) { if (file.EndsWith(".meta")) continue; string fileName = Path.GetFileName(file); string destFilePath = Path.Combine(destDir, fileName); // 物理拷贝(覆盖模式) File.Copy(file, destFilePath, true); // 拷贝对应的 meta CopyMetaFile(file, destFilePath); } // 递归子目录 string[] subDirs = Directory.GetDirectories(sourceDir); foreach (string subDir in subDirs) { string subDirName = Path.GetFileName(subDir); string nextDestDir = Path.Combine(destDir, subDirName); CopyDirectoryWithStructure(subDir, nextDestDir); } } void CopyMetaFile(string sourcePath, string destPath) { string sourceMeta = sourcePath + ".meta"; string destMeta = destPath + ".meta"; if (File.Exists(sourceMeta)) { File.Copy(sourceMeta, destMeta, true); } } }