-
-
Save unitycoder/482b37371743f47ffd230be0e4dbf336 to your computer and use it in GitHub Desktop.
Modify the version to be directly available, monitor all folders containing C# scripts under assets, and directly copy them to any directory under assets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Concurrent; | |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Threading.Tasks; | |
| using UnityEditor; | |
| using UnityEditorInternal; | |
| using UnityEngine; | |
| namespace Naninovel | |
| { | |
| /// <summary> | |
| /// Uses file system watcher to track changes to `.nani` files in the project directory. | |
| /// </summary> | |
| public static class ScriptFileWatcher | |
| { | |
| /// <summary> | |
| /// Invoked when a <see cref="Script"/> asset is created or modified; returns modified script asset path. | |
| /// </summary> | |
| public static event Action<string> OnModified; | |
| private static ConcurrentQueue<string> modifiedScriptPaths = new ConcurrentQueue<string>(); | |
| private static ConcurrentStack<FileSystemWatcher> runningWatchers = new ConcurrentStack<FileSystemWatcher>(); | |
| [InitializeOnLoadMethod] | |
| public static void Initialize() | |
| { | |
| StopWatching(); | |
| var config = new ScriptsConfiguration(); | |
| if (config.WatchScripts) StartWatching(config); | |
| } | |
| private static void StartWatching(ScriptsConfiguration config) | |
| { | |
| EditorApplication.update += Update; | |
| foreach (var path in FindDirectoriesWithScripts(config)) | |
| WatchDirectory(path); | |
| } | |
| private static void StopWatching() | |
| { | |
| EditorApplication.update -= Update; | |
| foreach (var watcher in runningWatchers) | |
| watcher?.Dispose(); | |
| runningWatchers.Clear(); | |
| } | |
| private static void Update() | |
| { | |
| if (modifiedScriptPaths.Count == 0) return; | |
| if (!modifiedScriptPaths.TryDequeue(out var fullPath)) return; | |
| if (!File.Exists(fullPath)) return; | |
| var assetPath = PathUtils.AbsoluteToAssetPath(fullPath); | |
| AssetDatabase.ImportAsset(assetPath); | |
| OnModified?.Invoke(assetPath); | |
| // Required to rebuild script when editor is not in focus, because script view | |
| // delays rebuild, but delayed call is not invoked while editor is not in focus. | |
| if (!InternalEditorUtility.isApplicationActive) | |
| EditorApplication.delayCall?.Invoke(); | |
| } | |
| private static IReadOnlyCollection<string> FindDirectoriesWithScripts(ScriptsConfiguration config) | |
| { | |
| var result = new List<string>(); | |
| var dataPath = string.IsNullOrEmpty(config.WatchedDirectory) || !Directory.Exists(config.WatchedDirectory) ? Application.dataPath : config.WatchedDirectory;//这里的意思是如果没有填写路径或者路径不存在就用默认路径 | |
| bool yes = true; | |
| if (yes) result.Add(dataPath); | |
| //遍历dataPath下的所有子目录 并直接添加到result | |
| foreach (var path in Directory.GetDirectories(dataPath, "*", SearchOption.AllDirectories))//这里的意思是只要有一个.cs文件就算有脚本 SearchOption.AllDirectories是搜索所有子目录 | |
| { | |
| if (ContainsScripts(path)) | |
| { | |
| result.Add(path); | |
| //Debug.LogError("AllPath:" + path); | |
| } | |
| } | |
| return result; | |
| bool ContainsScripts(string path) => Directory.GetFiles(path, "*.cs", SearchOption.TopDirectoryOnly).Length > 0;//这里的意思是只要有一个.nani文件就算有脚本 | |
| } | |
| private static void WatchDirectory(string path) | |
| { | |
| Task.Run(AddWatcher).ContinueWith(DisposeWatcher, TaskScheduler.FromCurrentSynchronizationContext()); | |
| FileSystemWatcher AddWatcher() | |
| { | |
| var watcher = CreateWatcher(path); | |
| runningWatchers.Push(watcher); | |
| return watcher; | |
| } | |
| } | |
| private static FileSystemWatcher CreateWatcher(string path) | |
| { | |
| var watcher = new FileSystemWatcher(); | |
| watcher.Path = path; | |
| watcher.IncludeSubdirectories = false; | |
| watcher.NotifyFilter = NotifyFilters.LastWrite; | |
| watcher.Filter = "*.cs"; | |
| watcher.Changed += (_, e) => modifiedScriptPaths.Enqueue(e.FullPath); | |
| watcher.EnableRaisingEvents = true; | |
| return watcher; | |
| } | |
| private static void DisposeWatcher(Task<FileSystemWatcher> startTask) | |
| { | |
| try | |
| { | |
| var watcher = startTask.Result; | |
| AppDomain.CurrentDomain.DomainUnload += (EventHandler)((_, __) => { watcher.Dispose(); }); | |
| } | |
| catch (Exception e) | |
| { | |
| Debug.LogError($"Failed to stop script file watcher: {e.Message}"); | |
| } | |
| } | |
| } | |
| internal class ScriptsConfiguration | |
| { | |
| public bool WatchScripts = true; | |
| public string WatchedDirectory = "Assets"; | |
| } | |
| internal class PathUtils | |
| { | |
| public static string AbsoluteToAssetPath(string absolutePath) | |
| { | |
| absolutePath = absolutePath.Replace("\\", "/"); | |
| return "Assets" + absolutePath.Replace(Application.dataPath, string.Empty); | |
| } | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment