123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- using AssetBank.Settings;
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using UnityEditor;
- using UnityEngine;
- using Newtonsoft.Json.Linq;
- namespace AssetBank.Editor.Tools
- {
- /// <summary>
- /// Handles the core logic for scanning the project and managing the component stats.
- /// </summary>
- public class ComponentStatsController
- {
- private static readonly string ProjectRoot = Path.GetDirectoryName(Application.dataPath);
-
- public enum ScanSource
- {
- Scenes,
- Prefabs,
- Both
- }
- /// <summary>
- /// Scans the project for components and gathers statistics about them.
- /// </summary>
- /// <param name="scanSource">The type of assets to scan.</param>
- /// <returns>A dictionary mapping component names to their statistics.</returns>
- public Dictionary<string, ComponentStats> ScanProject(ScanSource scanSource)
- {
- var settings = ProjectExporterSettings.GetOrCreateSettings();
- var ignoredFolders = settings.FoldersToIgnore;
- var assetsToScan = new List<string>();
- if (scanSource == ScanSource.Scenes || scanSource == ScanSource.Both)
- {
- assetsToScan.AddRange(AssetDatabase.FindAssets("t:Scene", new[] { "Assets" }));
- }
- if (scanSource == ScanSource.Prefabs || scanSource == ScanSource.Both)
- {
- assetsToScan.AddRange(AssetDatabase.FindAssets("t:Prefab", new[] { "Assets" }));
- }
- var assetPaths = assetsToScan.Distinct().Select(AssetDatabase.GUIDToAssetPath).ToList();
- var filteredPaths = assetPaths.Where(path => !ignoredFolders.Any(folder => path.StartsWith(folder, StringComparison.OrdinalIgnoreCase))).ToList();
- var tempDir = Path.Combine("Library", "ComponentScanTemp");
- if (Directory.Exists(tempDir))
- {
- Directory.Delete(tempDir, true);
- }
- Directory.CreateDirectory(tempDir);
- var componentStats = new Dictionary<string, ComponentStats>();
- try
- {
- EditorUtility.DisplayProgressBar("Scanning Components", "Preparing to scan...", 0f);
- for (int i = 0; i < filteredPaths.Count; i++)
- {
- var assetPath = filteredPaths[i];
- var progress = (float)i / filteredPaths.Count;
- EditorUtility.DisplayProgressBar("Scanning Components", $"Processing: {Path.GetFileName(assetPath)}", progress);
- var jsonPath = Path.Combine(tempDir, $"{Path.GetFileNameWithoutExtension(assetPath)}_{i}.json");
- if (ExecutePythonConversion(assetPath, jsonPath))
- {
- ParseComponentsFromJson(jsonPath, componentStats);
- }
- }
- }
- finally
- {
- if (Directory.Exists(tempDir))
- {
- Directory.Delete(tempDir, true);
- }
- EditorUtility.ClearProgressBar();
- }
- return componentStats;
- }
-
- private string FindPythonExecutable()
- {
- var venvPath = Path.Combine(ProjectRoot, "venv", "bin", "python3");
- if (File.Exists(venvPath))
- {
- return venvPath;
- }
- venvPath = Path.Combine(ProjectRoot, "venv", "bin", "python");
- if (File.Exists(venvPath))
- {
- return venvPath;
- }
- return "python3";
- }
- private bool ExecutePythonConversion(string assetPath, string jsonOutputPath)
- {
- var pythonExecutable = FindPythonExecutable();
- var scriptGuid = AssetDatabase.FindAssets("convert_scene").FirstOrDefault();
- if (string.IsNullOrEmpty(scriptGuid))
- {
- UnityEngine.Debug.LogError("Conversion script 'convert_scene.py' not found.");
- return false;
- }
- var scriptPath = Path.Combine(ProjectRoot, AssetDatabase.GUIDToAssetPath(scriptGuid));
- var absoluteAssetPath = Path.Combine(ProjectRoot, assetPath);
- var process = new Process
- {
- StartInfo = new ProcessStartInfo
- {
- FileName = pythonExecutable,
- Arguments = $"\"{scriptPath}\" \"{absoluteAssetPath}\" \"{jsonOutputPath}\"",
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- UseShellExecute = false,
- CreateNoWindow = true
- }
- };
- process.Start();
- string error = process.StandardError.ReadToEnd();
- process.WaitForExit();
- if (process.ExitCode == 0) return true;
- UnityEngine.Debug.LogError($"Failed to convert {assetPath}. Error: {error}");
- return false;
- }
- /// <summary>
- /// Parses a JSON file and updates the statistics for each component found.
- /// </summary>
- private void ParseComponentsFromJson(string jsonPath, Dictionary<string, ComponentStats> stats)
- {
- try
- {
- var jsonContent = File.ReadAllText(jsonPath);
- var jsonArray = JArray.Parse(jsonContent);
- foreach (var item in jsonArray)
- {
- if (item["data"] is not JObject dataObject) continue;
-
- var componentProperty = dataObject.Properties().FirstOrDefault();
- if (componentProperty == null) continue;
- var componentName = componentProperty.Name;
- if (!stats.ContainsKey(componentName))
- {
- stats[componentName] = new ComponentStats { Name = componentName };
- }
- var currentStats = stats[componentName];
- currentStats.TotalOccurrences++;
- // Pass the *value* of the component property to the counter
- var propertyCount = CountProperties(componentProperty.Value);
-
- currentStats.TotalProperties += propertyCount;
- if (propertyCount > currentStats.MaxProperties)
- {
- currentStats.MaxProperties = propertyCount;
- }
- }
- }
- catch (Exception e)
- {
- UnityEngine.Debug.LogError($"Failed to parse JSON file {jsonPath}. Error: {e.Message}");
- }
- }
- /// <summary>
- /// Recursively counts the number of properties in a JToken.
- /// </summary>
- private int CountProperties(JToken token)
- {
- int count = 0;
- if (token is JObject obj)
- {
- foreach (var prop in obj.Properties())
- {
- count++; // Count the key itself
- count += CountProperties(prop.Value); // Recursively count properties in the value
- }
- }
- else if (token is JArray arr)
- {
- foreach (var item in arr)
- {
- count += CountProperties(item);
- }
- }
- return count;
- }
- public void UpdateAndSaveScanResults(Dictionary<string, ComponentStats> stats)
- {
- var settings = ComponentStatsSettings.GetOrCreateSettings();
- settings.ComponentStats.Clear();
- settings.ComponentStats.AddRange(stats.Values.OrderBy(s => s.Name));
- settings.LastScanTime = DateTime.Now.ToString("g"); // "g" for general short date/time
- EditorUtility.SetDirty(settings);
- AssetDatabase.SaveAssets();
- }
- public void SaveStats(Dictionary<string, ComponentStats> stats)
- {
- var settings = ComponentStatsSettings.GetOrCreateSettings();
- settings.ComponentStats.Clear();
- settings.ComponentStats.AddRange(stats.Values.OrderBy(s => s.Name));
-
- EditorUtility.SetDirty(settings);
- AssetDatabase.SaveAssets();
- EditorUtility.DisplayDialog("Success", $"Saved {settings.ComponentStats.Count} component stats.", "OK");
- }
- }
- }
|