-
-
Save Asuta/8c95cbd13c6c0aa7cd23c93d05e85edf 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/Scritps",and You can add as many folders as you want
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>(); | |
foreach(var dataPathElement in config.WatchedDirectoryList) | |
{ | |
//dataPath = string.IsNullOrEmpty(config.WatchedDirectory) || !Directory.Exists(config.WatchedDirectory) ? Application.dataPath : config.WatchedDirectory;//这里的意思是如果没有填写路径或者路径不存在就用默认路径 | |
var dataPath = string.IsNullOrEmpty(dataPathElement) || !Directory.Exists(dataPathElement) ? null : dataPathElement; | |
if (ContainsScripts(dataPath)) result.Add(dataPath); | |
foreach (var path in Directory.GetDirectories(dataPath, "*", SearchOption.AllDirectories)) | |
{ | |
if (ContainsScripts(path)) | |
{ | |
result.Add(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[] WatchedDirectoryList = new string[] | |
{ | |
"Assets/Scripts", | |
}; | |
} | |
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