三国卡牌客户端基础资源仓库
yyl
2 天以前 cec146fc3fe287928e075c79ece20a20a9b16b20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
 
namespace ProjSG.Editor.UI
{
    public class AssetDuplicateCheck : EditorWindow
    {
        private Vector2 scrollPosition;
        private bool showSettings = true;
        private bool showResults = false;
        private bool showFullPath = false;
        
        private string[] searchFolders = new string[] { "Assets" };
        private string[] searchExtensions = new string[] { ".png", ".jpg", ".jpeg", ".tga", ".psd", ".tif", ".tiff" };
        private bool includeSubfolders = true;
        private bool compareByContent = true;
        private bool compareByName = false;
        private int minFileSize = 10;
        
        private Dictionary<string, List<string>> duplicateGroups = new Dictionary<string, List<string>>();
        private int totalFilesScanned = 0;
        private int totalDuplicatesFound = 0;
        private long totalSpaceSaved = 0;
        
        private bool keepNewestFiles = true;
        private bool createBackup = true;
        private string backupFolder = "";
        private Dictionary<string, bool> selectedForRemoval = new Dictionary<string, bool>();
        
        private bool isProcessing = false;
        private float progress = 0f;
        private string progressInfo = "";
 
        [MenuItem("工具/资源管理/资源查重工具")]
        public static void ShowWindow()
        {
            AssetDuplicateCheck window = GetWindow<AssetDuplicateCheck>("资源查重工具");
            window.minSize = new Vector2(600, 400);
            window.Show();
        }
 
        private string GetAssetPath(string filePath)
        {
            string fullPath = Path.GetFullPath(filePath);
            string projectDataPath = Path.GetFullPath(Application.dataPath);
 
            if (fullPath.StartsWith(projectDataPath, StringComparison.OrdinalIgnoreCase))
            {
                return "Assets" + fullPath.Substring(projectDataPath.Length).Replace('\\', '/');
            }
            
            string projectRootPath = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
            if (fullPath.StartsWith(projectRootPath, StringComparison.OrdinalIgnoreCase))
            {
                string relativeToProjectRoot = fullPath.Substring(projectRootPath.Length);
                if (relativeToProjectRoot.StartsWith("/") || relativeToProjectRoot.StartsWith("\\"))
                {
                    relativeToProjectRoot = relativeToProjectRoot.Substring(1);
                }
                return relativeToProjectRoot.Replace('\\', '/');
            }
 
            Debug.LogWarning($"路径 '{filePath}' 无法可靠地转换为项目相对的资产路径。将返回原始路径(已规范化斜杠)。");
            return filePath.Replace('\\', '/');
        }
 
        private void OnGUI()
        {
            GUILayout.Label("资源查重工具", EditorStyles.boldLabel);
            EditorGUILayout.Space();
 
            scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
 
            showSettings = EditorGUILayout.Foldout(showSettings, "搜索设置", true);
            if (showSettings)
            {
                DrawSettings();
            }
 
            EditorGUILayout.Space();
 
            if (duplicateGroups.Count > 0)
            {
                showResults = EditorGUILayout.Foldout(showResults, $"搜索结果 (找到 {totalDuplicatesFound} 个重复资源,可节省 {totalSpaceSaved / 1024f:F2} MB)", true);
                if (showResults)
                {
                    DrawResults();
                }
            }
 
            EditorGUILayout.EndScrollView();
 
            EditorGUILayout.Space();
 
            if (isProcessing)
            {
                EditorGUI.ProgressBar(EditorGUILayout.GetControlRect(false, 20f), progress, progressInfo);
                if (GUILayout.Button("取消"))
                {
                    isProcessing = false;
                }
            }
            else
            {
                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("扫描重复资源", GUILayout.Height(30)))
                {
                    FindDuplicateAssets();
                }
 
                GUI.enabled = duplicateGroups.Count > 0;
                if (GUILayout.Button("处理选中的重复资源", GUILayout.Height(30)))
                {
                    ProcessDuplicateAssets();
                }
                GUI.enabled = true;
                EditorGUILayout.EndHorizontal();
            }
        }
 
        private void DrawSettings()
        {
            EditorGUI.indentLevel++;
 
            EditorGUILayout.LabelField("搜索文件夹:");
            EditorGUILayout.BeginHorizontal();
            string displaySearchFolder = searchFolders.Length > 0 ? searchFolders[0] : "Assets";
            EditorGUILayout.SelectableLabel(displaySearchFolder, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            
            if (GUILayout.Button("选择文件夹", GUILayout.Width(100)))
            {
                string folderPath = EditorUtility.OpenFolderPanel("选择要搜索的文件夹", Application.dataPath, "");
                if (!string.IsNullOrEmpty(folderPath))
                {
                    string relativePath = GetAssetPath(folderPath);
                    if (!string.IsNullOrEmpty(relativePath) && 
                        (relativePath.StartsWith("Assets", StringComparison.OrdinalIgnoreCase) || 
                         relativePath.StartsWith("Packages", StringComparison.OrdinalIgnoreCase) || 
                         Directory.Exists(Path.Combine(Path.GetFullPath(Path.Combine(Application.dataPath, "..")), relativePath))))
                    {
                        searchFolders = new string[] { relativePath };
                    }
                    else
                    {
                        Debug.LogWarning($"选择的文件夹 '{folderPath}' 不在项目 Assets 或 Packages 内,或无法转换为有效的项目相对路径。");
                    }
                }
            }
            EditorGUILayout.EndHorizontal();
 
            EditorGUILayout.LabelField("文件类型:");
            string extensionsString = string.Join(", ", searchExtensions);
            extensionsString = EditorGUILayout.TextField(extensionsString);
            searchExtensions = extensionsString.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(e => e.Trim().ToLower())
                .Select(e => e.StartsWith(".") ? e : "." + e)
                .Distinct()
                .ToArray();
 
            EditorGUILayout.Space();
            EditorGUILayout.LabelField("搜索选项:");
            includeSubfolders = EditorGUILayout.Toggle("包含子文件夹", includeSubfolders);
            compareByContent = EditorGUILayout.Toggle("按内容比较", compareByContent);
            compareByName = EditorGUILayout.Toggle("按名称比较", compareByName);
            minFileSize = EditorGUILayout.IntField("最小文件大小 (KB)", minFileSize);
            
            EditorGUILayout.Space();
            EditorGUILayout.LabelField("处理选项:");
            keepNewestFiles = EditorGUILayout.Toggle("保留最新文件", keepNewestFiles);
            createBackup = EditorGUILayout.Toggle("创建备份", createBackup);
            
            if (createBackup)
            {
                EditorGUILayout.BeginHorizontal();
                backupFolder = EditorGUILayout.TextField("备份文件夹:", backupFolder);
                if (GUILayout.Button("浏览", GUILayout.Width(60)))
                {
                    string path = EditorUtility.OpenFolderPanel("选择备份文件夹", backupFolder, "");
                    if (!string.IsNullOrEmpty(path))
                    {
                        backupFolder = path;
                    }
                }
                EditorGUILayout.EndHorizontal();
            }
 
            EditorGUI.indentLevel--;
        }
 
        private void DrawResults()
        {
            EditorGUI.indentLevel++;
            
            EditorGUILayout.BeginHorizontal();
            showFullPath = EditorGUILayout.Toggle("显示完整路径", showFullPath);
            
            GUILayout.FlexibleSpace();
            
            if (GUILayout.Button("全选", GUILayout.Width(60)))
            {
                SelectAll(true);
            }
            
            if (GUILayout.Button("全不选", GUILayout.Width(60)))
            {
                SelectAll(false);
            }
            
            if (GUILayout.Button("智能选择", GUILayout.Width(80)))
            {
                SmartSelect();
            }
            
            EditorGUILayout.EndHorizontal();
            
            EditorGUILayout.Space();
            
            int groupIndex = 0;
            foreach (var group in duplicateGroups)
            {
                if (group.Value.Count <= 1) continue;
                
                groupIndex++;
                EditorGUILayout.BeginVertical(GUI.skin.box);
                
                string groupTitle = $"组 {groupIndex}: {group.Value.Count} 个文件";
                if (group.Value.Count > 0)
                {
                    string fileName = Path.GetFileName(group.Value[0]);
                    groupTitle += $" - {fileName}";
                }
                
                EditorGUILayout.LabelField(groupTitle, EditorStyles.boldLabel);
                
                foreach (string filePath in group.Value)
                {
                    EditorGUILayout.BeginHorizontal();
                    
                    bool isSelected = false;
                    if (selectedForRemoval.ContainsKey(filePath))
                    {
                        isSelected = selectedForRemoval[filePath];
                    }
                    
                    bool newSelected = EditorGUILayout.Toggle(isSelected, GUILayout.Width(20));
                    if (newSelected != isSelected)
                    {
                        selectedForRemoval[filePath] = newSelected;
                    }
                    
                    string displayPath = showFullPath ? filePath : Path.GetFileName(filePath);
                    
                    FileInfo fileInfo = new FileInfo(Path.Combine(Application.dataPath, "..", filePath));
                    string fileDetails = $"{displayPath} - {fileInfo.Length / 1024} KB";
                    
                    if (fileInfo.Exists)
                    {
                        fileDetails += $" - {fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")}";
                    }
                    
                    EditorGUILayout.LabelField(fileDetails);
                    
                    if (GUILayout.Button("选择", GUILayout.Width(60)))
                    {
                        Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(filePath);
                    }
                    
                    EditorGUILayout.EndHorizontal();
                }
                
                EditorGUILayout.EndVertical();
                EditorGUILayout.Space();
            }
            
            EditorGUI.indentLevel--;
        }
 
        private void SelectAll(bool select)
        {
            foreach (var group in duplicateGroups)
            {
                if (group.Value.Count <= 1) continue;
                
                foreach (string filePath in group.Value)
                {
                    selectedForRemoval[filePath] = select;
                }
            }
        }
 
        private void SmartSelect()
        {
            foreach (var group in duplicateGroups)
            {
                if (group.Value.Count <= 1) continue;
                
                string fileToKeep = null;
                
                if (keepNewestFiles)
                {
                    fileToKeep = group.Value
                        .OrderByDescending(f => File.GetLastWriteTime(Path.Combine(Application.dataPath, "..", f)))
                        .FirstOrDefault();
                }
                else
                {
                    fileToKeep = group.Value.FirstOrDefault();
                }
                
                foreach (string filePath in group.Value)
                {
                    selectedForRemoval[filePath] = (filePath != fileToKeep);
                }
            }
        }
 
        // 计算文件的MD5哈希值
        private string GetFileHash(string filePath)
        {
            try
            {
                using (var md5 = MD5.Create())
                using (var stream = File.OpenRead(filePath))
                {
                    byte[] hash = md5.ComputeHash(stream);
                    return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
                }
            }
            catch (Exception ex)
            {
                Debug.LogError($"计算文件哈希值时出错: {filePath}, {ex.Message}");
                return string.Empty;
            }
        }
 
        private async void FindDuplicateAssets()
        {
            isProcessing = true;
            progress = 0f;
            progressInfo = "正在收集文件...";
            
            // 清空之前的结果
            duplicateGroups.Clear();
            selectedForRemoval.Clear();
            totalFilesScanned = 0;
            totalDuplicatesFound = 0;
            totalSpaceSaved = 0;
            
            try
            {
                // 收集所有文件
                List<string> allFiles = new List<string>();
                foreach (string folder in searchFolders)
                {
                    if (string.IsNullOrEmpty(folder)) continue;
                    
                    string fullPath = Path.Combine(Application.dataPath, "..", folder);
                    if (!Directory.Exists(fullPath)) continue;
                    
                    string[] files = Directory.GetFiles(fullPath, "*.*", includeSubfolders ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
                        .Where(f => searchExtensions.Contains(Path.GetExtension(f).ToLower()))
                        .Select(f => GetAssetPath(f))
                        .ToArray();
                    
                    allFiles.AddRange(files);
                }
                
                totalFilesScanned = allFiles.Count;
                progressInfo = $"找到 {totalFilesScanned} 个文件,正在分析...";
                progress = 0.1f;
                Repaint();
                
                // 按内容或名称分组
                Dictionary<string, List<string>> groups = new Dictionary<string, List<string>>();
                
                for (int i = 0; i < allFiles.Count; i++)
                {
                    string filePath = allFiles[i];
                    string fullPath = Path.Combine(Application.dataPath, "..", filePath);
                    
                    // 检查文件大小
                    FileInfo fileInfo = new FileInfo(fullPath);
                    if (!fileInfo.Exists || fileInfo.Length < minFileSize * 1024) continue;
                    
                    string key = "";
                    
                    if (compareByContent)
                    {
                        // 计算文件哈希值
                        key = GetFileHash(fullPath);
                    }
                    else if (compareByName)
                    {
                        // 使用文件名作为键
                        key = Path.GetFileNameWithoutExtension(filePath);
                    }
                    
                    if (string.IsNullOrEmpty(key)) continue;
                    
                    if (!groups.ContainsKey(key))
                    {
                        groups[key] = new List<string>();
                    }
                    
                    groups[key].Add(filePath);
                    
                    // 更新进度
                    if (i % 10 == 0)
                    {
                        progress = 0.1f + 0.8f * ((float)i / allFiles.Count);
                        progressInfo = $"正在分析文件 {i+1}/{allFiles.Count}...";
                        Repaint();
                        await Task.Delay(1);
                    }
                }
                
                // 过滤出重复组
                foreach (var group in groups)
                {
                    if (group.Value.Count > 1)
                    {
                        duplicateGroups[group.Key] = group.Value;
                        totalDuplicatesFound += group.Value.Count - 1;
                        
                        // 计算可节省空间
                        string firstFile = group.Value[0];
                        string fullPath = Path.Combine(Application.dataPath, "..", firstFile);
                        FileInfo fileInfo = new FileInfo(fullPath);
                        if (fileInfo.Exists)
                        {
                            totalSpaceSaved += (group.Value.Count - 1) * fileInfo.Length / 1024;
                        }
                    }
                }
                
                progressInfo = $"分析完成,找到 {totalDuplicatesFound} 个重复资源";
                progress = 1f;
                Repaint();
                
                // 自动选择
                if (duplicateGroups.Count > 0)
                {
                    SmartSelect();
                }
            }
            catch (Exception ex)
            {
                Debug.LogError($"查找重复资源时出错: {ex.Message}\n{ex.StackTrace}");
                EditorUtility.DisplayDialog("错误", $"查找重复资源时出错: {ex.Message}", "确定");
            }
            finally
            {
                isProcessing = false;
                Repaint();
            }
        }
 
        private async void ProcessDuplicateAssets()
        {
            isProcessing = true;
            progress = 0f;
            progressInfo = "正在准备处理...";
            Repaint();
 
            // 统计要处理的文件数量
            int totalToProcess = selectedForRemoval.Count(pair => pair.Value);
            if (totalToProcess == 0)
            {
                EditorUtility.DisplayDialog("提示", "没有选择任何文件进行处理", "确定");
                isProcessing = false;
                return;
            }
            
            // 确认操作
            bool confirm = EditorUtility.DisplayDialog("确认操作", 
                $"将处理 {totalToProcess} 个重复资源。\n" +
                (createBackup ? $"备份将保存到: {backupFolder}" : "不创建备份") +
                "\n\n此操作不可撤销,是否继续?", 
                "继续", "取消");
                
            if (!confirm)
            {
                isProcessing = false;
                return;
            }
            
            try
            {
                // 处理每个重复组
                foreach (var group in duplicateGroups)
                {
                    if (group.Value.Count <= 1) continue;
                    
                    // 找出要保留的文件(通常是最新的文件)
                    string keepFile = null;
                    if (keepNewestFiles)
                    {
                        keepFile = group.Value
                            .OrderByDescending(f => new FileInfo(Path.Combine(Application.dataPath, "..", f)).LastWriteTime)
                            .FirstOrDefault();
                    }
                    else
                    {
                        // 如果不是保留最新的,则保留第一个未被选中删除的文件
                        keepFile = group.Value.FirstOrDefault(f => !selectedForRemoval.ContainsKey(f) || !selectedForRemoval[f]);
                    }
                    
                    // 如果没有找到要保留的文件,跳过这个组
                    if (string.IsNullOrEmpty(keepFile)) 
                    {
                        throw new Exception("没有找到要保留的文件");
                        continue;
                    }
                    // 处理这个组中的其他文件
                    foreach (string filePath in group.Value)
                    {
                        if (filePath == keepFile) continue;
                        
                        // 检查是否选中了这个文件进行删除
                        if (!selectedForRemoval.ContainsKey(filePath) || !selectedForRemoval[filePath]) continue;
                        
                        // 更新引用关系,将引用从filePath重定向到keepFile
                        UpdateReferences(filePath, keepFile);
                        
                        // ... existing code ...
                        
                        // 创建备份和删除文件的代码
                    }
                }
 
                // 刷新资源数据库
                AssetDatabase.Refresh();
 
                // 创建备份文件夹
                if (createBackup && !string.IsNullOrEmpty(backupFolder))
                {
                    Directory.CreateDirectory(backupFolder);
                }
                
                // 收集要删除的文件和保留的文件
                List<string> filesToRemove = new List<string>();
                Dictionary<string, string> fileReplaceMap = new Dictionary<string, string>();
                
                var tempGroups = new Dictionary<string, List<string>>(duplicateGroups);
                var tempRemoval = new Dictionary<string, bool>(selectedForRemoval);
 
                // 清空结果数据
                duplicateGroups.Clear();
                selectedForRemoval.Clear();
                totalFilesScanned = 0;
                totalDuplicatesFound = 0;
                totalSpaceSaved = 0;
                showResults = false;
 
                foreach (var group in tempGroups)
                {
                    if (group.Value.Count <= 1) continue;
                    
                    // 找出要保留的文件
                    string fileToKeep = null;
                    
                    foreach (string filePath in group.Value)
                    {
                        if (!tempRemoval.ContainsKey(filePath) || !tempRemoval[filePath])
                        {
                            fileToKeep = filePath;
                            break;
                        }
                    }
                    
                    // 如果所有文件都被标记为删除,保留第一个
                    if (string.IsNullOrEmpty(fileToKeep) && group.Value.Count > 0)
                    {
                        if (keepNewestFiles)
                        {
                            // 按最后修改时间排序,保留最新的
                            fileToKeep = group.Value
                                .OrderByDescending(f => File.GetLastWriteTime(Path.Combine(Application.dataPath, "..", f)))
                                .FirstOrDefault();
                        }
                        else
                        {
                            fileToKeep = group.Value[0];
                        }
                        
                        if (tempRemoval.ContainsKey(fileToKeep))
                        {
                            tempRemoval[fileToKeep] = false;
                        }
                    }
                    
                    // 收集要删除的文件
                    foreach (string filePath in group.Value)
                    {
                        if (filePath != fileToKeep && tempRemoval.ContainsKey(filePath) && tempRemoval[filePath])
                        {
                            filesToRemove.Add(filePath);
                            fileReplaceMap[filePath] = fileToKeep;
                        }
                    }
                }
                
                // 处理文件
                int processed = 0;
                foreach (string filePath in filesToRemove)
                {
                    processed++;
                    progress = (float)processed / totalToProcess;
                    progressInfo = $"正在处理 {processed}/{totalToProcess}: {Path.GetFileName(filePath)}";
                    Repaint();
                    
                    try
                    {
                        // 备份文件
                        if (createBackup && !string.IsNullOrEmpty(backupFolder))
                        {
                            string backupPath = Path.Combine(backupFolder, Path.GetFileName(filePath));
                            File.Copy(Path.Combine(Application.dataPath, "..", filePath), backupPath, true);
                        }
                        
                        // 更新引用
                        if (fileReplaceMap.ContainsKey(filePath))
                        {
                            UpdateReferences(filePath, fileReplaceMap[filePath]);
                        }
                        
                        // 删除文件
                        AssetDatabase.DeleteAsset(filePath);
                        
                        await Task.Delay(1);
                    }
                    catch (Exception ex)
                    {
                        Debug.LogError($"处理文件时出错: {filePath}, {ex.Message}");
                    }
                }
                
                // 刷新资源数据库
                AssetDatabase.Refresh();
                
 
                
                // 完成
                progressInfo = $"处理完成,已处理 {processed} 个文件";
                progress = 1f;
                EditorUtility.DisplayDialog("完成", $"已处理 {processed} 个重复资源", "确定");
            }
            catch (Exception ex)
            {
                Debug.LogError($"处理重复资源时出错: {ex.Message}\n{ex.StackTrace}");
                EditorUtility.DisplayDialog("错误", $"处理重复资源时出错: {ex.Message}", "确定");
            }
            finally
            {
                isProcessing = false;
                Repaint();
            }
        }
 
        // 更新引用关系
        private void UpdateReferences(string oldPath, string newPath)
        {
            string[] guids = AssetDatabase.FindAssets("t:Object");
            
            foreach (string guid in guids)
            {
                string assetPath = AssetDatabase.GUIDToAssetPath(guid);
                
                if (assetPath == oldPath || assetPath == newPath) continue;
                
                if (!assetPath.EndsWith(".asset") && !assetPath.EndsWith(".prefab") && !assetPath.EndsWith(".unity") && !assetPath.EndsWith(".mat"))
                    continue;
                
                UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath);
                if (obj == null) continue;
                
                string[] dependencies = AssetDatabase.GetDependencies(assetPath, false);
                if (dependencies.Contains(oldPath))
                {
                    // 获取旧资源和新资源
                    UnityEngine.Object oldAsset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(oldPath);
                    UnityEngine.Object newAsset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(newPath);
                    
                    if (oldAsset != null && newAsset != null)
                    {
                        // 使用SerializedObject更新引用
                        SerializedObject serializedObj = new SerializedObject(obj);
                        bool modified = false;
                        
                        SerializedProperty iterator = serializedObj.GetIterator();
                        while (iterator.NextVisible(true))
                        {
                            if (iterator.propertyType == SerializedPropertyType.ObjectReference && 
                                iterator.objectReferenceValue == oldAsset)
                            {
                                iterator.objectReferenceValue = newAsset;
                                modified = true;
                            }
                        }
                        
                        if (modified)
                        {
                            serializedObj.ApplyModifiedProperties();
                            Debug.Log($"已更新资源 {assetPath} 中的引用: {oldPath} -> {newPath}");
                        }
                    }
                    
                    EditorUtility.SetDirty(obj);
                }
            }
            
            AssetDatabase.SaveAssets();
        }
    }
}