Files
BABA_YAGA/Assets/Editor/AutoSaveTool.cs
2026-03-26 20:27:19 +07:00

175 lines
6.6 KiB
C#

// ===============================================================================
// AutoSaveTool - Persistent & Robust Auto-Saving for Unity Editor
//
// Creator: Scove
// Last Updated: 2024-05-08
// Version: 2.0
//
// Purpose:
// This tool provides a persistent, background auto-saving mechanism for the Unity Editor.
// It runs automatically when Unity starts and saves all open scenes and modified assets
// at a user-defined interval, even when the settings window is closed.
//
// Key Features:
// 1. Runs persistently in the background via [InitializeOnLoad].
// 2. Saves configuration (interval, status) using EditorPrefs, persisting across Unity sessions.
// 3. Uses System.DateTime for accurate time tracking, unaffected by Play Mode reloads.
// 4. Pauses counting down during Play Mode or compilation to prevent accidental saving.
//
// How to Use:
// 1. Place this script in an 'Editor' folder in your project.
// 2. Open the settings window via: Menu -> Tools -> Auto Save Settings.
// 3. Enable "Bật Auto Save" (Enable Auto Save).
// 4. Set the desired "Thời gian (phút)" (Interval in minutes).
// 5. The tool will now save automatically in the background according to the schedule.
// ===============================================================================
using System;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
namespace Editor
{
[InitializeOnLoad] // Critical: Ensures the script runs in the background upon Unity startup
public class AutoSaveTool : EditorWindow
{
// Static Configuration Variables (Persisted via EditorPrefs)
private static bool isAutoSaveEnabled;
private static float saveIntervalMinutes;
private static bool showDebugLog;
// Time Tracking Variable
private static DateTime nextSaveTime;
// Static Constructor: Runs when Unity starts or recompiles
static AutoSaveTool()
{
// 1. Load settings from EditorPrefs (persistent storage)
isAutoSaveEnabled = EditorPrefs.GetBool("AutoSave_Enabled", false);
saveIntervalMinutes = EditorPrefs.GetFloat("AutoSave_Interval", 5f);
showDebugLog = EditorPrefs.GetBool("AutoSave_Log", true);
// 2. Initialize the timer
ResetTimer();
// 3. Register the background update loop
EditorApplication.update += OnEditorUpdate;
}
[MenuItem("Tools/Auto Save Settings")]
public static void ShowWindow()
{
GetWindow<AutoSaveTool>("Auto Save");
}
private void OnGUI()
{
GUILayout.Label("Auto Save Configuration (Runs in Background)", EditorStyles.boldLabel);
EditorGUILayout.Space();
// Begin tracking changes on the GUI
EditorGUI.BeginChangeCheck();
isAutoSaveEnabled = EditorGUILayout.Toggle("Enable Auto Save", isAutoSaveEnabled);
saveIntervalMinutes = EditorGUILayout.FloatField("Interval (Minutes)", saveIntervalMinutes);
showDebugLog = EditorGUILayout.Toggle("Show Debug Log", showDebugLog);
// If any setting changed, save it immediately to EditorPrefs
if (EditorGUI.EndChangeCheck())
{
if (saveIntervalMinutes < 0.5f) saveIntervalMinutes = 0.5f; // Minimum 30 seconds
EditorPrefs.SetBool("AutoSave_Enabled", isAutoSaveEnabled);
EditorPrefs.SetFloat("AutoSave_Interval", saveIntervalMinutes);
EditorPrefs.SetBool("AutoSave_Log", showDebugLog);
ResetTimer(); // Reset timer based on new settings
}
EditorGUILayout.Space();
if (isAutoSaveEnabled)
{
TimeSpan timeRemaining = nextSaveTime - DateTime.Now;
if (timeRemaining.TotalSeconds < 0) timeRemaining = TimeSpan.Zero;
// Display warning if Play Mode or Compiling
if (EditorApplication.isPlaying || EditorApplication.isCompiling)
{
EditorGUILayout.HelpBox("Currently in Play Mode or Compiling. Saving is temporarily paused to prevent errors.", MessageType.Warning);
}
else
{
string timeStr = string.Format("{0:00}:{1:00}", timeRemaining.Minutes, timeRemaining.Seconds);
EditorGUILayout.HelpBox($"System is running in background.\nAuto-saving in: {timeStr}", MessageType.Info);
}
if (GUILayout.Button("Save Now", GUILayout.Height(30)))
{
SaveNow();
}
}
else
{
EditorGUILayout.HelpBox("Auto Save System is currently DISABLED.", MessageType.Error);
}
}
// This function runs continuously in the background
private static void OnEditorUpdate()
{
if (!isAutoSaveEnabled) return;
// If playing or compiling -> PAUSE the countdown (do not reset)
if (EditorApplication.isPlaying || EditorApplication.isCompiling)
{
// Add delta time to nextSaveTime to compensate for time elapsed while paused
nextSaveTime = nextSaveTime.AddSeconds(Time.unscaledDeltaTime);
return;
}
// Check if it's time to save
if (DateTime.Now >= nextSaveTime)
{
SaveNow();
}
// Repaint the GUI if the window is open to keep the countdown smooth
if (HasOpenInstances<AutoSaveTool>())
{
GetWindow<AutoSaveTool>().Repaint();
}
}
private static void ResetTimer()
{
nextSaveTime = DateTime.Now.AddMinutes(saveIntervalMinutes);
}
private static void SaveNow()
{
var currentScene = EditorSceneManager.GetActiveScene();
bool isSaved = false;
// 1. Save Current Scene if it is dirty AND has a path (not Untitled)
if (currentScene.isDirty && !string.IsNullOrEmpty(currentScene.path))
{
EditorSceneManager.SaveOpenScenes();
isSaved = true;
}
// 2. Always save Assets (Prefabs, ScriptableObjects, etc.)
AssetDatabase.SaveAssets();
isSaved = true;
if (isSaved && showDebugLog)
{
Debug.Log($"<color=#00FF00><b>[AutoSave]</b></color> Project saved automatically at {DateTime.Now.ToString("HH:mm:ss")}");
}
ResetTimer();
}
}
}