This commit is contained in:
2026-07-04 18:01:40 +07:00
parent 315c14c651
commit 5b88e6d3c3
75 changed files with 64013 additions and 370482 deletions

View File

@@ -29,32 +29,30 @@ using System;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
namespace Editor
{
[InitializeOnLoad] // Critical: Ensures the script runs in the background upon Unity startup
[InitializeOnLoad]
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()
private Label statusLabel;
private VisualElement progressBar;
[InitializeOnLoadMethod]
static void Init()
{
// 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;
}
@@ -64,83 +62,194 @@ namespace Editor
GetWindow<AutoSaveTool>("Auto Save");
}
private void OnGUI()
public void CreateGUI()
{
GUILayout.Label("Auto Save Configuration (Runs in Background)", EditorStyles.boldLabel);
EditorGUILayout.Space();
VisualElement root = rootVisualElement;
root.style.paddingTop = 15;
root.style.paddingBottom = 15;
root.style.paddingLeft = 15;
root.style.paddingRight = 15;
// 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
Label title = new Label("Auto Save Settings");
title.style.fontSize = 22;
title.style.unityFontStyleAndWeight = FontStyle.Bold;
title.style.color = new StyleColor(new Color(0.3f, 0.7f, 1f));
title.style.marginBottom = 15;
root.Add(title);
// Settings Card
VisualElement settingsCard = CreateCard();
Toggle enableToggle = new Toggle("Enable Auto Save");
enableToggle.value = isAutoSaveEnabled;
enableToggle.RegisterValueChangedCallback(evt => {
isAutoSaveEnabled = evt.newValue;
EditorPrefs.SetBool("AutoSave_Enabled", isAutoSaveEnabled);
ResetTimer();
UpdateUIStatus();
});
enableToggle.style.unityFontStyleAndWeight = FontStyle.Bold;
settingsCard.Add(enableToggle);
Slider intervalSlider = new Slider("Interval (Minutes)", 0.5f, 60f);
intervalSlider.value = saveIntervalMinutes;
intervalSlider.style.marginTop = 15;
intervalSlider.RegisterValueChangedCallback(evt => {
saveIntervalMinutes = evt.newValue;
EditorPrefs.SetFloat("AutoSave_Interval", saveIntervalMinutes);
ResetTimer();
});
// Add a label to show exact slider value
Label intervalLabel = new Label($"Current: {saveIntervalMinutes:F1} min");
intervalLabel.style.alignSelf = Align.FlexEnd;
intervalLabel.style.color = new StyleColor(new Color(0.7f, 0.7f, 0.7f));
intervalSlider.RegisterValueChangedCallback(evt => {
intervalLabel.text = $"Current: {evt.newValue:F1} min";
});
settingsCard.Add(intervalSlider);
settingsCard.Add(intervalLabel);
Toggle logToggle = new Toggle("Show Debug Log");
logToggle.value = showDebugLog;
logToggle.style.marginTop = 10;
logToggle.RegisterValueChangedCallback(evt => {
showDebugLog = evt.newValue;
EditorPrefs.SetBool("AutoSave_Log", showDebugLog);
});
settingsCard.Add(logToggle);
root.Add(settingsCard);
ResetTimer(); // Reset timer based on new settings
}
// Status Card
VisualElement statusCard = CreateCard();
statusCard.style.marginTop = 15;
Label statusTitle = new Label("System Status");
statusTitle.style.fontSize = 16;
statusTitle.style.unityFontStyleAndWeight = FontStyle.Bold;
statusTitle.style.marginBottom = 15;
statusCard.Add(statusTitle);
EditorGUILayout.Space();
statusLabel = new Label();
statusLabel.style.fontSize = 15;
statusLabel.style.marginBottom = 10;
statusCard.Add(statusLabel);
if (isAutoSaveEnabled)
{
TimeSpan timeRemaining = nextSaveTime - DateTime.Now;
if (timeRemaining.TotalSeconds < 0) timeRemaining = TimeSpan.Zero;
// Progress bar
VisualElement progressBg = new VisualElement();
progressBg.style.height = 12;
progressBg.style.backgroundColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f));
progressBg.style.borderTopLeftRadius = 6;
progressBg.style.borderTopRightRadius = 6;
progressBg.style.borderBottomLeftRadius = 6;
progressBg.style.borderBottomRightRadius = 6;
progressBg.style.marginBottom = 20;
progressBg.style.overflow = Overflow.Hidden;
// 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);
}
progressBar = new VisualElement();
progressBar.style.height = 12;
progressBar.style.backgroundColor = new StyleColor(new Color(0.3f, 0.7f, 1f));
progressBar.style.width = Length.Percent(0);
progressBg.Add(progressBar);
if (GUILayout.Button("Save Now", GUILayout.Height(30)))
{
SaveNow();
}
}
else
{
EditorGUILayout.HelpBox("Auto Save System is currently DISABLED.", MessageType.Error);
}
statusCard.Add(progressBg);
Button saveNowBtn = new Button(() => SaveNow());
saveNowBtn.text = "Force Save Now";
saveNowBtn.style.height = 40;
saveNowBtn.style.backgroundColor = new StyleColor(new Color(0.2f, 0.6f, 0.3f));
saveNowBtn.style.color = new StyleColor(Color.white);
saveNowBtn.style.fontSize = 14;
saveNowBtn.style.unityFontStyleAndWeight = FontStyle.Bold;
saveNowBtn.style.borderTopLeftRadius = 6;
saveNowBtn.style.borderTopRightRadius = 6;
saveNowBtn.style.borderBottomLeftRadius = 6;
saveNowBtn.style.borderBottomRightRadius = 6;
statusCard.Add(saveNowBtn);
root.Add(statusCard);
// Periodically update the countdown text and progress bar
root.schedule.Execute(UpdateUIStatus).Every(100);
UpdateUIStatus();
root.Add(ScovySignature.CreateSignatureBox());
}
private VisualElement CreateCard()
{
VisualElement card = new VisualElement();
card.style.backgroundColor = new StyleColor(new Color(0.18f, 0.18f, 0.18f, 0.9f));
card.style.borderTopLeftRadius = 10;
card.style.borderTopRightRadius = 10;
card.style.borderBottomLeftRadius = 10;
card.style.borderBottomRightRadius = 10;
card.style.paddingTop = 15;
card.style.paddingBottom = 15;
card.style.paddingLeft = 15;
card.style.paddingRight = 15;
card.style.borderTopWidth = 1;
card.style.borderBottomWidth = 1;
card.style.borderLeftWidth = 1;
card.style.borderRightWidth = 1;
card.style.borderTopColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderBottomColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderLeftColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderRightColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
return card;
}
private void UpdateUIStatus()
{
if (statusLabel == null || progressBar == null) return;
if (!isAutoSaveEnabled)
{
statusLabel.text = "Auto Save is DISABLED.";
statusLabel.style.color = new StyleColor(new Color(0.85f, 0.35f, 0.35f));
progressBar.style.width = Length.Percent(0);
return;
}
if (EditorApplication.isPlaying || EditorApplication.isCompiling)
{
statusLabel.text = "PAUSED (Play Mode / Compiling)";
statusLabel.style.color = new StyleColor(new Color(0.9f, 0.75f, 0.2f));
progressBar.style.backgroundColor = new StyleColor(new Color(0.9f, 0.75f, 0.2f));
return;
}
TimeSpan timeRemaining = nextSaveTime - DateTime.Now;
if (timeRemaining.TotalSeconds < 0) timeRemaining = TimeSpan.Zero;
string timeStr = string.Format("{0:00}:{1:00}", timeRemaining.Minutes, timeRemaining.Seconds);
statusLabel.text = $"Auto-saving in: {timeStr}";
statusLabel.style.color = new StyleColor(new Color(0.6f, 0.8f, 1f));
progressBar.style.backgroundColor = new StyleColor(new Color(0.3f, 0.7f, 1f));
float totalSeconds = saveIntervalMinutes * 60f;
float elapsed = totalSeconds - (float)timeRemaining.TotalSeconds;
float percent = Mathf.Clamp01(elapsed / totalSeconds) * 100f;
progressBar.style.width = Length.Percent(percent);
}
// 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()
@@ -151,17 +260,14 @@ namespace Editor
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;

View File

@@ -22,6 +22,8 @@ using UnityEditor;
using UnityEngine;
using System.Globalization;
using System;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
namespace Editor
{
@@ -33,8 +35,9 @@ namespace Editor
private static EventModifiers loadModifier;
private static KeyCode[] slotKeys = new KeyCode[9];
// Static constructor runs automatically when Unity loads or recompiles
static CameraBookmarksTool()
// Replaced static constructor with InitializeOnLoadMethod to avoid EditorPrefs exceptions
[InitializeOnLoadMethod]
static void Init()
{
LoadSettings();
SceneView.duringSceneGui += OnSceneGUI;
@@ -69,52 +72,130 @@ namespace Editor
}
}
private void OnGUI()
public void CreateGUI()
{
GUILayout.Label("Shortcut Configuration", EditorStyles.boldLabel);
EditorGUILayout.Space();
VisualElement root = rootVisualElement;
root.style.paddingTop = 15;
root.style.paddingBottom = 15;
root.style.paddingLeft = 15;
root.style.paddingRight = 15;
EditorGUI.BeginChangeCheck();
Label title = new Label("Shortcut Configuration");
title.style.fontSize = 20;
title.style.unityFontStyleAndWeight = FontStyle.Bold;
title.style.color = new StyleColor(new Color(0.3f, 0.7f, 1f));
title.style.marginBottom = 15;
root.Add(title);
// Modifier Keys Setup
saveModifier = (EventModifiers)EditorGUILayout.EnumFlagsField("Save Modifier", saveModifier);
loadModifier = (EventModifiers)EditorGUILayout.EnumFlagsField("Load Modifier", loadModifier);
// Warning Box
VisualElement warningBox = new VisualElement();
warningBox.style.backgroundColor = new StyleColor(new Color(0.8f, 0.6f, 0.1f, 0.3f));
warningBox.style.borderLeftWidth = 4;
warningBox.style.borderLeftColor = new StyleColor(new Color(0.9f, 0.7f, 0.1f));
warningBox.style.paddingTop = 10;
warningBox.style.paddingBottom = 10;
warningBox.style.paddingLeft = 10;
warningBox.style.marginBottom = 15;
warningBox.style.display = DisplayStyle.None;
Label warningLabel = new Label("Warning: Save and Load modifiers are the SAME! This will cause conflicts.");
warningLabel.style.color = new StyleColor(new Color(0.9f, 0.8f, 0.5f));
warningLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
warningBox.Add(warningLabel);
root.Add(warningBox);
if (saveModifier == loadModifier)
{
EditorGUILayout.HelpBox("Warning: Save and Load modifiers are the SAME! This will cause conflicts.", MessageType.Warning);
}
// Modifiers Card
VisualElement modifiersCard = CreateCard();
EnumFlagsField saveField = new EnumFlagsField("Save Modifier", saveModifier);
saveField.RegisterValueChangedCallback(evt => {
saveModifier = (EventModifiers)evt.newValue;
SaveSettings();
warningBox.style.display = (saveModifier == loadModifier) ? DisplayStyle.Flex : DisplayStyle.None;
});
modifiersCard.Add(saveField);
EditorGUILayout.Space();
GUILayout.Label("Slot Keys Assignment", EditorStyles.boldLabel);
EnumFlagsField loadField = new EnumFlagsField("Load Modifier", loadModifier);
loadField.RegisterValueChangedCallback(evt => {
loadModifier = (EventModifiers)evt.newValue;
SaveSettings();
warningBox.style.display = (saveModifier == loadModifier) ? DisplayStyle.Flex : DisplayStyle.None;
});
loadField.style.marginTop = 10;
modifiersCard.Add(loadField);
warningBox.style.display = (saveModifier == loadModifier) ? DisplayStyle.Flex : DisplayStyle.None;
root.Add(modifiersCard);
// KeyCode Setup for 9 slots
// Slots Section
Label slotsTitle = new Label("Slot Keys Assignment");
slotsTitle.style.fontSize = 16;
slotsTitle.style.unityFontStyleAndWeight = FontStyle.Bold;
slotsTitle.style.color = new StyleColor(new Color(0.8f, 0.8f, 0.8f));
slotsTitle.style.marginTop = 15;
slotsTitle.style.marginBottom = 10;
root.Add(slotsTitle);
VisualElement slotsCard = CreateCard();
for (int i = 0; i < 9; i++)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label($"Slot {i + 1}", GUILayout.Width(100));
slotKeys[i] = (KeyCode)EditorGUILayout.EnumPopup(slotKeys[i]);
EditorGUILayout.EndHorizontal();
int index = i;
EnumField slotField = new EnumField($"Slot {i + 1}", slotKeys[i]);
slotField.RegisterValueChangedCallback(evt => {
slotKeys[index] = (KeyCode)evt.newValue;
SaveSettings();
});
if (i > 0) slotField.style.marginTop = 8;
slotsCard.Add(slotField);
}
if (EditorGUI.EndChangeCheck())
{
SaveSettings(); // Save immediately if anything changes
}
EditorGUILayout.Space();
EditorGUILayout.Space();
root.Add(slotsCard);
// Reset Button
if (GUILayout.Button("Reset to Default Settings", GUILayout.Height(30)))
{
Button resetBtn = new Button(() => {
saveModifier = EventModifiers.Control;
loadModifier = EventModifiers.Shift;
for (int i = 0; i < 9; i++) slotKeys[i] = KeyCode.Alpha1 + i;
SaveSettings();
GUI.FocusControl(null); // Remove focus to refresh UI correctly
}
root.Clear();
CreateGUI(); // Rebuild
});
resetBtn.text = "Reset to Default Settings";
resetBtn.style.height = 40;
resetBtn.style.marginTop = 20;
resetBtn.style.backgroundColor = new StyleColor(new Color(0.7f, 0.3f, 0.3f));
resetBtn.style.color = new StyleColor(Color.white);
resetBtn.style.unityFontStyleAndWeight = FontStyle.Bold;
resetBtn.style.borderTopLeftRadius = 6;
resetBtn.style.borderTopRightRadius = 6;
resetBtn.style.borderBottomLeftRadius = 6;
resetBtn.style.borderBottomRightRadius = 6;
root.Add(resetBtn);
root.Add(ScovySignature.CreateSignatureBox());
}
private VisualElement CreateCard()
{
VisualElement card = new VisualElement();
card.style.backgroundColor = new StyleColor(new Color(0.18f, 0.18f, 0.18f, 0.9f));
card.style.borderTopLeftRadius = 8;
card.style.borderTopRightRadius = 8;
card.style.borderBottomLeftRadius = 8;
card.style.borderBottomRightRadius = 8;
card.style.paddingTop = 15;
card.style.paddingBottom = 15;
card.style.paddingLeft = 15;
card.style.paddingRight = 15;
card.style.borderTopWidth = 1;
card.style.borderBottomWidth = 1;
card.style.borderLeftWidth = 1;
card.style.borderRightWidth = 1;
card.style.borderTopColor = new StyleColor(new Color(0.25f, 0.25f, 0.25f));
card.style.borderBottomColor = new StyleColor(new Color(0.25f, 0.25f, 0.25f));
card.style.borderLeftColor = new StyleColor(new Color(0.25f, 0.25f, 0.25f));
card.style.borderRightColor = new StyleColor(new Color(0.25f, 0.25f, 0.25f));
return card;
}
static void OnSceneGUI(SceneView view)

View File

@@ -0,0 +1,454 @@
using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
[CustomEditor(typeof(CameraJumpPoints))]
public class CameraJumpPointsEditor : UnityEditor.Editor
{
private static CameraJumpPoints[] allPoints;
// Store previous pose
private static Vector3 previousPosition;
private static Quaternion previousRotation;
private static bool hasPreviousPose = false;
private static Transform currentJumpTarget = null;
[InitializeOnLoadMethod]
static void Init()
{
SceneView.duringSceneGui += OnSceneGUIStatic;
EditorApplication.hierarchyChanged += OnHierarchyChanged;
}
static void OnHierarchyChanged()
{
allPoints = Object.FindObjectsOfType<CameraJumpPoints>();
}
static void OnSceneGUIStatic(SceneView sceneView)
{
Event e = Event.current;
if (e.type == EventType.KeyDown && e.keyCode != KeyCode.None)
{
if (allPoints == null)
{
allPoints = Object.FindObjectsOfType<CameraJumpPoints>();
}
bool needsRefresh = false;
foreach (var cp in allPoints)
{
if (cp == null)
{
needsRefresh = true;
continue;
}
foreach (var pt in cp.points)
{
if (pt.key == e.keyCode && pt.ctrl == e.control && pt.alt == e.alt && pt.shift == e.shift)
{
if (pt.target == null) continue;
if (currentJumpTarget == pt.target && hasPreviousPose)
{
// Jump back
sceneView.pivot = previousPosition;
sceneView.rotation = previousRotation;
currentJumpTarget = null;
hasPreviousPose = false;
sceneView.Repaint();
}
else
{
// Save previous pose
previousPosition = sceneView.pivot;
previousRotation = sceneView.rotation;
hasPreviousPose = true;
currentJumpTarget = pt.target;
// Jump to target exactly matching camera position
sceneView.rotation = pt.target.rotation;
sceneView.pivot = pt.target.position + pt.target.forward * sceneView.cameraDistance;
sceneView.Repaint();
}
e.Use();
return;
}
}
}
if (needsRefresh)
{
allPoints = Object.FindObjectsOfType<CameraJumpPoints>();
}
}
// Lock camera movement when in View Mode
if (hasPreviousPose && currentJumpTarget != null)
{
bool isNavigating = false;
if (e.type == EventType.KeyDown)
{
if (e.keyCode == KeyCode.W || e.keyCode == KeyCode.A || e.keyCode == KeyCode.S || e.keyCode == KeyCode.D ||
e.keyCode == KeyCode.Q || e.keyCode == KeyCode.E ||
e.keyCode == KeyCode.UpArrow || e.keyCode == KeyCode.DownArrow ||
e.keyCode == KeyCode.LeftArrow || e.keyCode == KeyCode.RightArrow)
{
isNavigating = true;
}
}
else if (e.type == EventType.MouseDown || e.type == EventType.MouseDrag)
{
// Right click (1), Middle click (2), or Alt+Left click (orbit)
if (e.button == 1 || e.button == 2 || (e.button == 0 && e.alt))
{
isNavigating = true;
}
}
else if (e.type == EventType.ScrollWheel)
{
isNavigating = true;
}
if (isNavigating)
{
string hotkeyStr = "your shortcut";
if (allPoints != null)
{
foreach (var cp in allPoints)
{
if (cp == null) continue;
foreach (var pt in cp.points)
{
if (pt.target == currentJumpTarget)
{
hotkeyStr = pt.GetKeyString();
break;
}
}
}
}
sceneView.ShowNotification(new GUIContent($"View Mode Locked. Press {hotkeyStr} again to escape."));
e.Use();
}
}
}
public override VisualElement CreateInspectorGUI()
{
VisualElement root = new VisualElement();
root.style.paddingTop = 15;
root.style.paddingBottom = 15;
root.style.paddingLeft = 10;
root.style.paddingRight = 10;
// Title
Label title = new Label("Camera Bookmarks");
title.style.fontSize = 22;
title.style.unityFontStyleAndWeight = FontStyle.Bold;
title.style.marginBottom = 15;
title.style.color = new StyleColor(new Color(0.3f, 0.7f, 1f));
root.Add(title);
// Help Box Custom
VisualElement helpBox = new VisualElement();
helpBox.style.backgroundColor = new StyleColor(new Color(0.15f, 0.15f, 0.15f, 0.8f));
helpBox.style.borderLeftWidth = 4;
helpBox.style.borderLeftColor = new StyleColor(new Color(0.3f, 0.7f, 1f));
helpBox.style.borderTopRightRadius = 6;
helpBox.style.borderBottomRightRadius = 6;
helpBox.style.paddingTop = 12;
helpBox.style.paddingBottom = 12;
helpBox.style.paddingLeft = 12;
helpBox.style.paddingRight = 12;
helpBox.style.marginBottom = 20;
Label helpText = new Label("• Jump instantly via hotkeys in Scene View.\n• Press the exact same hotkey again to return.\n• Use 'Capture Current View' for quick setup.");
helpText.style.whiteSpace = WhiteSpace.Normal;
helpText.style.color = new StyleColor(new Color(0.85f, 0.85f, 0.85f));
helpBox.Add(helpText);
root.Add(helpBox);
// List Container
VisualElement listContainer = new VisualElement();
root.Add(listContainer);
SerializedProperty pointsProp = serializedObject.FindProperty("points");
System.Action rebuildList = null;
rebuildList = () => {
listContainer.Clear();
serializedObject.Update();
for (int i = 0; i < pointsProp.arraySize; i++)
{
int index = i;
SerializedProperty pointProp = pointsProp.GetArrayElementAtIndex(index);
// Card UI
VisualElement card = new VisualElement();
card.style.backgroundColor = new StyleColor(new Color(0.2f, 0.2f, 0.2f, 0.9f));
card.style.borderTopLeftRadius = 8;
card.style.borderTopRightRadius = 8;
card.style.borderBottomLeftRadius = 8;
card.style.borderBottomRightRadius = 8;
card.style.paddingTop = 15;
card.style.paddingBottom = 15;
card.style.paddingLeft = 15;
card.style.paddingRight = 15;
card.style.marginBottom = 12;
// Subtle shadow/border
card.style.borderTopWidth = 1;
card.style.borderBottomWidth = 1;
card.style.borderLeftWidth = 1;
card.style.borderRightWidth = 1;
card.style.borderTopColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderBottomColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderLeftColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderRightColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
// Header
VisualElement header = new VisualElement();
header.style.flexDirection = FlexDirection.Row;
header.style.justifyContent = Justify.SpaceBetween;
header.style.marginBottom = 12;
PropertyField nameField = new PropertyField(pointProp.FindPropertyRelative("name"), "");
nameField.style.flexGrow = 1;
nameField.style.unityFontStyleAndWeight = FontStyle.Bold;
Button deleteBtn = new Button(() => {
pointsProp.DeleteArrayElementAtIndex(index);
serializedObject.ApplyModifiedProperties();
rebuildList();
});
deleteBtn.text = "✕";
deleteBtn.style.backgroundColor = new StyleColor(new Color(0.8f, 0.25f, 0.25f, 0.9f));
deleteBtn.style.color = new StyleColor(Color.white);
deleteBtn.style.borderTopLeftRadius = 4;
deleteBtn.style.borderTopRightRadius = 4;
deleteBtn.style.borderBottomLeftRadius = 4;
deleteBtn.style.borderBottomRightRadius = 4;
deleteBtn.style.width = 24;
deleteBtn.style.height = 24;
deleteBtn.style.marginLeft = 15;
deleteBtn.style.unityFontStyleAndWeight = FontStyle.Bold;
header.Add(nameField);
header.Add(deleteBtn);
card.Add(header);
// Target Row with Sync button
VisualElement targetRow = new VisualElement();
targetRow.style.flexDirection = FlexDirection.Row;
targetRow.style.marginBottom = 12;
PropertyField targetField = new PropertyField(pointProp.FindPropertyRelative("target"), "Target Object");
targetField.style.flexGrow = 1;
targetRow.Add(targetField);
Button syncBtn = new Button(() => {
SceneView sv = SceneView.lastActiveSceneView;
if (sv != null && sv.camera != null) {
Transform t = (Transform)pointProp.FindPropertyRelative("target").objectReferenceValue;
if (t != null) {
Undo.RecordObject(t, "Sync Target View");
t.position = sv.camera.transform.position;
t.rotation = sv.camera.transform.rotation;
}
}
});
syncBtn.text = "Sync to Scene";
syncBtn.tooltip = "Updates this object's transform to match your current Scene View.";
syncBtn.style.marginLeft = 10;
syncBtn.style.paddingLeft = 8;
syncBtn.style.paddingRight = 8;
syncBtn.style.borderTopLeftRadius = 4;
syncBtn.style.borderTopRightRadius = 4;
syncBtn.style.borderBottomLeftRadius = 4;
syncBtn.style.borderBottomRightRadius = 4;
syncBtn.style.backgroundColor = new StyleColor(new Color(0.3f, 0.3f, 0.3f));
targetRow.Add(syncBtn);
card.Add(targetRow);
// Key binding Row
VisualElement keyRow = new VisualElement();
keyRow.style.flexDirection = FlexDirection.Row;
keyRow.style.alignItems = Align.Center;
Label keyLabel = new Label("Shortcut Key");
keyLabel.style.width = 120;
keyRow.Add(keyLabel);
Button recordBtn = new Button();
recordBtn.style.flexGrow = 1;
recordBtn.style.height = 30;
recordBtn.style.borderTopLeftRadius = 6;
recordBtn.style.borderTopRightRadius = 6;
recordBtn.style.borderBottomLeftRadius = 6;
recordBtn.style.borderBottomRightRadius = 6;
recordBtn.style.unityFontStyleAndWeight = FontStyle.Bold;
System.Action updateBtnText = () => {
SerializedProperty keyP = pointProp.FindPropertyRelative("key");
SerializedProperty cP = pointProp.FindPropertyRelative("ctrl");
SerializedProperty aP = pointProp.FindPropertyRelative("alt");
SerializedProperty sP = pointProp.FindPropertyRelative("shift");
if ((KeyCode)keyP.intValue == KeyCode.None) {
recordBtn.text = "Not Set (Click to Record)";
recordBtn.style.backgroundColor = new StyleColor(new Color(0.3f, 0.3f, 0.3f));
} else {
string s = "";
if (cP.boolValue) s += "Ctrl + ";
if (aP.boolValue) s += "Alt + ";
if (sP.boolValue) s += "Shift + ";
s += ((KeyCode)keyP.intValue).ToString();
recordBtn.text = s;
recordBtn.style.backgroundColor = new StyleColor(new Color(0.15f, 0.45f, 0.8f));
}
};
updateBtnText();
bool isRecording = false;
recordBtn.clicked += () => {
if (isRecording) {
isRecording = false;
updateBtnText();
return;
}
isRecording = true;
recordBtn.text = "Press any key... (ESC to cancel)";
recordBtn.style.backgroundColor = new StyleColor(new Color(0.8f, 0.4f, 0.1f));
recordBtn.Focus();
};
recordBtn.RegisterCallback<KeyDownEvent>((evt) => {
if (!isRecording) return;
if (evt.keyCode == KeyCode.Escape) {
isRecording = false;
updateBtnText();
evt.StopPropagation();
return;
}
if (evt.keyCode != KeyCode.None &&
evt.keyCode != KeyCode.LeftAlt && evt.keyCode != KeyCode.RightAlt &&
evt.keyCode != KeyCode.LeftControl && evt.keyCode != KeyCode.RightControl &&
evt.keyCode != KeyCode.LeftShift && evt.keyCode != KeyCode.RightShift &&
evt.keyCode != KeyCode.LeftCommand && evt.keyCode != KeyCode.RightCommand)
{
pointProp.FindPropertyRelative("key").intValue = (int)evt.keyCode;
pointProp.FindPropertyRelative("ctrl").boolValue = evt.ctrlKey;
pointProp.FindPropertyRelative("alt").boolValue = evt.altKey;
pointProp.FindPropertyRelative("shift").boolValue = evt.shiftKey;
serializedObject.ApplyModifiedProperties();
isRecording = false;
updateBtnText();
evt.StopPropagation();
}
});
recordBtn.focusable = true;
keyRow.Add(recordBtn);
card.Add(keyRow);
listContainer.Add(card);
}
// Buttons layout
VisualElement buttonsRow = new VisualElement();
buttonsRow.style.flexDirection = FlexDirection.Row;
buttonsRow.style.marginTop = 15;
// Capture Current View Button
Button captureBtn = new Button(() => {
SceneView sv = SceneView.lastActiveSceneView;
if (sv != null && sv.camera != null) {
GameObject go = new GameObject("Camera Point " + (pointsProp.arraySize + 1));
CameraJumpPoints tgt = (CameraJumpPoints)target;
go.transform.SetParent(tgt.transform);
go.transform.position = sv.camera.transform.position;
go.transform.rotation = sv.camera.transform.rotation;
Undo.RegisterCreatedObjectUndo(go, "Capture Camera Point");
pointsProp.arraySize++;
SerializedProperty newElem = pointsProp.GetArrayElementAtIndex(pointsProp.arraySize - 1);
newElem.FindPropertyRelative("name").stringValue = go.name;
newElem.FindPropertyRelative("target").objectReferenceValue = go.transform;
newElem.FindPropertyRelative("key").intValue = (int)KeyCode.None;
newElem.FindPropertyRelative("ctrl").boolValue = false;
newElem.FindPropertyRelative("alt").boolValue = false;
newElem.FindPropertyRelative("shift").boolValue = false;
serializedObject.ApplyModifiedProperties();
rebuildList();
} else {
Debug.LogWarning("No active Scene View found to capture.");
}
});
captureBtn.text = "+ Capture Scene View";
captureBtn.style.flexGrow = 1;
captureBtn.style.height = 40;
captureBtn.style.fontSize = 14;
captureBtn.style.unityFontStyleAndWeight = FontStyle.Bold;
captureBtn.style.backgroundColor = new StyleColor(new Color(0.25f, 0.65f, 0.4f));
captureBtn.style.color = new StyleColor(Color.white);
captureBtn.style.borderTopLeftRadius = 8;
captureBtn.style.borderTopRightRadius = 8;
captureBtn.style.borderBottomLeftRadius = 8;
captureBtn.style.borderBottomRightRadius = 8;
captureBtn.style.marginRight = 5;
// Add Empty Button
Button addEmptyBtn = new Button(() => {
pointsProp.arraySize++;
SerializedProperty newElem = pointsProp.GetArrayElementAtIndex(pointsProp.arraySize - 1);
newElem.FindPropertyRelative("name").stringValue = "New Empty " + pointsProp.arraySize;
newElem.FindPropertyRelative("target").objectReferenceValue = null;
newElem.FindPropertyRelative("key").intValue = (int)KeyCode.None;
newElem.FindPropertyRelative("ctrl").boolValue = false;
newElem.FindPropertyRelative("alt").boolValue = false;
newElem.FindPropertyRelative("shift").boolValue = false;
serializedObject.ApplyModifiedProperties();
rebuildList();
});
addEmptyBtn.text = "Add Empty";
addEmptyBtn.style.width = 100;
addEmptyBtn.style.height = 40;
addEmptyBtn.style.borderTopLeftRadius = 8;
addEmptyBtn.style.borderTopRightRadius = 8;
addEmptyBtn.style.borderBottomLeftRadius = 8;
addEmptyBtn.style.borderBottomRightRadius = 8;
buttonsRow.Add(captureBtn);
buttonsRow.Add(addEmptyBtn);
listContainer.Add(buttonsRow);
};
rebuildList();
// Listen for Unity serialization changes (e.g. Undo/Redo)
root.TrackPropertyValue(pointsProp, (prop) => {
rebuildList();
});
root.Add(Editor.ScovySignature.CreateSignatureBox());
return root;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6418c2e4db0af4342add5271469ded58

View File

@@ -37,7 +37,7 @@ namespace Editor
private float maxTilt = 5f;
private bool uniformScale = true;
[MenuItem("Tools/Level Decorator (Chaos Maker)")]
[MenuItem("Tools/Level Decorator")]
public static void ShowWindow()
{
GetWindow<LevelDecorator>("Chaos Maker");

View File

@@ -26,6 +26,8 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
namespace Editor
{
@@ -40,8 +42,8 @@ namespace Editor
private static EventModifiers playModifier;
private static KeyCode playKey;
// Static constructor runs automatically when Unity loads or recompiles
static PlayFromHereTool()
[InitializeOnLoadMethod]
static void Init()
{
LoadSettings();
// Subscribe to SceneView GUI to listen for keyboard inputs
@@ -72,52 +74,149 @@ namespace Editor
EditorPrefs.SetInt("PFH_PlayKey", (int)playKey);
}
private void OnGUI()
public void CreateGUI()
{
GUILayout.Label("Shortcut Configuration", EditorStyles.boldLabel);
EditorGUILayout.Space();
VisualElement root = rootVisualElement;
root.style.paddingTop = 15;
root.style.paddingBottom = 15;
root.style.paddingLeft = 15;
root.style.paddingRight = 15;
EditorGUI.BeginChangeCheck();
Label title = new Label("Shortcut Configuration");
title.style.fontSize = 22;
title.style.unityFontStyleAndWeight = FontStyle.Bold;
title.style.color = new StyleColor(new Color(0.3f, 0.7f, 1f));
title.style.marginBottom = 15;
root.Add(title);
// Section for Mode 1
EditorGUILayout.BeginVertical("box");
GUILayout.Label("Mode 1: Teleport Only", EditorStyles.boldLabel);
teleportModifier = (EventModifiers)EditorGUILayout.EnumFlagsField("Modifier Keys", teleportModifier);
teleportKey = (KeyCode)EditorGUILayout.EnumPopup("Main Key", teleportKey);
EditorGUILayout.EndVertical();
// Warning Box for Conflicts
VisualElement warningBox = new VisualElement();
warningBox.style.backgroundColor = new StyleColor(new Color(0.8f, 0.2f, 0.2f, 0.3f));
warningBox.style.borderLeftWidth = 4;
warningBox.style.borderLeftColor = new StyleColor(new Color(0.9f, 0.3f, 0.3f));
warningBox.style.paddingTop = 10;
warningBox.style.paddingBottom = 10;
warningBox.style.paddingLeft = 10;
warningBox.style.marginBottom = 15;
warningBox.style.display = DisplayStyle.None;
Label warningLabel = new Label("Conflict: Mode 1 and Mode 2 have the same hotkey!");
warningLabel.style.color = new StyleColor(new Color(0.9f, 0.5f, 0.5f));
warningLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
warningBox.Add(warningLabel);
root.Add(warningBox);
EditorGUILayout.Space();
System.Action checkConflicts = () => {
bool conflict = (teleportModifier == playModifier && teleportKey == playKey);
warningBox.style.display = conflict ? DisplayStyle.Flex : DisplayStyle.None;
};
// Section for Mode 2
EditorGUILayout.BeginVertical("box");
GUILayout.Label("Mode 2: Play From Here (Teleport + Play)", EditorStyles.boldLabel);
playModifier = (EventModifiers)EditorGUILayout.EnumFlagsField("Modifier Keys", playModifier);
playKey = (KeyCode)EditorGUILayout.EnumPopup("Main Key", playKey);
EditorGUILayout.EndVertical();
// Mode 1 Card
Label mode1Title = new Label("Mode 1: Teleport Only");
mode1Title.style.fontSize = 16;
mode1Title.style.unityFontStyleAndWeight = FontStyle.Bold;
mode1Title.style.color = new StyleColor(new Color(0.8f, 0.8f, 0.8f));
mode1Title.style.marginBottom = 10;
root.Add(mode1Title);
if (EditorGUI.EndChangeCheck())
{
VisualElement card1 = CreateCard();
EnumFlagsField tMod = new EnumFlagsField("Modifier Keys", teleportModifier);
tMod.RegisterValueChangedCallback(evt => {
teleportModifier = (EventModifiers)evt.newValue;
SaveSettings();
}
checkConflicts();
});
card1.Add(tMod);
// Conflict warning
if (teleportModifier == playModifier && teleportKey == playKey)
{
EditorGUILayout.HelpBox("Conflict: Mode 1 and Mode 2 have the same hotkey!", MessageType.Error);
}
EnumField tKey = new EnumField("Main Key", teleportKey);
tKey.RegisterValueChangedCallback(evt => {
teleportKey = (KeyCode)evt.newValue;
SaveSettings();
checkConflicts();
});
tKey.style.marginTop = 10;
card1.Add(tKey);
root.Add(card1);
EditorGUILayout.Space();
EditorGUILayout.Space();
// Mode 2 Card
Label mode2Title = new Label("Mode 2: Play From Here (Teleport + Play)");
mode2Title.style.fontSize = 16;
mode2Title.style.unityFontStyleAndWeight = FontStyle.Bold;
mode2Title.style.color = new StyleColor(new Color(0.8f, 0.8f, 0.8f));
mode2Title.style.marginTop = 15;
mode2Title.style.marginBottom = 10;
root.Add(mode2Title);
if (GUILayout.Button("Reset to Defaults", GUILayout.Height(30)))
{
VisualElement card2 = CreateCard();
EnumFlagsField pMod = new EnumFlagsField("Modifier Keys", playModifier);
pMod.RegisterValueChangedCallback(evt => {
playModifier = (EventModifiers)evt.newValue;
SaveSettings();
checkConflicts();
});
card2.Add(pMod);
EnumField pKey = new EnumField("Main Key", playKey);
pKey.RegisterValueChangedCallback(evt => {
playKey = (KeyCode)evt.newValue;
SaveSettings();
checkConflicts();
});
pKey.style.marginTop = 10;
card2.Add(pKey);
root.Add(card2);
checkConflicts();
// Reset Button
Button resetBtn = new Button(() => {
teleportModifier = EventModifiers.Control | EventModifiers.Alt;
teleportKey = KeyCode.P;
playModifier = EventModifiers.Control | EventModifiers.Alt | EventModifiers.Shift;
playKey = KeyCode.P;
SaveSettings();
GUI.FocusControl(null);
}
root.Clear();
CreateGUI(); // Rebuild
});
resetBtn.text = "Reset to Defaults";
resetBtn.style.height = 40;
resetBtn.style.marginTop = 20;
resetBtn.style.backgroundColor = new StyleColor(new Color(0.7f, 0.3f, 0.3f));
resetBtn.style.color = new StyleColor(Color.white);
resetBtn.style.fontSize = 14;
resetBtn.style.unityFontStyleAndWeight = FontStyle.Bold;
resetBtn.style.borderTopLeftRadius = 6;
resetBtn.style.borderTopRightRadius = 6;
resetBtn.style.borderBottomLeftRadius = 6;
resetBtn.style.borderBottomRightRadius = 6;
root.Add(resetBtn);
root.Add(ScovySignature.CreateSignatureBox());
}
private VisualElement CreateCard()
{
VisualElement card = new VisualElement();
card.style.backgroundColor = new StyleColor(new Color(0.18f, 0.18f, 0.18f, 0.9f));
card.style.borderTopLeftRadius = 10;
card.style.borderTopRightRadius = 10;
card.style.borderBottomLeftRadius = 10;
card.style.borderBottomRightRadius = 10;
card.style.paddingTop = 15;
card.style.paddingBottom = 15;
card.style.paddingLeft = 15;
card.style.paddingRight = 15;
card.style.borderTopWidth = 1;
card.style.borderBottomWidth = 1;
card.style.borderLeftWidth = 1;
card.style.borderRightWidth = 1;
card.style.borderTopColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderBottomColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderLeftColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderRightColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
return card;
}
private static void OnSceneGUI(SceneView view)

View File

@@ -26,6 +26,8 @@ using UnityEditor;
using UnityEngine;
using UnityEditor.SceneManagement;
using System.IO;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
namespace Editor
{
@@ -41,55 +43,69 @@ namespace Editor
window.minSize = new Vector2(300, 450);
}
private void OnGUI()
public void CreateGUI()
{
EditorGUILayout.Space();
VisualElement root = rootVisualElement;
root.style.paddingTop = 15;
root.style.paddingBottom = 15;
root.style.paddingLeft = 15;
root.style.paddingRight = 15;
DrawPlaySection();
EditorGUILayout.Space(15);
DrawSceneNavigation();
EditorGUILayout.Space(15);
DrawDataManagement();
}
// Header
Label title = new Label("Project Dashboard");
title.style.fontSize = 22;
title.style.unityFontStyleAndWeight = FontStyle.Bold;
title.style.color = new StyleColor(new Color(0.3f, 0.7f, 1f));
title.style.marginBottom = 15;
root.Add(title);
private void DrawPlaySection()
{
GUILayout.Label("QUICK PLAY", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical("box");
// Quick Play Card
VisualElement playCard = CreateCard();
Label playTitle = new Label("QUICK PLAY");
playTitle.style.unityFontStyleAndWeight = FontStyle.Bold;
playTitle.style.marginBottom = 10;
playCard.Add(playTitle);
// Green Play Button
Color oldColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(0.4f, 1f, 0.4f); // Light Green
Button playBtn = new Button(() => PlayFromBootScene());
playBtn.text = "▶ PLAY GAME (From Boot Scene)";
playBtn.style.height = 40;
playBtn.style.backgroundColor = new StyleColor(new Color(0.2f, 0.6f, 0.3f));
playBtn.style.color = new StyleColor(Color.white);
playBtn.style.unityFontStyleAndWeight = FontStyle.Bold;
playBtn.style.borderTopLeftRadius = 6;
playBtn.style.borderTopRightRadius = 6;
playBtn.style.borderBottomLeftRadius = 6;
playBtn.style.borderBottomRightRadius = 6;
playCard.Add(playBtn);
if (GUILayout.Button("▶ PLAY GAME (From Boot Scene)", GUILayout.Height(40)))
{
PlayFromBootScene();
}
Label helpText = new Label("Automatically saves, loads the first scene in Build Settings, and presses Play.");
helpText.style.color = new StyleColor(new Color(0.6f, 0.6f, 0.6f));
helpText.style.marginTop = 10;
helpText.style.whiteSpace = WhiteSpace.Normal;
playCard.Add(helpText);
GUI.backgroundColor = oldColor;
EditorGUILayout.HelpBox("Automatically saves your current scene, loads the first scene in Build Settings, and presses Play.", MessageType.Info);
EditorGUILayout.EndVertical();
}
root.Add(playCard);
private void DrawSceneNavigation()
{
GUILayout.Label("SCENE NAVIGATION (Build Settings)", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical("box");
// Scene Navigation
VisualElement navCard = CreateCard();
navCard.style.marginTop = 15;
Label navTitle = new Label("SCENE NAVIGATION (Build Settings)");
navTitle.style.unityFontStyleAndWeight = FontStyle.Bold;
navTitle.style.marginBottom = 10;
navCard.Add(navTitle);
// Fetch scenes dynamically from Build Settings
ScrollView scrollView = new ScrollView();
scrollView.style.maxHeight = 200;
EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes;
if (scenes.Length == 0)
{
EditorGUILayout.HelpBox("No scenes found in Build Settings! Please go to File -> Build Settings and add your scenes.", MessageType.Warning);
Label warn = new Label("No scenes found in Build Settings! Please go to File -> Build Settings.");
warn.style.color = new StyleColor(new Color(0.9f, 0.7f, 0.2f));
warn.style.whiteSpace = WhiteSpace.Normal;
navCard.Add(warn);
}
else
{
sceneScrollPos = EditorGUILayout.BeginScrollView(sceneScrollPos, GUILayout.MaxHeight(200));
for (int i = 0; i < scenes.Length; i++)
{
if (scenes[i].enabled)
@@ -97,41 +113,51 @@ namespace Editor
string scenePath = scenes[i].path;
string sceneName = Path.GetFileNameWithoutExtension(scenePath);
EditorGUILayout.BeginHorizontal();
GUILayout.Label($"[{i}]", GUILayout.Width(25));
VisualElement row = new VisualElement();
row.style.flexDirection = FlexDirection.Row;
row.style.marginBottom = 5;
row.style.alignItems = Align.Center;
Label indexLbl = new Label($"[{i}]");
indexLbl.style.width = 30;
indexLbl.style.unityTextAlign = TextAnchor.MiddleLeft;
Button loadBtn = new Button(() => OpenScene(scenePath));
loadBtn.text = $"Load {sceneName}";
loadBtn.style.flexGrow = 1;
loadBtn.style.height = 25;
loadBtn.style.borderTopLeftRadius = 4;
loadBtn.style.borderTopRightRadius = 4;
loadBtn.style.borderBottomLeftRadius = 4;
loadBtn.style.borderBottomRightRadius = 4;
if (GUILayout.Button($"Load {sceneName}", GUILayout.Height(25)))
{
OpenScene(scenePath);
}
EditorGUILayout.EndHorizontal();
row.Add(indexLbl);
row.Add(loadBtn);
scrollView.Add(row);
}
}
EditorGUILayout.EndScrollView();
navCard.Add(scrollView);
}
root.Add(navCard);
EditorGUILayout.EndVertical();
}
// Data Management
VisualElement dataCard = CreateCard();
dataCard.style.marginTop = 15;
Label dataTitle = new Label("DATA MANAGEMENT");
dataTitle.style.unityFontStyleAndWeight = FontStyle.Bold;
dataTitle.style.marginBottom = 10;
dataCard.Add(dataTitle);
private void DrawDataManagement()
{
GUILayout.Label("DATA MANAGEMENT", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical("box");
Button folderBtn = new Button(() => EditorUtility.RevealInFinder(Application.persistentDataPath));
folderBtn.text = "Open Save Folder (Explorer/Finder)";
folderBtn.style.height = 30;
folderBtn.style.borderTopLeftRadius = 4;
folderBtn.style.borderTopRightRadius = 4;
folderBtn.style.borderBottomLeftRadius = 4;
folderBtn.style.borderBottomRightRadius = 4;
dataCard.Add(folderBtn);
if (GUILayout.Button("Open Save Folder (Explorer/Finder)", GUILayout.Height(30)))
{
EditorUtility.RevealInFinder(Application.persistentDataPath);
}
EditorGUILayout.Space(5);
// Red Delete Button
Color oldColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(1f, 0.4f, 0.4f); // Light Red
if (GUILayout.Button("⚠ Clear PlayerPrefs & Save Data", GUILayout.Height(35)))
{
Button clearBtn = new Button(() => {
if (EditorUtility.DisplayDialog(
"Clear All Data?",
"Are you sure you want to delete all PlayerPrefs and JSON save files?\nThis action cannot be undone.",
@@ -140,10 +166,46 @@ namespace Editor
{
ClearAllData();
}
}
});
clearBtn.text = "⚠ Clear PlayerPrefs & Save Data";
clearBtn.style.height = 35;
clearBtn.style.marginTop = 10;
clearBtn.style.backgroundColor = new StyleColor(new Color(0.7f, 0.3f, 0.3f));
clearBtn.style.color = new StyleColor(Color.white);
clearBtn.style.unityFontStyleAndWeight = FontStyle.Bold;
clearBtn.style.borderTopLeftRadius = 6;
clearBtn.style.borderTopRightRadius = 6;
clearBtn.style.borderBottomLeftRadius = 6;
clearBtn.style.borderBottomRightRadius = 6;
dataCard.Add(clearBtn);
GUI.backgroundColor = oldColor;
EditorGUILayout.EndVertical();
root.Add(dataCard);
root.Add(ScovySignature.CreateSignatureBox());
}
private VisualElement CreateCard()
{
VisualElement card = new VisualElement();
card.style.backgroundColor = new StyleColor(new Color(0.18f, 0.18f, 0.18f, 0.9f));
card.style.borderTopLeftRadius = 10;
card.style.borderTopRightRadius = 10;
card.style.borderBottomLeftRadius = 10;
card.style.borderBottomRightRadius = 10;
card.style.paddingTop = 15;
card.style.paddingBottom = 15;
card.style.paddingLeft = 15;
card.style.paddingRight = 15;
card.style.borderTopWidth = 1;
card.style.borderBottomWidth = 1;
card.style.borderLeftWidth = 1;
card.style.borderRightWidth = 1;
card.style.borderTopColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderBottomColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderLeftColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
card.style.borderRightColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f));
return card;
}
private void PlayFromBootScene()

View File

@@ -0,0 +1,82 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using System.IO;
namespace Editor
{
public static class ScovySignature
{
public static VisualElement CreateSignatureBox()
{
VisualElement signatureBox = new VisualElement();
signatureBox.style.marginTop = 25;
signatureBox.style.paddingTop = 15;
signatureBox.style.paddingBottom = 10;
signatureBox.style.borderTopWidth = 1;
signatureBox.style.borderTopColor = new StyleColor(new Color(0.25f, 0.25f, 0.25f));
signatureBox.style.alignItems = Align.Center;
Label creatorLabel = new Label("Created by Scovy");
creatorLabel.style.fontSize = 14;
creatorLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
creatorLabel.style.color = new StyleColor(new Color(0.4f, 0.8f, 1f));
signatureBox.Add(creatorLabel);
// Load ascii art from catgirl.txt
string asciiText = " /\\_/\\ \n ( o.o ) \n > ^ < "; // Fallback
// Search for the text asset anywhere in the project
string[] guids = AssetDatabase.FindAssets("catgirl t:TextAsset");
if (guids.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
TextAsset txtAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(path);
if (txtAsset != null)
{
asciiText = txtAsset.text;
}
}
else
{
// Fallback attempt to read directly if Unity hasn't indexed it yet
string directPath = Path.Combine(Application.dataPath, "Editors/catgirl.txt");
if (File.Exists(directPath))
{
asciiText = File.ReadAllText(directPath);
}
}
// Prevent Unity from stripping leading spaces by replacing them with non-breaking spaces
asciiText = asciiText.Replace(" ", "\u00A0");
Label asciiArt = new Label(asciiText);
asciiArt.style.whiteSpace = WhiteSpace.Pre;
asciiArt.style.color = new StyleColor(new Color(0.5f, 0.5f, 0.5f));
asciiArt.style.marginTop = 5;
asciiArt.style.unityTextAlign = TextAnchor.MiddleCenter;
// Increased font size based on feedback
asciiArt.style.fontSize = 10;
// Attempt to load an OS system font that supports Braille characters (like MS Gothic or Segoe UI Symbol)
Font osFont = Font.CreateDynamicFontFromOSFont(new string[] { "MS Gothic", "Segoe UI Symbol", "Consolas", "Arial" }, 10);
if (osFont != null)
{
// In newer Unity versions unityFontDefinition is preferred
asciiArt.style.unityFontDefinition = new StyleFontDefinition(osFont);
}
// Also add a scrollview wrapper just in case it's still too large
ScrollView scrollWrapper = new ScrollView();
scrollWrapper.style.maxHeight = 250; // Increased height to accommodate larger font
scrollWrapper.style.marginTop = 5;
scrollWrapper.contentContainer.style.alignItems = Align.Center;
scrollWrapper.Add(asciiArt);
signatureBox.Add(scrollWrapper);
return signatureBox;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e111b033e887ade4b8a2459210a0df00

View File

@@ -45,80 +45,74 @@ namespace Editor
private static void OnSceneGUI(SceneView sceneView)
{
// Only display the panel when the game is actually running
if (!Application.isPlaying) return;
Handles.BeginGUI();
// Calculate center-top position dynamically based on current Scene View size
float posX = (sceneView.position.width - PANEL_WIDTH) / 2f;
float posY = 15f;
Rect panelRect = new Rect(posX, posY, PANEL_WIDTH, PANEL_HEIGHT);
float panelWidth = 460f;
float panelHeight = 40f;
float posX = (sceneView.position.width - panelWidth) / 2f;
float posY = 20f;
Rect panelRect = new Rect(posX, posY, panelWidth, panelHeight);
// Draw the main background box
GUI.Box(panelRect, GUIContent.none, EditorStyles.helpBox);
GUILayout.BeginArea(new Rect(posX + 10, posY + 10, PANEL_WIDTH - 20, PANEL_HEIGHT - 20));
// --- 1. HEADER (Title & Current Speed) ---
GUIStyle headerStyle = new GUIStyle(EditorStyles.boldLabel)
{
richText = true,
alignment = TextAnchor.MiddleCenter,
fontSize = 13
};
// Sleek semi-transparent dark background
EditorGUI.DrawRect(panelRect, new Color(0.1f, 0.1f, 0.1f, 0.85f));
// Format the time scale to show 2 decimal places
// Thin accent line at the top
EditorGUI.DrawRect(new Rect(posX, posY, panelWidth, 2), new Color(0.3f, 0.7f, 1f, 0.8f));
GUILayout.BeginArea(new Rect(posX + 10, posY + 8, panelWidth - 20, panelHeight - 16));
GUILayout.BeginHorizontal();
// --- 1. HEADER (Current Speed) ---
string currentSpeedText = EditorApplication.isPaused ? "<color=#FF6B6B>PAUSED</color>" : $"<color=#4ECDC4>{Time.timeScale:F2}x</color>";
GUILayout.Label($"⏳ <b>TIME LORD</b> | Current: {currentSpeedText}", headerStyle);
GUILayout.Space(5);
GUILayout.Label($"⏳ {currentSpeedText}", new GUIStyle(EditorStyles.boldLabel) { richText = true, fontSize = 13, alignment = TextAnchor.MiddleLeft }, GUILayout.Width(80));
// --- 2. TIME SLIDER (Fine-tune control) ---
EditorGUI.BeginChangeCheck();
float newTimeScale = GUILayout.HorizontalSlider(Time.timeScale, 0f, 10f);
float newTimeScale = GUILayout.HorizontalSlider(Time.timeScale, 0f, 10f, GUILayout.Width(80), GUILayout.Height(20));
if (EditorGUI.EndChangeCheck())
{
Time.timeScale = newTimeScale;
// Auto-resume if adjusting slider while paused
if (EditorApplication.isPaused && newTimeScale > 0f)
{
EditorApplication.isPaused = false;
}
}
GUILayout.Space(5);
GUILayout.Space(15);
// --- 3. PRESET SPEED BUTTONS ---
GUILayout.BeginHorizontal();
DrawSpeedButton("0.1x", 0.1f);
DrawSpeedButton("0.5x", 0.5f);
DrawSpeedButton("1x", 1f);
DrawSpeedButton("2x", 2f);
DrawSpeedButton("5x", 5f);
GUILayout.EndHorizontal();
DrawSpeedButton("0.5x", 0.5f, 35);
DrawSpeedButton("1x", 1f, 30);
DrawSpeedButton("2x", 2f, 30);
DrawSpeedButton("5x", 5f, 30);
GUILayout.Space(5);
GUILayout.FlexibleSpace();
// --- 4. PAUSE / RESUME BUTTON ---
Color oldBgColor = GUI.backgroundColor;
GUI.backgroundColor = EditorApplication.isPaused ? new Color(1f, 0.4f, 0.4f) : new Color(0.9f, 0.9f, 0.9f);
GUI.backgroundColor = EditorApplication.isPaused ? new Color(1f, 0.4f, 0.4f) : new Color(0.8f, 0.8f, 0.8f);
string pauseLabel = EditorApplication.isPaused ? "▶ RESUME GAME" : "⏸ PAUSE GAME";
string pauseLabel = EditorApplication.isPaused ? "▶ RESUME" : "⏸ PAUSE";
GUIStyle pauseStyle = new GUIStyle(GUI.skin.button) { fontStyle = FontStyle.Bold };
if (GUILayout.Button(pauseLabel, pauseStyle, GUILayout.Height(25)))
if (GUILayout.Button(pauseLabel, pauseStyle, GUILayout.Width(80), GUILayout.Height(24)))
{
EditorApplication.isPaused = !EditorApplication.isPaused;
}
GUI.backgroundColor = oldBgColor;
GUILayout.EndHorizontal();
GUILayout.EndArea();
Handles.EndGUI();
}
/// <summary>
/// Draws a preset button that automatically highlights green if it matches the current time scale.
/// </summary>
private static void DrawSpeedButton(string label, float targetSpeed)
private static void DrawSpeedButton(string label, float targetSpeed, float width)
{
Color oldBgColor = GUI.backgroundColor;
@@ -129,7 +123,7 @@ namespace Editor
GUI.backgroundColor = new Color(0.4f, 1f, 0.4f); // Light Green
}
if (GUILayout.Button(label, GUILayout.Height(22)))
if (GUILayout.Button(label, GUILayout.Width(width), GUILayout.Height(24)))
{
Time.timeScale = targetSpeed;

View File

@@ -0,0 +1,4 @@
|、
(˚ˎ 。7
|、˜〵
じしˍ,)

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c1243462c0117614cba30fe64aa5d2b0
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: