Compare commits
7 Commits
kill-json-
...
32ecbf4dc4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32ecbf4dc4 | ||
|
|
4dfe6202fd | ||
|
|
f8805dabe7 | ||
|
|
961da5e729 | ||
|
|
a049c6a750 | ||
|
|
01caca1878 | ||
|
|
7565b189b9 |
Binary file not shown.
|
Before Width: | Height: | Size: 37 KiB |
@@ -1,156 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57224356af3d045dbbf8b420e13b4b10
|
||||
TextureImporter:
|
||||
internalIDToNameTable:
|
||||
- first:
|
||||
213: 2364528222304962155
|
||||
second: ramasjang_icon_0
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 2
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites:
|
||||
- serializedVersion: 2
|
||||
name: ramasjang_icon_0
|
||||
rect:
|
||||
serializedVersion: 2
|
||||
x: 4
|
||||
y: 5
|
||||
width: 334
|
||||
height: 334
|
||||
alignment: 0
|
||||
pivot: {x: 0, y: 0}
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
customData:
|
||||
outline: []
|
||||
physicsShape: []
|
||||
tessellationDetail: -1
|
||||
bones: []
|
||||
spriteID: b668fdfe3ed70d020800000000000000
|
||||
internalID: 2364528222304962155
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spriteCustomMetadata:
|
||||
entries: []
|
||||
nameFileIdTable:
|
||||
ramasjang_icon_0: 2364528222304962155
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -71,7 +71,6 @@ namespace AppleHills.Editor
|
||||
{
|
||||
CardSystemManager.Instance.OnBoosterOpened += OnBoosterOpened;
|
||||
CardSystemManager.Instance.OnCardCollected += OnCardCollected;
|
||||
CardSystemManager.Instance.OnCardRarityUpgraded += OnCardRarityUpgraded;
|
||||
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
|
||||
|
||||
isSubscribed = true;
|
||||
@@ -87,7 +86,6 @@ namespace AppleHills.Editor
|
||||
{
|
||||
CardSystemManager.Instance.OnBoosterOpened -= OnBoosterOpened;
|
||||
CardSystemManager.Instance.OnCardCollected -= OnCardCollected;
|
||||
CardSystemManager.Instance.OnCardRarityUpgraded -= OnCardRarityUpgraded;
|
||||
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
||||
}
|
||||
|
||||
@@ -109,13 +107,6 @@ namespace AppleHills.Editor
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void OnCardRarityUpgraded(CardData card)
|
||||
{
|
||||
lastEventMessage = $"Card upgraded: {card.Name} → {card.Rarity}!";
|
||||
RefreshData();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void OnBoosterCountChanged(int count)
|
||||
{
|
||||
boosterCount = count;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Interactions
|
||||
|
||||
829
Assets/Editor/CustomLogWindow.cs
Normal file
829
Assets/Editor/CustomLogWindow.cs
Normal file
@@ -0,0 +1,829 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Core;
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom dockable log window with advanced filtering capabilities.
|
||||
/// Supports filtering by class, method, log level, time range, and text search.
|
||||
/// Multiple instances can be opened with independent filter states.
|
||||
///
|
||||
/// PERFORMANCE NOTE: For large log counts (10,000+), consider implementing virtualized scrolling.
|
||||
/// Only render visible entries within the scroll view to avoid GUI overhead.
|
||||
/// See DrawLogEntries() method for potential optimization location.
|
||||
/// </summary>
|
||||
public class CustomLogWindow : EditorWindow
|
||||
{
|
||||
#region Window State
|
||||
|
||||
private Vector2 scrollPosition;
|
||||
private readonly List<LogEntry> allLogs = new List<LogEntry>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Filter State
|
||||
|
||||
private HashSet<string> activeClassTags = new HashSet<string>();
|
||||
private HashSet<string> activeMethodTags = new HashSet<string>();
|
||||
private HashSet<string> selectedClassFilters = new HashSet<string>();
|
||||
private HashSet<string> selectedMethodFilters = new HashSet<string>();
|
||||
private HashSet<LogLevel> selectedLevelFilters = new HashSet<LogLevel>
|
||||
{
|
||||
LogLevel.Debug, LogLevel.Info, LogLevel.Warning, LogLevel.Error
|
||||
};
|
||||
private string searchText = "";
|
||||
private bool autoScroll = true;
|
||||
|
||||
// Time range filtering
|
||||
private bool enableTimeFilter = false;
|
||||
private float minTimestamp = 0;
|
||||
private float maxTimestamp = 0;
|
||||
private float currentMaxTimestamp = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI State
|
||||
|
||||
// Colors for log levels
|
||||
private readonly Color debugColor = Color.white;
|
||||
private readonly Color infoColor = Color.white;
|
||||
private readonly Color warningColor = Color.yellow;
|
||||
private readonly Color errorColor = new Color(1f, 0.3f, 0.3f);
|
||||
|
||||
// Alternating row background colors
|
||||
private readonly Color evenRowColor = new Color(0.22f, 0.22f, 0.22f);
|
||||
private readonly Color oddRowColor = new Color(0.26f, 0.26f, 0.26f);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Menu Items
|
||||
|
||||
[MenuItem("AppleHills/Custom Log Console")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<CustomLogWindow>("Custom Log");
|
||||
window.Show();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// Subscribe to new log events
|
||||
Logging.OnLogEntryAdded += OnLogAdded;
|
||||
|
||||
// Load existing logs
|
||||
allLogs.AddRange(Logging.GetRecentLogs());
|
||||
|
||||
// Build initial tag lists
|
||||
RebuildTagLists();
|
||||
|
||||
// Initialize time range
|
||||
UpdateTimeRange();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Logging.OnLogEntryAdded -= OnLogAdded;
|
||||
}
|
||||
|
||||
private void OnLogAdded(LogEntry entry)
|
||||
{
|
||||
allLogs.Add(entry);
|
||||
|
||||
// Update active tags
|
||||
activeClassTags.Add(entry.ClassName);
|
||||
activeMethodTags.Add(entry.MethodName);
|
||||
|
||||
// Update time range
|
||||
UpdateTimeRange();
|
||||
|
||||
if (autoScroll)
|
||||
scrollPosition.y = float.MaxValue;
|
||||
|
||||
Repaint(); // Redraw window
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GUI Drawing
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
DrawToolbar();
|
||||
DrawLogEntries();
|
||||
}
|
||||
|
||||
private void DrawToolbar()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
|
||||
// Clear button
|
||||
if (GUILayout.Button("Clear", EditorStyles.toolbarButton, GUILayout.Width(50)))
|
||||
{
|
||||
allLogs.Clear();
|
||||
activeClassTags.Clear();
|
||||
activeMethodTags.Clear();
|
||||
Logging.ClearLogs();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
// Auto-scroll toggle
|
||||
autoScroll = GUILayout.Toggle(autoScroll, "Auto-scroll", EditorStyles.toolbarButton, GUILayout.Width(80));
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Class Filter Button
|
||||
string classLabel = selectedClassFilters.Count == 0 ? "Classes: All" :
|
||||
selectedClassFilters.Count == activeClassTags.Count ? "Classes: All" :
|
||||
$"Classes: {selectedClassFilters.Count}";
|
||||
if (GUILayout.Button(new GUIContent(classLabel, "Filter by class"), EditorStyles.toolbarDropDown, GUILayout.Width(100)))
|
||||
{
|
||||
ShowClassFilterMenu();
|
||||
}
|
||||
|
||||
// Method Filter Button
|
||||
string methodLabel = selectedMethodFilters.Count == 0 ? "Methods: All" :
|
||||
selectedMethodFilters.Count == activeMethodTags.Count ? "Methods: All" :
|
||||
$"Methods: {selectedMethodFilters.Count}";
|
||||
if (GUILayout.Button(new GUIContent(methodLabel, "Filter by method"), EditorStyles.toolbarDropDown, GUILayout.Width(110)))
|
||||
{
|
||||
ShowMethodFilterMenu();
|
||||
}
|
||||
|
||||
// Log Level Filter Button
|
||||
string levelLabel = selectedLevelFilters.Count == 4 ? "Levels: All" :
|
||||
selectedLevelFilters.Count == 0 ? "Levels: None" :
|
||||
$"Levels: {selectedLevelFilters.Count}";
|
||||
if (GUILayout.Button(new GUIContent(levelLabel, "Filter by log level"), EditorStyles.toolbarDropDown, GUILayout.Width(90)))
|
||||
{
|
||||
ShowLevelFilterMenu();
|
||||
}
|
||||
|
||||
// Time Range Filter Button
|
||||
if (GUILayout.Button(new GUIContent("⏱", "Time range filter"), EditorStyles.toolbarButton, GUILayout.Width(25)))
|
||||
{
|
||||
ShowTimeRangeWindow();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
// Search box
|
||||
GUILayout.Label("Search:", GUILayout.Width(50));
|
||||
searchText = GUILayout.TextField(searchText, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Export button
|
||||
if (GUILayout.Button("Export", EditorStyles.toolbarButton, GUILayout.Width(60)))
|
||||
{
|
||||
ExportLogs();
|
||||
}
|
||||
|
||||
GUILayout.Space(5);
|
||||
|
||||
// Log count
|
||||
var filteredCount = GetFilteredLogs().Count();
|
||||
GUILayout.Label($"{filteredCount}/{allLogs.Count}", GUILayout.Width(80));
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void ShowClassFilterMenu()
|
||||
{
|
||||
ClassFilterWindow.ShowWindow(this, activeClassTags, selectedClassFilters,
|
||||
(newSelection) =>
|
||||
{
|
||||
selectedClassFilters = newSelection;
|
||||
Repaint();
|
||||
});
|
||||
}
|
||||
|
||||
private void ShowMethodFilterMenu()
|
||||
{
|
||||
MethodFilterWindow.ShowWindow(this, activeMethodTags, selectedMethodFilters,
|
||||
(newSelection) =>
|
||||
{
|
||||
selectedMethodFilters = newSelection;
|
||||
Repaint();
|
||||
});
|
||||
}
|
||||
|
||||
private void ShowLevelFilterMenu()
|
||||
{
|
||||
LevelFilterWindow.ShowWindow(this, selectedLevelFilters,
|
||||
(newSelection) =>
|
||||
{
|
||||
selectedLevelFilters = newSelection;
|
||||
Repaint();
|
||||
});
|
||||
}
|
||||
|
||||
private void ShowTimeRangeWindow()
|
||||
{
|
||||
TimeRangeFilterWindow.ShowWindow(this, enableTimeFilter, minTimestamp, maxTimestamp, currentMaxTimestamp,
|
||||
(enabled, min, max) =>
|
||||
{
|
||||
enableTimeFilter = enabled;
|
||||
minTimestamp = min;
|
||||
maxTimestamp = max;
|
||||
Repaint();
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawLogEntries()
|
||||
{
|
||||
// PERFORMANCE NOTE: For 10,000+ logs, consider virtualized scrolling here.
|
||||
// Only render entries visible within the scroll view rect.
|
||||
// Current implementation renders all filtered logs which may cause GUI slowdown.
|
||||
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
var filteredLogs = GetFilteredLogs().ToList();
|
||||
|
||||
for (int i = 0; i < filteredLogs.Count; i++)
|
||||
{
|
||||
DrawLogEntry(filteredLogs[i], i);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void DrawLogEntry(LogEntry entry, int index)
|
||||
{
|
||||
// Draw alternating row background
|
||||
Color bgColor = (index % 2 == 0) ? evenRowColor : oddRowColor;
|
||||
Rect rowRect = EditorGUILayout.BeginHorizontal();
|
||||
EditorGUI.DrawRect(rowRect, bgColor);
|
||||
|
||||
// Color-code by log level
|
||||
Color color = GetColorForLevel(entry.Level);
|
||||
var originalColor = GUI.contentColor;
|
||||
GUI.contentColor = color;
|
||||
|
||||
// Timestamp
|
||||
GUILayout.Label($"[{entry.Timestamp:F2}s]", GUILayout.Width(80));
|
||||
|
||||
// Level
|
||||
GUILayout.Label($"[{entry.Level}]", GUILayout.Width(70));
|
||||
|
||||
// Class
|
||||
GUILayout.Label($"[{entry.ClassName}]", GUILayout.Width(150));
|
||||
|
||||
// Method
|
||||
GUILayout.Label($"[{entry.MethodName}]", GUILayout.Width(150));
|
||||
|
||||
// Message
|
||||
GUILayout.Label(entry.Message, GUILayout.ExpandWidth(true));
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
GUI.contentColor = originalColor;
|
||||
|
||||
// Right-click context menu
|
||||
if (Event.current.type == EventType.ContextClick && rowRect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
menu.AddItem(new GUIContent("Copy Full Message"), false, () =>
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = entry.FullFormattedMessage;
|
||||
});
|
||||
|
||||
menu.AddItem(new GUIContent("Copy Message Only"), false, () =>
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = entry.Message;
|
||||
});
|
||||
|
||||
menu.AddSeparator("");
|
||||
|
||||
menu.AddItem(new GUIContent("Filter to this Class"), false, () =>
|
||||
{
|
||||
selectedClassFilters.Clear();
|
||||
selectedClassFilters.Add(entry.ClassName);
|
||||
Repaint();
|
||||
});
|
||||
|
||||
menu.AddItem(new GUIContent("Filter to this Method"), false, () =>
|
||||
{
|
||||
selectedMethodFilters.Clear();
|
||||
selectedMethodFilters.Add(entry.MethodName);
|
||||
Repaint();
|
||||
});
|
||||
|
||||
menu.AddItem(new GUIContent("Filter to this Class + Method"), false, () =>
|
||||
{
|
||||
selectedClassFilters.Clear();
|
||||
selectedClassFilters.Add(entry.ClassName);
|
||||
selectedMethodFilters.Clear();
|
||||
selectedMethodFilters.Add(entry.MethodName);
|
||||
Repaint();
|
||||
});
|
||||
|
||||
menu.AddSeparator("");
|
||||
|
||||
menu.AddItem(new GUIContent("Filter to this Log Level"), false, () =>
|
||||
{
|
||||
selectedLevelFilters.Clear();
|
||||
selectedLevelFilters.Add(entry.Level);
|
||||
Repaint();
|
||||
});
|
||||
|
||||
menu.ShowAsContext();
|
||||
Event.current.Use();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Filtering Logic
|
||||
|
||||
private IEnumerable<LogEntry> GetFilteredLogs()
|
||||
{
|
||||
return allLogs.Where(entry =>
|
||||
{
|
||||
// Filter by class (if any selected)
|
||||
if (selectedClassFilters.Count > 0 && !selectedClassFilters.Contains(entry.ClassName))
|
||||
return false;
|
||||
|
||||
// Filter by method (if any selected)
|
||||
if (selectedMethodFilters.Count > 0 && !selectedMethodFilters.Contains(entry.MethodName))
|
||||
return false;
|
||||
|
||||
// Filter by log level
|
||||
if (!selectedLevelFilters.Contains(entry.Level))
|
||||
return false;
|
||||
|
||||
// Filter by time range
|
||||
if (enableTimeFilter)
|
||||
{
|
||||
if (entry.Timestamp < minTimestamp || entry.Timestamp > maxTimestamp)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter by search text
|
||||
if (!string.IsNullOrEmpty(searchText))
|
||||
{
|
||||
bool matchesSearch =
|
||||
entry.ClassName.Contains(searchText, StringComparison.OrdinalIgnoreCase) ||
|
||||
entry.MethodName.Contains(searchText, StringComparison.OrdinalIgnoreCase) ||
|
||||
entry.Message.Contains(searchText, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (!matchesSearch)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private Color GetColorForLevel(LogLevel level)
|
||||
{
|
||||
return level switch
|
||||
{
|
||||
LogLevel.Debug => debugColor,
|
||||
LogLevel.Info => infoColor,
|
||||
LogLevel.Warning => warningColor,
|
||||
LogLevel.Error => errorColor,
|
||||
_ => infoColor
|
||||
};
|
||||
}
|
||||
|
||||
private void RebuildTagLists()
|
||||
{
|
||||
activeClassTags.Clear();
|
||||
activeMethodTags.Clear();
|
||||
|
||||
foreach (var log in allLogs)
|
||||
{
|
||||
activeClassTags.Add(log.ClassName);
|
||||
activeMethodTags.Add(log.MethodName);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTimeRange()
|
||||
{
|
||||
if (allLogs.Count > 0)
|
||||
{
|
||||
currentMaxTimestamp = allLogs.Max(l => l.Timestamp);
|
||||
|
||||
// Initialize time range on first log
|
||||
if (maxTimestamp == 0)
|
||||
{
|
||||
maxTimestamp = currentMaxTimestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Auto-expand max range as new logs come in
|
||||
maxTimestamp = currentMaxTimestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportLogs()
|
||||
{
|
||||
string defaultFileName = $"logs_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.txt";
|
||||
string path = EditorUtility.SaveFilePanel("Export Logs", "", defaultFileName, "txt");
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
var filteredLogs = GetFilteredLogs().ToList();
|
||||
var lines = filteredLogs.Select(e => e.FullFormattedMessage);
|
||||
File.WriteAllLines(path, lines);
|
||||
|
||||
EditorUtility.DisplayDialog("Export Successful",
|
||||
$"Exported {filteredLogs.Count} log entries to:\n{path}", "OK");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Export Failed",
|
||||
$"Failed to export logs:\n{ex.Message}", "OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persistent popup window for class filtering with multi-selection support
|
||||
/// </summary>
|
||||
public class ClassFilterWindow : EditorWindow
|
||||
{
|
||||
private HashSet<string> availableTags;
|
||||
private HashSet<string> selectedTags;
|
||||
private System.Action<HashSet<string>> onApply;
|
||||
private Vector2 scrollPosition;
|
||||
private string searchFilter = "";
|
||||
|
||||
public static void ShowWindow(CustomLogWindow parent, HashSet<string> available, HashSet<string> selected,
|
||||
System.Action<HashSet<string>> applyCallback)
|
||||
{
|
||||
var window = CreateInstance<ClassFilterWindow>();
|
||||
window.titleContent = new GUIContent("Class Filter");
|
||||
window.availableTags = available;
|
||||
window.selectedTags = new HashSet<string>(selected);
|
||||
window.onApply = applyCallback;
|
||||
|
||||
var parentRect = parent.position;
|
||||
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 300, 400);
|
||||
window.ShowUtility();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Control buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||
{
|
||||
if (availableTags != null)
|
||||
selectedTags = new HashSet<string>(availableTags);
|
||||
}
|
||||
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||
{
|
||||
selectedTags.Clear();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
searchFilter = EditorGUILayout.TextField(searchFilter, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Scrollable list of toggles
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
if (availableTags != null && availableTags.Count > 0)
|
||||
{
|
||||
var filteredTags = string.IsNullOrEmpty(searchFilter)
|
||||
? availableTags.OrderBy(t => t)
|
||||
: availableTags.Where(t => t.Contains(searchFilter, StringComparison.OrdinalIgnoreCase)).OrderBy(t => t);
|
||||
|
||||
foreach (var tag in filteredTags)
|
||||
{
|
||||
bool isSelected = selectedTags.Contains(tag);
|
||||
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
||||
|
||||
if (newSelection != isSelected)
|
||||
{
|
||||
if (newSelection)
|
||||
selectedTags.Add(tag);
|
||||
else
|
||||
selectedTags.Remove(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("No classes available", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Apply/Close buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||
{
|
||||
onApply?.Invoke(selectedTags);
|
||||
Close();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persistent popup window for method filtering with multi-selection support
|
||||
/// </summary>
|
||||
public class MethodFilterWindow : EditorWindow
|
||||
{
|
||||
private HashSet<string> availableTags;
|
||||
private HashSet<string> selectedTags;
|
||||
private System.Action<HashSet<string>> onApply;
|
||||
private Vector2 scrollPosition;
|
||||
private string searchFilter = "";
|
||||
|
||||
public static void ShowWindow(CustomLogWindow parent, HashSet<string> available, HashSet<string> selected,
|
||||
System.Action<HashSet<string>> applyCallback)
|
||||
{
|
||||
var window = CreateInstance<MethodFilterWindow>();
|
||||
window.titleContent = new GUIContent("Method Filter");
|
||||
window.availableTags = available;
|
||||
window.selectedTags = new HashSet<string>(selected);
|
||||
window.onApply = applyCallback;
|
||||
|
||||
var parentRect = parent.position;
|
||||
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 300, 400);
|
||||
window.ShowUtility();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Control buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||
{
|
||||
if (availableTags != null)
|
||||
selectedTags = new HashSet<string>(availableTags);
|
||||
}
|
||||
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||
{
|
||||
selectedTags.Clear();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
searchFilter = EditorGUILayout.TextField(searchFilter, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Scrollable list of toggles
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
if (availableTags != null && availableTags.Count > 0)
|
||||
{
|
||||
var filteredTags = string.IsNullOrEmpty(searchFilter)
|
||||
? availableTags.OrderBy(t => t)
|
||||
: availableTags.Where(t => t.Contains(searchFilter, StringComparison.OrdinalIgnoreCase)).OrderBy(t => t);
|
||||
|
||||
foreach (var tag in filteredTags)
|
||||
{
|
||||
bool isSelected = selectedTags.Contains(tag);
|
||||
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
||||
|
||||
if (newSelection != isSelected)
|
||||
{
|
||||
if (newSelection)
|
||||
selectedTags.Add(tag);
|
||||
else
|
||||
selectedTags.Remove(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("No methods available", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Apply/Close buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||
{
|
||||
onApply?.Invoke(selectedTags);
|
||||
Close();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persistent popup window for log level filtering with multi-selection support
|
||||
/// </summary>
|
||||
public class LevelFilterWindow : EditorWindow
|
||||
{
|
||||
private HashSet<LogLevel> selectedLevels;
|
||||
private System.Action<HashSet<LogLevel>> onApply;
|
||||
|
||||
public static void ShowWindow(CustomLogWindow parent, HashSet<LogLevel> selected,
|
||||
System.Action<HashSet<LogLevel>> applyCallback)
|
||||
{
|
||||
var window = CreateInstance<LevelFilterWindow>();
|
||||
window.titleContent = new GUIContent("Log Level Filter");
|
||||
window.selectedLevels = new HashSet<LogLevel>(selected);
|
||||
window.onApply = applyCallback;
|
||||
|
||||
var parentRect = parent.position;
|
||||
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 250, 180);
|
||||
window.ShowUtility();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Control buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||
{
|
||||
selectedLevels = new HashSet<LogLevel>
|
||||
{
|
||||
LogLevel.Debug, LogLevel.Info, LogLevel.Warning, LogLevel.Error
|
||||
};
|
||||
}
|
||||
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||
{
|
||||
selectedLevels.Clear();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Log level toggles
|
||||
foreach (LogLevel level in Enum.GetValues(typeof(LogLevel)))
|
||||
{
|
||||
bool isSelected = selectedLevels.Contains(level);
|
||||
bool newSelection = EditorGUILayout.ToggleLeft(level.ToString(), isSelected);
|
||||
|
||||
if (newSelection != isSelected)
|
||||
{
|
||||
if (newSelection)
|
||||
selectedLevels.Add(level);
|
||||
else
|
||||
selectedLevels.Remove(level);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Apply/Close buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||
{
|
||||
onApply?.Invoke(selectedLevels);
|
||||
Close();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Popup window for configuring time range filters
|
||||
/// </summary>
|
||||
public class TimeRangeFilterWindow : EditorWindow
|
||||
{
|
||||
private bool enableTimeFilter;
|
||||
private float minTimestamp;
|
||||
private float maxTimestamp;
|
||||
private float currentMaxTimestamp;
|
||||
private System.Action<bool, float, float> onApply;
|
||||
|
||||
public static void ShowWindow(CustomLogWindow parent, bool enabled, float min, float max, float currentMax,
|
||||
System.Action<bool, float, float> applyCallback)
|
||||
{
|
||||
var window = CreateInstance<TimeRangeFilterWindow>();
|
||||
window.titleContent = new GUIContent("Time Range Filter");
|
||||
window.enableTimeFilter = enabled;
|
||||
window.minTimestamp = min;
|
||||
window.maxTimestamp = max;
|
||||
window.currentMaxTimestamp = currentMax;
|
||||
window.onApply = applyCallback;
|
||||
|
||||
// Position near the parent window
|
||||
var parentRect = parent.position;
|
||||
window.position = new Rect(parentRect.x + 100, parentRect.y + 100, 350, 120);
|
||||
|
||||
window.ShowUtility();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
enableTimeFilter = EditorGUILayout.Toggle("Enable Time Filter", enableTimeFilter);
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
if (enableTimeFilter && currentMaxTimestamp > 0)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField($"Min: {minTimestamp:F2}s", GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField($"Max: {maxTimestamp:F2}s", GUILayout.Width(100));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.MinMaxSlider(
|
||||
ref minTimestamp,
|
||||
ref maxTimestamp,
|
||||
0,
|
||||
currentMaxTimestamp);
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Reset Range"))
|
||||
{
|
||||
minTimestamp = 0;
|
||||
maxTimestamp = currentMaxTimestamp;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else if (enableTimeFilter)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No logs available for time filtering", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||
{
|
||||
onApply?.Invoke(enableTimeFilter, minTimestamp, maxTimestamp);
|
||||
Close();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Cancel", GUILayout.Width(80)))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Editor/CustomLogWindow.cs.meta
Normal file
3
Assets/Editor/CustomLogWindow.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60a300585fef4ac990c963c8b37c3887
|
||||
timeCreated: 1762814971
|
||||
@@ -181,7 +181,7 @@ namespace Editor
|
||||
string scenePath = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
|
||||
|
||||
var allComponents = GameObject.FindObjectsOfType<Component>(true);
|
||||
var allComponents = GameObject.FindObjectsByType<Component>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
foreach (var component in allComponents)
|
||||
{
|
||||
if (component == null || component.gameObject.scene != scene) continue;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Editor.Lifecycle
|
||||
/// Editor-only bootstrap that ensures OnSceneReady is triggered when playing directly from a scene in Unity Editor.
|
||||
///
|
||||
/// PROBLEM: When you press Play in the editor without going through the scene manager:
|
||||
/// - CustomBoot runs and triggers OnBootCompletionTriggered (which broadcasts OnManagedAwake)
|
||||
/// - CustomBoot runs and triggers OnBootCompletionTriggered (which broadcasts OnManagedStart)
|
||||
/// - But BroadcastSceneReady is NEVER called for the initial scene
|
||||
/// - Components in the scene never receive their OnSceneReady() callback
|
||||
///
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using UnityEditor;
|
||||
using AppleHills.Core.Settings;
|
||||
using Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering.VirtualTexturing;
|
||||
|
||||
namespace AppleHills.Editor
|
||||
{
|
||||
@@ -65,7 +63,7 @@ namespace AppleHills.Editor
|
||||
GetPuzzlePromptRange
|
||||
);
|
||||
|
||||
LogDebugMessage("Editor settings loaded for Scene View use");
|
||||
Logging.Debug("Editor settings loaded for Scene View use");
|
||||
}
|
||||
|
||||
public static void RefreshSceneViews()
|
||||
@@ -102,14 +100,5 @@ namespace AppleHills.Editor
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void LogDebugMessage(string message)
|
||||
{
|
||||
if (Application.isPlaying &&
|
||||
DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().settingsLogVerbosity <= LogVerbosity.Debug)
|
||||
{
|
||||
Logging.Debug($"[EditorSettingsProvider] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
using Interactions;
|
||||
using PuzzleS;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
public class ItemPrefabEditorWindow : EditorWindow
|
||||
{
|
||||
private GameObject _selectedGameObject;
|
||||
private InteractableBase _interactable;
|
||||
private PickupItemData _pickupData;
|
||||
private PuzzleStepSO _objectiveData;
|
||||
private UnityEditor.Editor _soEditor;
|
||||
private string _pickupSoFolderPath = "Assets/Data/Items";
|
||||
private string _puzzleSoFolderPath = "Assets/Data/Puzzles";
|
||||
|
||||
private enum ItemType { None, Pickup, ItemSlot }
|
||||
private ItemType _itemType = ItemType.None;
|
||||
|
||||
[MenuItem("AppleHills/Item Prefab Editor")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<ItemPrefabEditorWindow>("Item Prefab Editor");
|
||||
window.minSize = new Vector2(400, 400);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Selection.selectionChanged += Repaint;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Selection.selectionChanged -= Repaint;
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
_selectedGameObject = null;
|
||||
_interactable = null;
|
||||
if (Selection.activeGameObject != null)
|
||||
{
|
||||
_selectedGameObject = Selection.activeGameObject;
|
||||
_interactable = _selectedGameObject.GetComponent<InteractableBase>();
|
||||
}
|
||||
else if (Selection.activeObject is GameObject go)
|
||||
{
|
||||
_selectedGameObject = go;
|
||||
_interactable = go.GetComponent<InteractableBase>();
|
||||
}
|
||||
|
||||
if (_selectedGameObject == null || _interactable == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Select a GameObject or prefab with an InteractableBase component to edit.", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField("Editing: ", _selectedGameObject.name, EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Determine current type
|
||||
bool hasPickup = _selectedGameObject.GetComponent<Pickup>() != null;
|
||||
bool hasSlot = _selectedGameObject.GetComponent<ItemSlot>() != null;
|
||||
if (hasSlot) _itemType = ItemType.ItemSlot;
|
||||
else if (hasPickup) _itemType = ItemType.Pickup;
|
||||
else _itemType = ItemType.None;
|
||||
|
||||
// Item type selection
|
||||
var newType = (ItemType)EditorGUILayout.EnumPopup("Item Type", _itemType);
|
||||
if (newType != _itemType)
|
||||
{
|
||||
// Remove both, then add selected
|
||||
PrefabEditorUtility.RemoveComponent<Pickup>(_selectedGameObject);
|
||||
PrefabEditorUtility.RemoveComponent<ItemSlot>(_selectedGameObject);
|
||||
if (newType == ItemType.Pickup)
|
||||
PrefabEditorUtility.AddOrGetComponent<Pickup>(_selectedGameObject);
|
||||
else if (newType == ItemType.ItemSlot)
|
||||
PrefabEditorUtility.AddOrGetComponent<ItemSlot>(_selectedGameObject);
|
||||
_itemType = newType;
|
||||
}
|
||||
|
||||
// ObjectiveStepBehaviour
|
||||
bool hasObjective = _selectedGameObject.GetComponent<ObjectiveStepBehaviour>() != null;
|
||||
bool addObjective = EditorGUILayout.Toggle("ObjectiveStepBehaviour", hasObjective);
|
||||
if (addObjective && !hasObjective)
|
||||
{
|
||||
PrefabEditorUtility.AddOrGetComponent<ObjectiveStepBehaviour>(_selectedGameObject);
|
||||
}
|
||||
else if (!addObjective && hasObjective)
|
||||
{
|
||||
PrefabEditorUtility.RemoveComponent<ObjectiveStepBehaviour>(_selectedGameObject);
|
||||
}
|
||||
|
||||
// Pickup Data (for Pickup or ItemSlot)
|
||||
if (_itemType == ItemType.Pickup || _itemType == ItemType.ItemSlot)
|
||||
{
|
||||
var pickup = _selectedGameObject.GetComponent<Pickup>();
|
||||
_pickupData = pickup.itemData;
|
||||
EditorGUILayout.LabelField("Pickup Data:", EditorStyles.boldLabel);
|
||||
_pickupData = (PickupItemData)EditorGUILayout.ObjectField("PickupItemData", _pickupData, typeof(PickupItemData), false);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel("Save To");
|
||||
EditorGUILayout.SelectableLabel(_pickupSoFolderPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
||||
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
||||
{
|
||||
_pickupSoFolderPath = PrefabEditorUtility.SelectFolder(_pickupSoFolderPath, "Data/Items");
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (_pickupData == null && GUILayout.Button("Create New PickupItemData"))
|
||||
{
|
||||
_pickupData = PrefabEditorUtility.CreateScriptableAsset<PickupItemData>(_selectedGameObject.name + "_pickup", _pickupSoFolderPath);
|
||||
}
|
||||
if (_pickupData != null)
|
||||
{
|
||||
PrefabEditorUtility.DrawScriptableObjectEditor(ref _soEditor, _pickupData);
|
||||
pickup.itemData = _pickupData;
|
||||
}
|
||||
}
|
||||
|
||||
// Objective Data
|
||||
if (addObjective)
|
||||
{
|
||||
var obj = _selectedGameObject.GetComponent<ObjectiveStepBehaviour>();
|
||||
_objectiveData = obj.stepData;
|
||||
EditorGUILayout.LabelField("Objective Data:", EditorStyles.boldLabel);
|
||||
_objectiveData = (PuzzleStepSO)EditorGUILayout.ObjectField("PuzzleStepSO", _objectiveData, typeof(PuzzleStepSO), false);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel("Save To");
|
||||
EditorGUILayout.SelectableLabel(_puzzleSoFolderPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
||||
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
||||
{
|
||||
_puzzleSoFolderPath = PrefabEditorUtility.SelectFolder(_puzzleSoFolderPath, "Data/Puzzles");
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (_objectiveData == null && GUILayout.Button("Create New PuzzleStepSO"))
|
||||
{
|
||||
_objectiveData = PrefabEditorUtility.CreateScriptableAsset<PuzzleStepSO>(_selectedGameObject.name + "_puzzle", _puzzleSoFolderPath);
|
||||
}
|
||||
if (_objectiveData != null)
|
||||
{
|
||||
PrefabEditorUtility.DrawScriptableObjectEditor(ref _soEditor, _objectiveData);
|
||||
obj.stepData = _objectiveData;
|
||||
}
|
||||
}
|
||||
if (GUI.changed)
|
||||
{
|
||||
EditorUtility.SetDirty(_selectedGameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(_selectedGameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 943b203cde5343c68a6278c111fce2ed
|
||||
timeCreated: 1757508162
|
||||
@@ -1,168 +0,0 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using Interactions;
|
||||
using PuzzleS;
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
public class PrefabCreatorWindow : EditorWindow
|
||||
{
|
||||
private string _prefabName = "NewPrefab";
|
||||
private string _saveFolderPath = "Assets/Prefabs/Items";
|
||||
private string _pickupSoFolderPath = "Assets/Data/Items";
|
||||
private string _puzzleSoFolderPath = "Assets/Data/Puzzles";
|
||||
private bool _addObjective;
|
||||
|
||||
private PickupItemData _pickupData;
|
||||
private PuzzleStepSO _objectiveData;
|
||||
private UnityEditor.Editor _soEditor;
|
||||
|
||||
private enum ItemType { None, Pickup, ItemSlot }
|
||||
private ItemType _itemType = ItemType.None;
|
||||
|
||||
private bool _createNext = false;
|
||||
|
||||
[MenuItem("AppleHills/Item Prefab Creator")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<PrefabCreatorWindow>("Prefab Creator");
|
||||
window.minSize = new Vector2(400, 400);
|
||||
// Set default paths if not already set
|
||||
if (string.IsNullOrEmpty(window._saveFolderPath))
|
||||
window._saveFolderPath = "Assets/Prefabs/Items";
|
||||
if (string.IsNullOrEmpty(window._pickupSoFolderPath))
|
||||
window._pickupSoFolderPath = "Assets/Data/Items";
|
||||
if (string.IsNullOrEmpty(window._puzzleSoFolderPath))
|
||||
window._puzzleSoFolderPath = "Assets/Data/Puzzles";
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Prefab Creator", EditorStyles.boldLabel);
|
||||
_prefabName = EditorGUILayout.TextField("Prefab Name", _prefabName);
|
||||
// Prefab save folder
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel("Save Folder");
|
||||
EditorGUILayout.SelectableLabel(_saveFolderPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
||||
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
||||
{
|
||||
_saveFolderPath = PrefabEditorUtility.SelectFolder(_saveFolderPath, "Prefabs/Items");
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Add Components:", EditorStyles.boldLabel);
|
||||
|
||||
// Item type selection
|
||||
var newType = (ItemType)EditorGUILayout.EnumPopup("Item Type", _itemType);
|
||||
if (newType != _itemType)
|
||||
{
|
||||
_itemType = newType;
|
||||
}
|
||||
bool addObjective = EditorGUILayout.Toggle("ObjectiveStepBehaviour", _addObjective);
|
||||
_addObjective = addObjective;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Pickup Data (for Pickup or ItemSlot)
|
||||
if (_itemType == ItemType.Pickup || _itemType == ItemType.ItemSlot)
|
||||
{
|
||||
EditorGUILayout.LabelField("Pickup Data:", EditorStyles.boldLabel);
|
||||
_pickupData = (PickupItemData)EditorGUILayout.ObjectField("PickupItemData", _pickupData, typeof(PickupItemData), false);
|
||||
// Pickup SO save folder
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel("Save To");
|
||||
EditorGUILayout.SelectableLabel(_pickupSoFolderPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
||||
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
||||
{
|
||||
_pickupSoFolderPath = PrefabEditorUtility.SelectFolder(_pickupSoFolderPath, "Data/Items");
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (_pickupData == null && GUILayout.Button("Create New PickupItemData"))
|
||||
{
|
||||
_pickupData = PrefabEditorUtility.CreateScriptableAsset<PickupItemData>(_prefabName + "_pickup", _pickupSoFolderPath);
|
||||
}
|
||||
if (_pickupData != null)
|
||||
{
|
||||
PrefabEditorUtility.DrawScriptableObjectEditor(ref _soEditor, _pickupData);
|
||||
}
|
||||
}
|
||||
// Objective Data
|
||||
if (_addObjective)
|
||||
{
|
||||
EditorGUILayout.LabelField("Objective Data:", EditorStyles.boldLabel);
|
||||
_objectiveData = (PuzzleStepSO)EditorGUILayout.ObjectField("PuzzleStepSO", _objectiveData, typeof(PuzzleStepSO), false);
|
||||
// Puzzle SO save folder
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel("Save To");
|
||||
EditorGUILayout.SelectableLabel(_puzzleSoFolderPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
||||
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
||||
{
|
||||
_puzzleSoFolderPath = PrefabEditorUtility.SelectFolder(_puzzleSoFolderPath, "Data/Puzzles");
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (_objectiveData == null && GUILayout.Button("Create New PuzzleStepSO"))
|
||||
{
|
||||
_objectiveData = PrefabEditorUtility.CreateScriptableAsset<PuzzleStepSO>(_prefabName + "_puzzle", _puzzleSoFolderPath);
|
||||
}
|
||||
if (_objectiveData != null)
|
||||
{
|
||||
PrefabEditorUtility.DrawScriptableObjectEditor(ref _soEditor, _objectiveData);
|
||||
}
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUI.enabled = !string.IsNullOrEmpty(_prefabName) && !string.IsNullOrEmpty(_saveFolderPath);
|
||||
if (GUILayout.Button("Create Prefab", GUILayout.Height(28)))
|
||||
{
|
||||
CreatePrefab();
|
||||
}
|
||||
_createNext = GUILayout.Toggle(_createNext, "Create Next", GUILayout.Width(100), GUILayout.Height(28));
|
||||
GUI.enabled = true;
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void CreatePrefab()
|
||||
{
|
||||
var go = new GameObject(_prefabName);
|
||||
// Note: No need to add InteractableBase separately - Pickup and ItemSlot inherit from it
|
||||
go.AddComponent<BoxCollider>();
|
||||
int interactableLayer = LayerMask.NameToLayer("Interactable");
|
||||
if (interactableLayer != -1)
|
||||
go.layer = interactableLayer;
|
||||
go.AddComponent<SpriteRenderer>();
|
||||
if (_itemType == ItemType.Pickup)
|
||||
{
|
||||
var pickup = go.AddComponent<Pickup>();
|
||||
pickup.itemData = _pickupData;
|
||||
}
|
||||
else if (_itemType == ItemType.ItemSlot)
|
||||
{
|
||||
var slot = go.AddComponent<ItemSlot>();
|
||||
slot.itemData = _pickupData;
|
||||
}
|
||||
if (_addObjective)
|
||||
{
|
||||
var obj = go.AddComponent<ObjectiveStepBehaviour>();
|
||||
obj.stepData = _objectiveData;
|
||||
}
|
||||
string prefabPath = Path.Combine(_saveFolderPath, _prefabName + ".prefab").Replace("\\", "/");
|
||||
var prefab = PrefabUtility.SaveAsPrefabAsset(go, prefabPath);
|
||||
DestroyImmediate(go);
|
||||
AssetDatabase.Refresh();
|
||||
Selection.activeObject = prefab;
|
||||
EditorGUIUtility.PingObject(prefab);
|
||||
EditorUtility.DisplayDialog("Prefab Created", $"Prefab saved to {prefabPath}", "OK");
|
||||
if (_createNext)
|
||||
{
|
||||
_prefabName = "NewPrefab";
|
||||
_pickupData = null;
|
||||
_objectiveData = null;
|
||||
_itemType = ItemType.None;
|
||||
_addObjective = false;
|
||||
_soEditor = null;
|
||||
GUI.FocusControl(null);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f67e06e997f642509ba61ea12b0f793e
|
||||
timeCreated: 1757503955
|
||||
@@ -725,9 +725,10 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 3864057818161790164}
|
||||
- component: {fileID: 5271824036850954050}
|
||||
- component: {fileID: 8081783206361873868}
|
||||
- component: {fileID: 5548642987123338363}
|
||||
- component: {fileID: 3058107077406709872}
|
||||
- component: {fileID: 587711432829270645}
|
||||
- component: {fileID: 4189849640380816173}
|
||||
m_Layer: 5
|
||||
m_Name: Icon
|
||||
m_TagString: Untagged
|
||||
@@ -762,6 +763,53 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4599222264323240281}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &8081783206361873868
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4599222264323240281}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.RawImage
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Texture: {fileID: 0}
|
||||
m_UVRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: -1
|
||||
--- !u!114 &5548642987123338363
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4599222264323240281}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 2fd09147b9e9d42a48d6ddc915ddc3d2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: SkiaSharp.Unity::SkiaSharp.Unity.SkottiePlayerV2
|
||||
lottieFile: {fileID: 4900000, guid: 50e22b5bb8a496840952f2563758c13c, type: 3}
|
||||
customResolution: 0
|
||||
resWidth: 250
|
||||
resHeight: 250
|
||||
stateName: Idle
|
||||
resetAfterFinished: 0
|
||||
autoPlay: 1
|
||||
loop: 1
|
||||
--- !u!114 &3058107077406709872
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -802,7 +850,7 @@ MonoBehaviour:
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 0}
|
||||
m_TargetGraphic: {fileID: 8081783206361873868}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls:
|
||||
@@ -832,36 +880,6 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::UI.HudMenuButton
|
||||
pagePrefab: {fileID: 1498581815400593087, guid: ccd858c7962d48147b0233c1bf1382f5, type: 3}
|
||||
buttonName: RamaSjang Button
|
||||
--- !u!114 &4189849640380816173
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4599222264323240281}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 2364528222304962155, guid: 57224356af3d045dbbf8b420e13b4b10, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &5113586844274188410
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -148,6 +148,8 @@ RectTransform:
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 6108475066390421500}
|
||||
- {fileID: 6717870941799174515}
|
||||
- {fileID: 4136733570236406132}
|
||||
m_Father: {fileID: 1315170081792486277}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
@@ -400,8 +402,8 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier: '::'
|
||||
PageName:
|
||||
transitionDuration: 0.3
|
||||
rainbowIn: {fileID: 0}
|
||||
rainbowOut: {fileID: 0}
|
||||
rainbowIn: {fileID: 5382650426034128680}
|
||||
rainbowOut: {fileID: 3983328028282460839}
|
||||
gameLayoutContainer: {fileID: 904161782565348054}
|
||||
exitButton: {fileID: 8427602740714176801}
|
||||
rectMask: {fileID: 7425566603516801919}
|
||||
@@ -526,6 +528,99 @@ MonoBehaviour:
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &3983328028282460839
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6717870941799174515}
|
||||
- component: {fileID: 6334910097310371923}
|
||||
- component: {fileID: 8821021733826365168}
|
||||
- component: {fileID: 9163925342151684341}
|
||||
m_Layer: 5
|
||||
m_Name: RainbowOut
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6717870941799174515
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3983328028282460839}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 8293076336493130222}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &6334910097310371923
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3983328028282460839}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &8821021733826365168
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3983328028282460839}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.RawImage
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Texture: {fileID: 0}
|
||||
m_UVRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: -1
|
||||
--- !u!114 &9163925342151684341
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3983328028282460839}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 2fd09147b9e9d42a48d6ddc915ddc3d2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: SkiaSharp.Unity::SkiaSharp.Unity.SkottiePlayerV2
|
||||
lottieFile: {fileID: 4900000, guid: 589505308c5daf449800f30dd4b92ce7, type: 3}
|
||||
customResolution: 0
|
||||
resWidth: 250
|
||||
resHeight: 250
|
||||
stateName:
|
||||
resetAfterFinished: 0
|
||||
autoPlay: 0
|
||||
loop: 0
|
||||
--- !u!1 &4270065472017787841
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -841,6 +936,99 @@ MonoBehaviour:
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &5382650426034128680
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6108475066390421500}
|
||||
- component: {fileID: 2398693306920598044}
|
||||
- component: {fileID: 7920249735731934357}
|
||||
- component: {fileID: 3566391948883171773}
|
||||
m_Layer: 5
|
||||
m_Name: RainbowIn
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6108475066390421500
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5382650426034128680}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 8293076336493130222}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &2398693306920598044
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5382650426034128680}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7920249735731934357
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5382650426034128680}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.RawImage
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Texture: {fileID: 0}
|
||||
m_UVRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: -1
|
||||
--- !u!114 &3566391948883171773
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5382650426034128680}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 2fd09147b9e9d42a48d6ddc915ddc3d2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: SkiaSharp.Unity::SkiaSharp.Unity.SkottiePlayerV2
|
||||
lottieFile: {fileID: 4900000, guid: 622be2ef9d5e27d45a9deaf7ed805f5f, type: 3}
|
||||
customResolution: 0
|
||||
resWidth: 250
|
||||
resHeight: 250
|
||||
stateName:
|
||||
resetAfterFinished: 0
|
||||
autoPlay: 0
|
||||
loop: 0
|
||||
--- !u!1 &5867455130109727138
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -56,7 +56,7 @@ public class BirdEyesBehavior : ManagedBehaviour
|
||||
_statemachine.ChangeState("BirdSpawned");
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreRequested(string serializedData)
|
||||
internal override void OnSceneRestoreRequested(string serializedData)
|
||||
{
|
||||
base.OnSceneRestoreRequested(serializedData);
|
||||
|
||||
@@ -75,7 +75,7 @@ public class BirdEyesBehavior : ManagedBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
return _wolterisoutTriggered.ToString();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
"OptimizedRope",
|
||||
"AudioSourceEvents",
|
||||
"NewAssembly",
|
||||
"SkiaSharp.Unity",
|
||||
"SkiaSharp.Editor",
|
||||
"SkiaSharp",
|
||||
"Unity.Cinemachine"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using AppleHills.Core.Settings;
|
||||
using UnityEngine;
|
||||
using Core;
|
||||
@@ -33,11 +33,9 @@ namespace Bootstrap
|
||||
// Run very early - need to set up loading screen before other systems initialize
|
||||
public override int ManagedAwakePriority => 5;
|
||||
|
||||
protected override void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // Register with LifecycleManager
|
||||
|
||||
LogDebugMessage("BootSceneController.Awake() - Initializing loading screen DURING bootstrap");
|
||||
Logging.Debug("BootSceneController.Awake() - Initializing loading screen DURING bootstrap");
|
||||
|
||||
// Validate loading screen exists
|
||||
if (initialLoadingScreen == null)
|
||||
@@ -71,11 +69,11 @@ namespace Bootstrap
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
LogDebugMessage("BootSceneController.OnManagedAwake() - Boot is GUARANTEED complete, starting scene loading");
|
||||
Logging.Debug("BootSceneController.OnManagedStart() - Boot is GUARANTEED complete, starting scene loading");
|
||||
|
||||
// Boot is GUARANTEED complete at this point - that's the whole point of OnManagedAwake!
|
||||
// Boot is GUARANTEED complete at this point - that's the whole point of OnManagedStart!
|
||||
// No need to subscribe to OnBootCompleted or check CustomBoot.Initialised
|
||||
_bootComplete = true;
|
||||
_currentPhase = LoadingPhase.SceneLoading;
|
||||
@@ -102,12 +100,12 @@ namespace Bootstrap
|
||||
/// </summary>
|
||||
private void OnInitialLoadingComplete()
|
||||
{
|
||||
LogDebugMessage("Initial loading screen fully hidden, boot sequence completed");
|
||||
Logging.Debug("Initial loading screen fully hidden, boot sequence completed");
|
||||
|
||||
// Play the intro cinematic if available
|
||||
if (CinematicsManager.Instance != null)
|
||||
{
|
||||
LogDebugMessage("Attempting to play intro cinematic");
|
||||
Logging.Debug("Attempting to play intro cinematic");
|
||||
|
||||
// Use LoadAndPlayCinematic to play the intro sequence
|
||||
CinematicsManager.Instance.LoadAndPlayCinematic("IntroSequence", false);
|
||||
@@ -149,13 +147,13 @@ namespace Bootstrap
|
||||
{
|
||||
if (debugMode)
|
||||
{
|
||||
LogDebugMessage($"Bootstrap progress: {progress:P0}, Combined: {GetCombinedProgress():P0}");
|
||||
Logging.Debug($"Bootstrap progress: {progress:P0}, Combined: {GetCombinedProgress():P0}");
|
||||
}
|
||||
}
|
||||
|
||||
private void LogDebugInfo()
|
||||
{
|
||||
LogDebugMessage($"Debug - Phase: {_currentPhase}, Bootstrap: {CustomBoot.CurrentProgress:P0}, " +
|
||||
Logging.Debug($"Debug - Phase: {_currentPhase}, Bootstrap: {CustomBoot.CurrentProgress:P0}, " +
|
||||
$"Scene: {_sceneLoadingProgress:P0}, Combined: {GetCombinedProgress():P0}, Boot Complete: {_bootComplete}");
|
||||
}
|
||||
|
||||
@@ -172,7 +170,7 @@ namespace Bootstrap
|
||||
|
||||
private async void LoadMainScene()
|
||||
{
|
||||
LogDebugMessage($"Loading main menu scene: {mainSceneName}");
|
||||
Logging.Debug($"Loading main menu scene: {mainSceneName}");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -186,7 +184,7 @@ namespace Bootstrap
|
||||
|
||||
if (debugMode)
|
||||
{
|
||||
LogDebugMessage($"Scene loading raw: {value:P0}, Combined: {GetCombinedProgress():P0}");
|
||||
Logging.Debug($"Scene loading raw: {value:P0}, Combined: {GetCombinedProgress():P0}");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -210,13 +208,13 @@ namespace Bootstrap
|
||||
_sceneLoadingProgress = 1f;
|
||||
|
||||
// CRITICAL: Broadcast lifecycle events so components get their OnSceneReady callbacks
|
||||
LogDebugMessage($"Broadcasting OnSceneReady for: {mainSceneName}");
|
||||
Logging.Debug($"Broadcasting OnSceneReady for: {mainSceneName}");
|
||||
LifecycleManager.Instance?.BroadcastSceneReady(mainSceneName);
|
||||
|
||||
// Restore scene data for the main menu
|
||||
if (SaveLoadManager.Instance != null)
|
||||
{
|
||||
LogDebugMessage($"Restoring scene data for: {mainSceneName}");
|
||||
Logging.Debug($"Restoring scene data for: {mainSceneName}");
|
||||
SaveLoadManager.Instance.RestoreSceneData();
|
||||
}
|
||||
|
||||
@@ -246,7 +244,7 @@ namespace Bootstrap
|
||||
Scene currentScene = SceneManager.GetActiveScene();
|
||||
string startingSceneName = currentScene.name;
|
||||
|
||||
LogDebugMessage($"Unloading StartingScene: {startingSceneName}");
|
||||
Logging.Debug($"Unloading StartingScene: {startingSceneName}");
|
||||
|
||||
// Unload the StartingScene
|
||||
await SceneManager.UnloadSceneAsync(startingSceneName);
|
||||
@@ -255,14 +253,14 @@ namespace Bootstrap
|
||||
Scene mainMenuScene = SceneManager.GetSceneByName(mainSceneName);
|
||||
SceneManager.SetActiveScene(mainMenuScene);
|
||||
|
||||
LogDebugMessage($"Transition complete: {startingSceneName} unloaded, {mainSceneName} is now active");
|
||||
Logging.Debug($"Transition complete: {startingSceneName} unloaded, {mainSceneName} is now active");
|
||||
|
||||
// Destroy the boot scene controller since its job is done
|
||||
Destroy(gameObject);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Warning($"[BootSceneController] Error unloading StartingScene: {e.Message}");
|
||||
Logging.Warning($"Error unloading StartingScene: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,13 +281,5 @@ namespace Bootstrap
|
||||
_progressAction?.Invoke(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void LogDebugMessage(string message)
|
||||
{
|
||||
if ( _logVerbosity <= LogVerbosity.Debug)
|
||||
{
|
||||
Logging.Debug($"[BootSceneController] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using AppleHills.Core.Settings;
|
||||
using Core;
|
||||
using Core.Lifecycle;
|
||||
using UnityEngine;
|
||||
@@ -105,7 +104,7 @@ namespace Bootstrap
|
||||
// Notify the LifecycleManager that boot is complete
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
LogDebugMessage("Calling LifecycleManager.OnBootCompletionTriggered()");
|
||||
Logging.Debug("Calling LifecycleManager.OnBootCompletionTriggered()");
|
||||
if (LifecycleManager.Instance != null)
|
||||
{
|
||||
LifecycleManager.Instance.OnBootCompletionTriggered();
|
||||
@@ -127,7 +126,7 @@ namespace Bootstrap
|
||||
// Notify the LifecycleManager that boot is complete
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
LogDebugMessage("Calling LifecycleManager.OnBootCompletionTriggered()");
|
||||
Logging.Debug("Calling LifecycleManager.OnBootCompletionTriggered()");
|
||||
if (LifecycleManager.Instance != null)
|
||||
{
|
||||
LifecycleManager.Instance.OnBootCompletionTriggered();
|
||||
@@ -238,16 +237,7 @@ namespace Bootstrap
|
||||
{
|
||||
CurrentProgress = Mathf.Clamp01(progress);
|
||||
OnBootProgressChanged?.Invoke(CurrentProgress);
|
||||
LogDebugMessage($"Progress: {CurrentProgress:P0}");
|
||||
}
|
||||
|
||||
private static void LogDebugMessage(string message)
|
||||
{
|
||||
if (DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().bootstrapLogVerbosity <=
|
||||
LogVerbosity.Debug)
|
||||
{
|
||||
Logging.Debug($"[CustomBoot] {message}");
|
||||
}
|
||||
Logging.Debug($"Progress: {CurrentProgress:P0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@ namespace Bootstrap
|
||||
float displayProgress = Mathf.Min(steadyProgress, actualProgress);
|
||||
|
||||
// Log the progress values for debugging
|
||||
LogDebugMessage($"Progress - Default: {steadyProgress:F2}, Actual: {actualProgress:F2}, Display: {displayProgress:F2}");
|
||||
Logging.Debug($"Progress - Default: {steadyProgress:F2}, Actual: {actualProgress:F2}, Display: {displayProgress:F2}");
|
||||
|
||||
// Directly set the progress bar fill amount without smoothing
|
||||
if (progressBarImage != null)
|
||||
@@ -151,7 +151,7 @@ namespace Bootstrap
|
||||
if (steadyProgress >= 1.0f && displayProgress >= 1.0f)
|
||||
{
|
||||
_animationComplete = true;
|
||||
LogDebugMessage("Animation complete");
|
||||
Logging.Debug("Animation complete");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace Bootstrap
|
||||
if (progressBarImage != null)
|
||||
{
|
||||
progressBarImage.fillAmount = 1.0f;
|
||||
LogDebugMessage("Final progress set to 1.0");
|
||||
Logging.Debug("Final progress set to 1.0");
|
||||
}
|
||||
|
||||
// Hide the screen if loading is also complete
|
||||
@@ -172,7 +172,7 @@ namespace Bootstrap
|
||||
if (loadingScreenContainer != null)
|
||||
{
|
||||
loadingScreenContainer.SetActive(false);
|
||||
LogDebugMessage("Animation AND loading complete, hiding screen");
|
||||
Logging.Debug("Animation AND loading complete, hiding screen");
|
||||
|
||||
// Invoke the callback when fully hidden
|
||||
_onLoadingScreenFullyHidden?.Invoke();
|
||||
@@ -189,7 +189,7 @@ namespace Bootstrap
|
||||
/// </summary>
|
||||
public void HideLoadingScreen()
|
||||
{
|
||||
LogDebugMessage("Loading complete, marking loading as finished");
|
||||
Logging.Debug("Loading complete, marking loading as finished");
|
||||
|
||||
// Mark that loading is complete
|
||||
_loadingComplete = true;
|
||||
@@ -200,7 +200,7 @@ namespace Bootstrap
|
||||
if (loadingScreenContainer != null)
|
||||
{
|
||||
loadingScreenContainer.SetActive(false);
|
||||
LogDebugMessage("Animation already complete, hiding screen immediately");
|
||||
Logging.Debug("Animation already complete, hiding screen immediately");
|
||||
|
||||
// Invoke the callback when fully hidden
|
||||
_onLoadingScreenFullyHidden?.Invoke();
|
||||
@@ -210,7 +210,7 @@ namespace Bootstrap
|
||||
}
|
||||
else
|
||||
{
|
||||
LogDebugMessage("Animation still in progress, waiting for it to complete");
|
||||
Logging.Debug("Animation still in progress, waiting for it to complete");
|
||||
// The coroutine will handle hiding when animation completes
|
||||
}
|
||||
}
|
||||
@@ -244,13 +244,5 @@ namespace Bootstrap
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
private void LogDebugMessage(string message)
|
||||
{
|
||||
if ( _logVerbosity <= LogVerbosity.Debug)
|
||||
{
|
||||
Logging.Debug($"[InitialLoadingScreen] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,15 +39,13 @@ namespace Cinematics
|
||||
|
||||
public override int ManagedAwakePriority => 170; // Cinematic systems
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
Logging.Debug("[CinematicsManager] Initialized");
|
||||
}
|
||||
|
||||
@@ -15,11 +15,10 @@ namespace Cinematics
|
||||
private float _holdStartTime;
|
||||
private bool _isHolding;
|
||||
private bool _skipPerformed;
|
||||
private bool _initialized = false;
|
||||
|
||||
public override int ManagedAwakePriority => 180; // Cinematic UI
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Reset the progress bar
|
||||
if (radialProgressBar != null)
|
||||
|
||||
@@ -37,14 +37,12 @@ namespace Core
|
||||
// ManagedBehaviour configuration
|
||||
public override int ManagedAwakePriority => 10; // Core infrastructure - runs early
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
|
||||
// Create settings providers - must happen in Awake so other managers can access settings in their ManagedAwake
|
||||
// Create settings providers - must happen in OnManagedAwake so other managers can access settings in their ManagedStart
|
||||
SettingsProvider.Instance.gameObject.name = "Settings Provider";
|
||||
DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider";
|
||||
|
||||
@@ -57,9 +55,9 @@ namespace Core
|
||||
_managerLogVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().gameManagerLogVerbosity;
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Settings are already initialized in Awake()
|
||||
// Settings are already initialized in OnManagedAwake()
|
||||
// This is available for future initialization that depends on other managers
|
||||
}
|
||||
|
||||
@@ -79,7 +77,7 @@ namespace Core
|
||||
component.Pause();
|
||||
}
|
||||
|
||||
LogDebugMessage($"Registered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}");
|
||||
Logging.Debug($"Registered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +90,7 @@ namespace Core
|
||||
if (component != null && _pausableComponents.Contains(component))
|
||||
{
|
||||
_pausableComponents.Remove(component);
|
||||
LogDebugMessage($"Unregistered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}");
|
||||
Logging.Debug($"Unregistered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +106,7 @@ namespace Core
|
||||
ApplyPause(true);
|
||||
}
|
||||
|
||||
LogDebugMessage($"Pause requested by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
|
||||
Logging.Debug($"Pause requested by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -123,7 +121,7 @@ namespace Core
|
||||
ApplyPause(false);
|
||||
}
|
||||
|
||||
LogDebugMessage($"Pause released by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
|
||||
Logging.Debug($"Pause released by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -162,12 +160,12 @@ namespace Core
|
||||
OnGameResumed?.Invoke();
|
||||
}
|
||||
|
||||
LogDebugMessage($"Game {(shouldPause ? "paused" : "resumed")}. Paused {_pausableComponents.Count} components.");
|
||||
Logging.Debug($"Game {(shouldPause ? "paused" : "resumed")}. Paused {_pausableComponents.Count} components.");
|
||||
}
|
||||
|
||||
private void InitializeSettings()
|
||||
{
|
||||
LogDebugMessage("Starting settings initialization...", "SettingsInitialization", _settingsLogVerbosity);
|
||||
Logging.Debug("Starting settings initialization...");
|
||||
|
||||
// Load settings synchronously
|
||||
var playerSettings = SettingsProvider.Instance.LoadSettingsSynchronous<PlayerFollowerSettings>();
|
||||
@@ -178,7 +176,7 @@ namespace Core
|
||||
if (playerSettings != null)
|
||||
{
|
||||
ServiceLocator.Register<IPlayerFollowerSettings>(playerSettings);
|
||||
LogDebugMessage("PlayerFollowerSettings registered successfully", "SettingsInitialization", _settingsLogVerbosity);
|
||||
Logging.Debug("PlayerFollowerSettings registered successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -188,7 +186,7 @@ namespace Core
|
||||
if (interactionSettings != null)
|
||||
{
|
||||
ServiceLocator.Register<IInteractionSettings>(interactionSettings);
|
||||
LogDebugMessage("InteractionSettings registered successfully", "SettingsInitialization", _settingsLogVerbosity);
|
||||
Logging.Debug("InteractionSettings registered successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -198,7 +196,7 @@ namespace Core
|
||||
if (minigameSettings != null)
|
||||
{
|
||||
ServiceLocator.Register<IDivingMinigameSettings>(minigameSettings);
|
||||
LogDebugMessage("MinigameSettings registered successfully", "SettingsInitialization", _settingsLogVerbosity);
|
||||
Logging.Debug("MinigameSettings registered successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -209,7 +207,7 @@ namespace Core
|
||||
_settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null;
|
||||
if (_settingsLoaded)
|
||||
{
|
||||
LogDebugMessage("All settings loaded and registered with ServiceLocator", "SettingsInitialization", _settingsLogVerbosity);
|
||||
Logging.Debug("All settings loaded and registered with ServiceLocator");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -222,7 +220,7 @@ namespace Core
|
||||
/// </summary>
|
||||
private void InitializeDeveloperSettings()
|
||||
{
|
||||
LogDebugMessage("Starting developer settings initialization...", "SettingsInitialization", _settingsLogVerbosity);
|
||||
Logging.Debug("Starting developer settings initialization...");
|
||||
|
||||
// Load developer settings
|
||||
var divingDevSettings = DeveloperSettingsProvider.Instance.GetSettings<DivingDeveloperSettings>();
|
||||
@@ -232,7 +230,7 @@ namespace Core
|
||||
|
||||
if (_developerSettingsLoaded)
|
||||
{
|
||||
LogDebugMessage("All developer settings loaded successfully", "SettingsInitialization", _settingsLogVerbosity);
|
||||
Logging.Debug("All developer settings loaded successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -267,19 +265,6 @@ namespace Core
|
||||
return DeveloperSettingsProvider.Instance?.GetSettings<T>();
|
||||
}
|
||||
|
||||
private void LogDebugMessage(string message, string prefix = "GameManager", LogVerbosity verbosity = LogVerbosity.None)
|
||||
{
|
||||
if (verbosity == LogVerbosity.None)
|
||||
{
|
||||
verbosity = _managerLogVerbosity;
|
||||
}
|
||||
|
||||
if ( verbosity <= LogVerbosity.Debug)
|
||||
{
|
||||
Logging.Debug($"[{prefix}] {message}");
|
||||
}
|
||||
}
|
||||
|
||||
// LEFTOVER LEGACY SETTINGS
|
||||
public float PlayerStopDistance => GetSettings<IInteractionSettings>()?.PlayerStopDistance ?? 6.0f;
|
||||
public float PlayerStopDistanceDirectInteraction => GetSettings<IInteractionSettings>()?.PlayerStopDistanceDirectInteraction ?? 2.0f;
|
||||
|
||||
@@ -50,20 +50,18 @@ namespace Core
|
||||
|
||||
public override int ManagedAwakePriority => 75; // Item registry
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
Logging.Debug("[ItemManager] Initialized");
|
||||
}
|
||||
|
||||
protected override void OnSceneReady()
|
||||
internal override void OnSceneReady()
|
||||
{
|
||||
// Replaces SceneLoadStarted subscription for clearing registrations
|
||||
ClearAllRegistrations();
|
||||
@@ -144,7 +142,6 @@ namespace Core
|
||||
{
|
||||
s.OnCorrectItemSlotted -= ItemSlot_OnCorrectItemSlotted;
|
||||
s.OnIncorrectItemSlotted -= ItemSlot_OnIncorrectItemSlotted;
|
||||
s.OnForbiddenItemSlotted -= ItemSlot_OnForbiddenItemSlotted;
|
||||
s.OnItemSlotRemoved -= ItemSlot_OnItemSlotRemoved;
|
||||
}
|
||||
}
|
||||
@@ -191,7 +188,6 @@ namespace Core
|
||||
// Subscribe to all slot events
|
||||
slot.OnCorrectItemSlotted += ItemSlot_OnCorrectItemSlotted;
|
||||
slot.OnIncorrectItemSlotted += ItemSlot_OnIncorrectItemSlotted;
|
||||
slot.OnForbiddenItemSlotted += ItemSlot_OnForbiddenItemSlotted;
|
||||
slot.OnItemSlotRemoved += ItemSlot_OnItemSlotRemoved;
|
||||
}
|
||||
}
|
||||
@@ -204,7 +200,6 @@ namespace Core
|
||||
// Unsubscribe from all slot events
|
||||
slot.OnCorrectItemSlotted -= ItemSlot_OnCorrectItemSlotted;
|
||||
slot.OnIncorrectItemSlotted -= ItemSlot_OnIncorrectItemSlotted;
|
||||
slot.OnForbiddenItemSlotted -= ItemSlot_OnForbiddenItemSlotted;
|
||||
slot.OnItemSlotRemoved -= ItemSlot_OnItemSlotRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -123,7 +123,24 @@ namespace Core.Lifecycle
|
||||
// ALWAYS add to managedAwakeList - this is the master list used for save/load
|
||||
InsertSorted(managedAwakeList, component, component.ManagedAwakePriority);
|
||||
|
||||
// Handle ManagedAwake timing based on boot state
|
||||
// Register for all scene lifecycle hooks
|
||||
InsertSorted(sceneUnloadingList, component, component.SceneUnloadingPriority);
|
||||
InsertSorted(sceneReadyList, component, component.SceneReadyPriority);
|
||||
InsertSorted(saveRequestedList, component, component.SavePriority);
|
||||
InsertSorted(restoreRequestedList, component, component.RestorePriority);
|
||||
InsertSorted(destroyList, component, component.DestroyPriority);
|
||||
|
||||
// Call OnManagedAwake immediately after registration (early initialization hook)
|
||||
try
|
||||
{
|
||||
component.OnManagedAwake();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[LifecycleManager] Error in OnManagedAwake for {component.gameObject.name}: {ex}");
|
||||
}
|
||||
|
||||
// Handle OnManagedStart timing based on boot state
|
||||
if (isBootComplete)
|
||||
{
|
||||
// Check if we're currently loading a scene
|
||||
@@ -136,27 +153,20 @@ namespace Core.Lifecycle
|
||||
else
|
||||
{
|
||||
// Truly late registration (component enabled after scene is ready)
|
||||
// Call OnManagedAwake immediately since boot already completed
|
||||
LogDebug($"Late registration: Calling OnManagedAwake immediately for {component.gameObject.name}");
|
||||
// Call OnManagedStart immediately since boot already completed
|
||||
LogDebug($"Late registration: Calling OnManagedStart immediately for {component.gameObject.name}");
|
||||
try
|
||||
{
|
||||
component.InvokeManagedAwake();
|
||||
component.OnManagedStart();
|
||||
HandleAutoRegistrations(component);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[LifecycleManager] Error in OnManagedAwake for {component.gameObject.name}: {ex}");
|
||||
Debug.LogError($"[LifecycleManager] Error in OnManagedStart for {component.gameObject.name}: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
// If boot not complete, component stays in list and will be processed by BroadcastManagedAwake()
|
||||
|
||||
// Register for all scene lifecycle hooks
|
||||
InsertSorted(sceneUnloadingList, component, component.SceneUnloadingPriority);
|
||||
InsertSorted(sceneReadyList, component, component.SceneReadyPriority);
|
||||
InsertSorted(saveRequestedList, component, component.SavePriority);
|
||||
InsertSorted(restoreRequestedList, component, component.RestorePriority);
|
||||
InsertSorted(destroyList, component, component.DestroyPriority);
|
||||
// If boot not complete, component stays in list and will be processed by BroadcastManagedStart()
|
||||
|
||||
// If this scene is already ready (and we're not in loading mode), call OnSceneReady immediately
|
||||
if (!isLoadingScene && currentSceneReady == sceneName)
|
||||
@@ -164,7 +174,7 @@ namespace Core.Lifecycle
|
||||
LogDebug($"Late registration: Calling OnSceneReady immediately for {component.gameObject.name}");
|
||||
try
|
||||
{
|
||||
component.InvokeSceneReady();
|
||||
component.OnSceneReady();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -202,7 +212,7 @@ namespace Core.Lifecycle
|
||||
|
||||
/// <summary>
|
||||
/// Called by CustomBoot when boot completes.
|
||||
/// Broadcasts ManagedAwake to all registered components.
|
||||
/// Broadcasts ManagedStart to all registered components.
|
||||
/// </summary>
|
||||
public void OnBootCompletionTriggered()
|
||||
{
|
||||
@@ -210,16 +220,16 @@ namespace Core.Lifecycle
|
||||
return;
|
||||
|
||||
LogDebug("=== Boot Completion Triggered ===");
|
||||
BroadcastManagedAwake();
|
||||
BroadcastManagedStart();
|
||||
isBootComplete = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcast OnManagedAwake to all registered components (priority ordered).
|
||||
/// Broadcast OnManagedStart to all registered components (priority ordered).
|
||||
/// </summary>
|
||||
private void BroadcastManagedAwake()
|
||||
private void BroadcastManagedStart()
|
||||
{
|
||||
LogDebug($"Broadcasting ManagedAwake to {managedAwakeList.Count} components");
|
||||
LogDebug($"Broadcasting ManagedStart to {managedAwakeList.Count} components");
|
||||
|
||||
// Create a copy to avoid collection modification during iteration
|
||||
var componentsCopy = new List<ManagedBehaviour>(managedAwakeList);
|
||||
@@ -230,12 +240,12 @@ namespace Core.Lifecycle
|
||||
|
||||
try
|
||||
{
|
||||
component.InvokeManagedAwake();
|
||||
component.OnManagedStart();
|
||||
HandleAutoRegistrations(component);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[LifecycleManager] Error in OnManagedAwake for {component.gameObject.name}: {ex}");
|
||||
Debug.LogError($"[LifecycleManager] Error in OnManagedStart for {component.gameObject.name}: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,20 +285,20 @@ namespace Core.Lifecycle
|
||||
// Sort by ManagedAwake priority (lower values first)
|
||||
pendingSceneComponents.Sort((a, b) => a.ManagedAwakePriority.CompareTo(b.ManagedAwakePriority));
|
||||
|
||||
// Call OnManagedAwake in priority order
|
||||
// Call OnManagedStart in priority order
|
||||
foreach (var component in pendingSceneComponents)
|
||||
{
|
||||
if (component == null) continue;
|
||||
|
||||
try
|
||||
{
|
||||
component.InvokeManagedAwake();
|
||||
component.OnManagedStart();
|
||||
HandleAutoRegistrations(component);
|
||||
LogDebug($"Processed batched component: {component.gameObject.name} (Priority: {component.ManagedAwakePriority})");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[LifecycleManager] Error in OnManagedAwake for batched component {component.gameObject.name}: {ex}");
|
||||
Debug.LogError($"[LifecycleManager] Error in OnManagedStart for batched component {component.gameObject.name}: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,7 +325,7 @@ namespace Core.Lifecycle
|
||||
{
|
||||
try
|
||||
{
|
||||
component.InvokeSceneUnloading();
|
||||
component.OnSceneUnloading();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -351,7 +361,7 @@ namespace Core.Lifecycle
|
||||
{
|
||||
try
|
||||
{
|
||||
component.InvokeSceneReady();
|
||||
component.OnSceneReady();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -379,7 +389,7 @@ namespace Core.Lifecycle
|
||||
|
||||
try
|
||||
{
|
||||
string serializedData = component.InvokeSceneSaveRequested();
|
||||
string serializedData = component.OnSceneSaveRequested();
|
||||
if (!string.IsNullOrEmpty(serializedData))
|
||||
{
|
||||
string saveId = component.SaveId;
|
||||
@@ -415,7 +425,7 @@ namespace Core.Lifecycle
|
||||
|
||||
try
|
||||
{
|
||||
string serializedData = component.InvokeGlobalSaveRequested();
|
||||
string serializedData = component.OnGlobalSaveRequested();
|
||||
if (!string.IsNullOrEmpty(serializedData))
|
||||
{
|
||||
saveData[component.SaveId] = serializedData;
|
||||
@@ -455,7 +465,7 @@ namespace Core.Lifecycle
|
||||
{
|
||||
try
|
||||
{
|
||||
component.InvokeSceneRestoreRequested(serializedData);
|
||||
component.OnSceneRestoreRequested(serializedData);
|
||||
restoredCount++;
|
||||
LogDebug($"Restored scene data to: {component.SaveId}");
|
||||
}
|
||||
@@ -486,7 +496,7 @@ namespace Core.Lifecycle
|
||||
|
||||
try
|
||||
{
|
||||
component.InvokeSceneRestoreCompleted();
|
||||
component.OnSceneRestoreCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -519,7 +529,7 @@ namespace Core.Lifecycle
|
||||
{
|
||||
try
|
||||
{
|
||||
component.InvokeGlobalRestoreRequested(serializedData);
|
||||
component.OnGlobalRestoreRequested(serializedData);
|
||||
restoredCount++;
|
||||
LogDebug($"Restored global data to: {component.SaveId}");
|
||||
}
|
||||
@@ -551,7 +561,7 @@ namespace Core.Lifecycle
|
||||
|
||||
try
|
||||
{
|
||||
component.InvokeGlobalLoadCompleted();
|
||||
component.OnGlobalLoadCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -578,7 +588,7 @@ namespace Core.Lifecycle
|
||||
|
||||
try
|
||||
{
|
||||
component.InvokeGlobalSaveStarted();
|
||||
component.OnGlobalSaveStarted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Core.Lifecycle
|
||||
{
|
||||
@@ -12,7 +11,7 @@ namespace Core.Lifecycle
|
||||
#region Priority Properties
|
||||
|
||||
/// <summary>
|
||||
/// Priority for OnManagedAwake (lower values execute first).
|
||||
/// Priority for OnManagedStart (lower values execute first).
|
||||
/// Default: 100
|
||||
/// </summary>
|
||||
public virtual int ManagedAwakePriority => 100;
|
||||
@@ -81,23 +80,6 @@ namespace Core.Lifecycle
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Accessors (for LifecycleManager)
|
||||
|
||||
// Public wrappers to invoke protected lifecycle methods
|
||||
public void InvokeManagedAwake() => OnManagedAwake();
|
||||
public void InvokeSceneUnloading() => OnSceneUnloading();
|
||||
public void InvokeSceneReady() => OnSceneReady();
|
||||
public string InvokeSceneSaveRequested() => OnSceneSaveRequested();
|
||||
public void InvokeSceneRestoreRequested(string data) => OnSceneRestoreRequested(data);
|
||||
public void InvokeSceneRestoreCompleted() => OnSceneRestoreCompleted();
|
||||
public string InvokeGlobalSaveRequested() => OnGlobalSaveRequested();
|
||||
public void InvokeGlobalRestoreRequested(string data) => OnGlobalRestoreRequested(data);
|
||||
public void InvokeManagedDestroy() => OnManagedDestroy();
|
||||
public void InvokeGlobalLoadCompleted() => OnGlobalLoadCompleted();
|
||||
public void InvokeGlobalSaveStarted() => OnGlobalSaveStarted();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private bool _isRegistered;
|
||||
@@ -108,9 +90,9 @@ namespace Core.Lifecycle
|
||||
|
||||
/// <summary>
|
||||
/// Unity Awake - automatically registers with LifecycleManager.
|
||||
/// IMPORTANT: Derived classes that override Awake MUST call base.Awake()
|
||||
/// SEALED: Cannot be overridden. Use OnManagedAwake() for early initialization or OnManagedStart() for late initialization.
|
||||
/// </summary>
|
||||
protected virtual void Awake()
|
||||
private void Awake()
|
||||
{
|
||||
if (LifecycleManager.Instance != null)
|
||||
{
|
||||
@@ -152,14 +134,27 @@ namespace Core.Lifecycle
|
||||
|
||||
#region Managed Lifecycle Hooks
|
||||
|
||||
/// <summary>
|
||||
/// Called immediately during registration (during Awake).
|
||||
/// Use for early initialization such as setting singleton instances.
|
||||
/// TIMING: Fires during component's Awake(), no execution order guarantees between components.
|
||||
/// NOT priority-ordered - fires whenever Unity calls this component's Awake().
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
internal virtual void OnManagedAwake()
|
||||
{
|
||||
// Override in derived classes
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called once per component after bootstrap completes.
|
||||
/// GUARANTEE: Bootstrap resources are available, all managers are initialized.
|
||||
/// For boot-time components: Called during LifecycleManager.BroadcastManagedAwake (priority ordered).
|
||||
/// For boot-time components: Called during LifecycleManager.BroadcastManagedStart (priority ordered).
|
||||
/// For late-registered components: Called immediately upon registration (bootstrap already complete).
|
||||
/// Replaces the old Awake + InitializePostBoot pattern.
|
||||
/// Use for initialization that depends on other systems.
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual void OnManagedAwake()
|
||||
internal virtual void OnManagedStart()
|
||||
{
|
||||
// Override in derived classes
|
||||
}
|
||||
@@ -168,8 +163,9 @@ namespace Core.Lifecycle
|
||||
/// Called before the scene this component belongs to is unloaded.
|
||||
/// Called in REVERSE priority order (higher values execute first).
|
||||
/// Use for scene-specific cleanup.
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual void OnSceneUnloading()
|
||||
internal virtual void OnSceneUnloading()
|
||||
{
|
||||
// Override in derived classes
|
||||
}
|
||||
@@ -178,8 +174,9 @@ namespace Core.Lifecycle
|
||||
/// Called after the scene this component belongs to has finished loading.
|
||||
/// Called in priority order (lower values execute first).
|
||||
/// Use for scene-specific initialization.
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual void OnSceneReady()
|
||||
internal virtual void OnSceneReady()
|
||||
{
|
||||
// Override in derived classes
|
||||
}
|
||||
@@ -193,8 +190,10 @@ namespace Core.Lifecycle
|
||||
/// - Called BEFORE scene unload during scene transitions
|
||||
/// - Frequency: Every scene transition
|
||||
/// - Use for: Level progress, object positions, puzzle states
|
||||
///
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual string OnSceneSaveRequested()
|
||||
internal virtual string OnSceneSaveRequested()
|
||||
{
|
||||
return null; // Default: no data to save
|
||||
}
|
||||
@@ -211,8 +210,10 @@ namespace Core.Lifecycle
|
||||
/// - Called AFTER scene load, during OnSceneReady phase
|
||||
/// - Frequency: Every scene transition
|
||||
/// - Use for: Restoring level progress, object positions, puzzle states
|
||||
///
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual void OnSceneRestoreRequested(string serializedData)
|
||||
internal virtual void OnSceneRestoreRequested(string serializedData)
|
||||
{
|
||||
// Default: no-op
|
||||
}
|
||||
@@ -234,8 +235,10 @@ namespace Core.Lifecycle
|
||||
/// COMMON PATTERN:
|
||||
/// Use this to perform actions that depend on whether data was restored or not.
|
||||
/// Example: Play one-time audio only if it hasn't been played before (_hasPlayed == false).
|
||||
///
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual void OnSceneRestoreCompleted()
|
||||
internal virtual void OnSceneRestoreCompleted()
|
||||
{
|
||||
// Default: no-op
|
||||
}
|
||||
@@ -248,8 +251,10 @@ namespace Core.Lifecycle
|
||||
/// - Called ONCE on game boot after save file is read
|
||||
/// - NOT called during scene transitions
|
||||
/// - Use for: Player inventory, unlocked features, card collections
|
||||
///
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual void OnGlobalRestoreRequested(string serializedData)
|
||||
internal virtual void OnGlobalRestoreRequested(string serializedData)
|
||||
{
|
||||
// Default: no-op
|
||||
}
|
||||
@@ -263,8 +268,10 @@ namespace Core.Lifecycle
|
||||
/// - Called ONCE before save file is written (on quit, manual save, etc.)
|
||||
/// - NOT called during scene transitions
|
||||
/// - Use for: Player inventory, unlocked features, card collections
|
||||
///
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual string OnGlobalSaveRequested()
|
||||
internal virtual string OnGlobalSaveRequested()
|
||||
{
|
||||
return null; // Default: no data to save
|
||||
}
|
||||
@@ -278,8 +285,10 @@ namespace Core.Lifecycle
|
||||
/// - Called ONCE on game boot after all restore operations complete
|
||||
/// - NOT called during scene transitions
|
||||
/// - Use for: Triggering UI updates, broadcasting load events
|
||||
///
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual void OnGlobalLoadCompleted()
|
||||
internal virtual void OnGlobalLoadCompleted()
|
||||
{
|
||||
// Default: no-op
|
||||
}
|
||||
@@ -293,8 +302,10 @@ namespace Core.Lifecycle
|
||||
/// - Called ONCE before save file is written
|
||||
/// - NOT called during scene transitions
|
||||
/// - Use for: Final validation, cleanup operations
|
||||
///
|
||||
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual void OnGlobalSaveStarted()
|
||||
internal virtual void OnGlobalSaveStarted()
|
||||
{
|
||||
// Default: no-op
|
||||
}
|
||||
@@ -304,8 +315,9 @@ namespace Core.Lifecycle
|
||||
/// Called in REVERSE priority order (higher values execute first).
|
||||
/// NOTE: Most cleanup is automatic (managed events, auto-registrations).
|
||||
/// Only override if you need custom cleanup logic.
|
||||
/// Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||
/// </summary>
|
||||
protected virtual void OnManagedDestroy()
|
||||
internal virtual void OnManagedDestroy()
|
||||
{
|
||||
// Override in derived classes
|
||||
}
|
||||
|
||||
@@ -1,17 +1,144 @@
|
||||
namespace Core
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Centralized logging system with automatic class/method tagging and editor integration.
|
||||
/// Broadcasts log entries to custom editor windows for filtering and analysis.
|
||||
/// </summary>
|
||||
public static class Logging
|
||||
{
|
||||
[System.Diagnostics.Conditional("ENABLE_LOG")]
|
||||
public static void Debug(object message)
|
||||
/// <summary>
|
||||
/// Event fired when a new log entry is added. Subscribe to this in editor windows.
|
||||
/// </summary>
|
||||
public static event Action<LogEntry> OnLogEntryAdded;
|
||||
|
||||
// Store recent logs for late-subscriber editor windows (e.g., windows opened after play mode started)
|
||||
private static readonly List<LogEntry> RecentLogs = new List<LogEntry>();
|
||||
private const int MaxStoredLogs = 5000; // Prevent memory bloat
|
||||
|
||||
/// <summary>
|
||||
/// Get all recent logs. Used by editor windows when they first open.
|
||||
/// </summary>
|
||||
public static IReadOnlyList<LogEntry> GetRecentLogs() => RecentLogs;
|
||||
|
||||
/// <summary>
|
||||
/// Clear all stored logs. Useful for editor windows "Clear" button.
|
||||
/// </summary>
|
||||
public static void ClearLogs()
|
||||
{
|
||||
UnityEngine.Debug.Log(message);
|
||||
RecentLogs.Clear();
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("ENABLE_LOG")]
|
||||
public static void Warning(object message)
|
||||
public static void Debug(string message,
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerMemberName] string memberName = "")
|
||||
{
|
||||
UnityEngine.Debug.LogWarning(message);
|
||||
LogInternal(LogLevel.Debug, message, filePath, memberName);
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("ENABLE_LOG")]
|
||||
public static void Info(string message,
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerMemberName] string memberName = "")
|
||||
{
|
||||
LogInternal(LogLevel.Info, message, filePath, memberName);
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("ENABLE_LOG")]
|
||||
public static void Warning(string message,
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerMemberName] string memberName = "")
|
||||
{
|
||||
LogInternal(LogLevel.Warning, message, filePath, memberName);
|
||||
}
|
||||
|
||||
public static void Error(string message,
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerMemberName] string memberName = "")
|
||||
{
|
||||
LogInternal(LogLevel.Error, message, filePath, memberName);
|
||||
}
|
||||
|
||||
private static void LogInternal(LogLevel level, string message, string filePath, string memberName)
|
||||
{
|
||||
string className = Path.GetFileNameWithoutExtension(filePath);
|
||||
string formattedMessage = $"[{className}][{memberName}] {message}";
|
||||
|
||||
// Create log entry
|
||||
var entry = new LogEntry(className, memberName, message, level, Time.realtimeSinceStartup);
|
||||
|
||||
// Store for late subscribers
|
||||
RecentLogs.Add(entry);
|
||||
if (RecentLogs.Count > MaxStoredLogs)
|
||||
RecentLogs.RemoveAt(0);
|
||||
|
||||
// Broadcast to editor windows (editor-only, won't fire in builds)
|
||||
OnLogEntryAdded?.Invoke(entry);
|
||||
|
||||
// Also log to Unity console
|
||||
switch (level)
|
||||
{
|
||||
case LogLevel.Debug:
|
||||
case LogLevel.Info:
|
||||
UnityEngine.Debug.Log(formattedMessage);
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
UnityEngine.Debug.LogWarning(formattedMessage);
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
UnityEngine.Debug.LogError(formattedMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single log entry with class, method, message, level, and timestamp.
|
||||
/// </summary>
|
||||
public class LogEntry
|
||||
{
|
||||
public string ClassName { get; }
|
||||
public string MethodName { get; }
|
||||
public string Message { get; }
|
||||
public LogLevel Level { get; }
|
||||
public float Timestamp { get; }
|
||||
|
||||
public LogEntry(string className, string methodName, string message, LogLevel level, float timestamp)
|
||||
{
|
||||
ClassName = className;
|
||||
MethodName = methodName;
|
||||
Message = message;
|
||||
Level = level;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formatted message with class and method tags.
|
||||
/// Format: [ClassName][MethodName] Message
|
||||
/// </summary>
|
||||
public string FormattedMessage => $"[{ClassName}][{MethodName}] {Message}";
|
||||
|
||||
/// <summary>
|
||||
/// Full formatted message with timestamp and level.
|
||||
/// Format: [12.34s][Debug][ClassName][MethodName] Message
|
||||
/// </summary>
|
||||
public string FullFormattedMessage => $"[{Timestamp:F2}s][{Level}]{FormattedMessage}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log severity levels.
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
}
|
||||
@@ -129,20 +129,18 @@ namespace AppleHills.Core
|
||||
|
||||
#region Lifecycle Methods
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// QuickAccess has minimal initialization
|
||||
}
|
||||
|
||||
protected override void OnSceneUnloading()
|
||||
internal override void OnSceneUnloading()
|
||||
{
|
||||
// Clear references BEFORE scene unloads for better cleanup timing
|
||||
ClearReferences();
|
||||
|
||||
@@ -46,11 +46,9 @@ namespace Core.SaveLoad
|
||||
// ManagedBehaviour configuration
|
||||
public override int ManagedAwakePriority => 20; // After GameManager and SceneManagerService
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
|
||||
// Initialize critical state immediately
|
||||
@@ -58,7 +56,7 @@ namespace Core.SaveLoad
|
||||
IsRestoringState = false;
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
Logging.Debug("[SaveLoadManager] Initialized");
|
||||
|
||||
@@ -69,19 +67,19 @@ namespace Core.SaveLoad
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSceneReady()
|
||||
internal override void OnSceneReady()
|
||||
{
|
||||
// SaveableInteractables now auto-register via ManagedBehaviour lifecycle
|
||||
// No need to discover and register them manually
|
||||
}
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
// SaveLoadManager orchestrates saves, doesn't participate in them
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override string OnGlobalSaveRequested()
|
||||
internal override string OnGlobalSaveRequested()
|
||||
{
|
||||
// SaveLoadManager orchestrates saves, doesn't participate in them
|
||||
return null;
|
||||
|
||||
@@ -30,9 +30,9 @@ namespace Core
|
||||
// Enable save/load participation
|
||||
public override bool AutoRegisterForSave => true;
|
||||
|
||||
protected override void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake();
|
||||
base.OnManagedAwake();
|
||||
|
||||
_director = GetComponent<PlayableDirector>();
|
||||
if (_director != null)
|
||||
@@ -65,7 +65,7 @@ namespace Core
|
||||
|
||||
#region Save/Load Implementation
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
var saveData = new PlayableDirectorSaveData
|
||||
{
|
||||
@@ -77,7 +77,7 @@ namespace Core
|
||||
return JsonUtility.ToJson(saveData);
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreRequested(string serializedData)
|
||||
internal override void OnSceneRestoreRequested(string serializedData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(serializedData))
|
||||
{
|
||||
|
||||
@@ -47,11 +47,9 @@ namespace Core
|
||||
// ManagedBehaviour configuration
|
||||
public override int ManagedAwakePriority => 15; // Core infrastructure, after GameManager
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
|
||||
// Initialize current scene tracking - critical for scene management
|
||||
@@ -65,17 +63,17 @@ namespace Core
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Set up loading screen reference and events
|
||||
// This must happen in ManagedAwake because LoadingScreenController instance needs to be set first
|
||||
// This must happen in ManagedStart because LoadingScreenController instance needs to be set first
|
||||
_loadingScreen = LoadingScreenController.Instance;
|
||||
SetupLoadingScreenEvents();
|
||||
|
||||
// Load verbosity settings
|
||||
_logVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().sceneLogVerbosity;
|
||||
|
||||
LogDebugMessage($"SceneManagerService initialized, current scene is: {CurrentGameplayScene}");
|
||||
Logging.Debug($"SceneManagerService initialized, current scene is: {CurrentGameplayScene}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -93,19 +91,19 @@ namespace Core
|
||||
if (activeScene.name != BootstrapSceneName)
|
||||
{
|
||||
CurrentGameplayScene = activeScene.name;
|
||||
LogDebugMessage($"Initialized with current scene: {CurrentGameplayScene}");
|
||||
Logging.Debug($"Initialized with current scene: {CurrentGameplayScene}");
|
||||
}
|
||||
// Otherwise default to MainMenu
|
||||
else
|
||||
{
|
||||
CurrentGameplayScene = "AppleHillsOverworld";
|
||||
LogDebugMessage($"Initialized with default scene: {CurrentGameplayScene}");
|
||||
Logging.Debug($"Initialized with default scene: {CurrentGameplayScene}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentGameplayScene = "AppleHillsOverworld";
|
||||
LogDebugMessage($"No valid active scene, defaulting to: {CurrentGameplayScene}");
|
||||
Logging.Debug($"No valid active scene, defaulting to: {CurrentGameplayScene}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +305,7 @@ namespace Core
|
||||
}
|
||||
|
||||
// PHASE 2: Broadcast scene unloading - notify components to cleanup
|
||||
LogDebugMessage($"Broadcasting OnSceneUnloading for: {oldSceneName}");
|
||||
Logging.Debug($"Broadcasting OnSceneUnloading for: {oldSceneName}");
|
||||
LifecycleManager.Instance?.BroadcastSceneUnloading(oldSceneName);
|
||||
|
||||
// PHASE 3: Save scene-specific data via SaveLoadManager (unless skipSave is true)
|
||||
@@ -316,19 +314,19 @@ namespace Core
|
||||
var debugSettings = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>();
|
||||
if (debugSettings.useSaveLoadSystem)
|
||||
{
|
||||
LogDebugMessage($"Saving scene data for: {oldSceneName}");
|
||||
Logging.Debug($"Saving scene data for: {oldSceneName}");
|
||||
SaveLoadManager.Instance.SaveSceneData();
|
||||
}
|
||||
}
|
||||
else if (skipSave)
|
||||
{
|
||||
LogDebugMessage($"Skipping save for: {oldSceneName} (skipSave=true)");
|
||||
Logging.Debug($"Skipping save for: {oldSceneName} (skipSave=true)");
|
||||
}
|
||||
|
||||
// PHASE 4: Clear PuzzleManager state before scene transition
|
||||
if (PuzzleS.PuzzleManager.Instance != null)
|
||||
{
|
||||
LogDebugMessage($"Clearing puzzle state before scene transition");
|
||||
Logging.Debug($"Clearing puzzle state before scene transition");
|
||||
PuzzleS.PuzzleManager.Instance.ClearPuzzleState();
|
||||
}
|
||||
|
||||
@@ -352,7 +350,7 @@ namespace Core
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning($"[SceneManagerService] Previous scene '{oldSceneName}' is not loaded, skipping unload.");
|
||||
Logging.Warning($"Previous scene '{oldSceneName}' is not loaded, skipping unload.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,7 +362,7 @@ namespace Core
|
||||
}
|
||||
|
||||
// PHASE 8: Begin scene loading mode - enables priority-ordered component initialization
|
||||
LogDebugMessage($"Beginning scene load for: {newSceneName}");
|
||||
Logging.Debug($"Beginning scene load for: {newSceneName}");
|
||||
LifecycleManager.Instance?.BeginSceneLoad(newSceneName);
|
||||
|
||||
// PHASE 9: Load new gameplay scene
|
||||
@@ -372,7 +370,7 @@ namespace Core
|
||||
CurrentGameplayScene = newSceneName;
|
||||
|
||||
// PHASE 10: Broadcast scene ready - processes batched components in priority order, then calls OnSceneReady
|
||||
LogDebugMessage($"Broadcasting OnSceneReady for: {newSceneName}");
|
||||
Logging.Debug($"Broadcasting OnSceneReady for: {newSceneName}");
|
||||
LifecycleManager.Instance?.BroadcastSceneReady(newSceneName);
|
||||
|
||||
// PHASE 11: Restore scene-specific data via SaveLoadManager
|
||||
@@ -381,14 +379,14 @@ namespace Core
|
||||
var debugSettings = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>();
|
||||
if (debugSettings.useSaveLoadSystem)
|
||||
{
|
||||
LogDebugMessage($"Restoring scene data for: {newSceneName}");
|
||||
Logging.Debug($"Restoring scene data for: {newSceneName}");
|
||||
SaveLoadManager.Instance.RestoreSceneData();
|
||||
}
|
||||
}
|
||||
else if (skipSave)
|
||||
{
|
||||
SaveLoadManager.Instance.RestoreSceneData();
|
||||
LogDebugMessage($"Skipping restore for: {newSceneName} (skipSave=true)");
|
||||
Logging.Debug($"Skipping restore for: {newSceneName} (skipSave=true)");
|
||||
}
|
||||
|
||||
// PHASE 12: Only hide the loading screen if autoHideLoadingScreen is true
|
||||
@@ -397,13 +395,5 @@ namespace Core
|
||||
_loadingScreen.HideLoadingScreen();
|
||||
}
|
||||
}
|
||||
|
||||
private void LogDebugMessage(string message)
|
||||
{
|
||||
if (_logVerbosity <= LogVerbosity.Debug)
|
||||
{
|
||||
Logging.Debug($"[SceneManagerService] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,20 +21,18 @@ namespace Core
|
||||
// ManagedBehaviour configuration
|
||||
public override int ManagedAwakePriority => 70; // Platform-specific utility
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
|
||||
// Load verbosity settings early (GameManager sets up settings in its Awake)
|
||||
// Load verbosity settings early (GameManager sets up settings in its OnManagedAwake)
|
||||
_logVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().sceneLogVerbosity;
|
||||
|
||||
LogDebugMessage("Initialized");
|
||||
Logging.Debug("Initialized");
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Subscribe to SceneManagerService to enforce orientation on every scene load
|
||||
if (SceneManagerService.Instance != null)
|
||||
@@ -51,7 +49,7 @@ namespace Core
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void OnSceneReady()
|
||||
internal override void OnSceneReady()
|
||||
{
|
||||
// Handle orientation when scene is ready (initial scene)
|
||||
// Note: This fires for the scene that just loaded, LifecycleManager tracks which scene
|
||||
@@ -73,7 +71,7 @@ namespace Core
|
||||
if (sceneName.ToLower().Contains("bootstrap"))
|
||||
{
|
||||
// Bootstrap being loaded additively, don't do anything
|
||||
LogDebugMessage($"Detected bootstrapped scene: '{sceneName}'. Skipping orientation enforcement.");
|
||||
Logging.Debug($"Detected bootstrapped scene: '{sceneName}'. Skipping orientation enforcement.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,23 +81,23 @@ namespace Core
|
||||
}
|
||||
else
|
||||
{
|
||||
LogDebugMessage($"No orientationConfig assigned. Defaulting to Landscape for scene '{sceneName}'");
|
||||
Logging.Debug($"No orientationConfig assigned. Defaulting to Landscape for scene '{sceneName}'");
|
||||
}
|
||||
|
||||
switch (requirement)
|
||||
{
|
||||
case ScreenOrientationRequirement.Portrait:
|
||||
LogDebugMessage($"Forcing Portrait for scene '{sceneName}'");
|
||||
Logging.Debug($"Forcing Portrait for scene '{sceneName}'");
|
||||
StartCoroutine(ForcePortrait());
|
||||
break;
|
||||
case ScreenOrientationRequirement.Landscape:
|
||||
LogDebugMessage($"Forcing Landscape for scene '{sceneName}'");
|
||||
Logging.Debug($"Forcing Landscape for scene '{sceneName}'");
|
||||
StartCoroutine(ForceLandscape());
|
||||
break;
|
||||
case ScreenOrientationRequirement.NotApplicable:
|
||||
default:
|
||||
// Default to landscape when no specific requirement is found
|
||||
LogDebugMessage($"No specific orientation for scene '{sceneName}'. Defaulting to Landscape");
|
||||
Logging.Debug($"No specific orientation for scene '{sceneName}'. Defaulting to Landscape");
|
||||
StartCoroutine(ForceLandscape());
|
||||
break;
|
||||
}
|
||||
@@ -127,12 +125,12 @@ namespace Core
|
||||
if (!currentlyLandscape)
|
||||
{
|
||||
// Lock it to portrait and allow the device to orient itself
|
||||
LogDebugMessage($"Actually forcing Portrait from previous: {Screen.orientation}");
|
||||
Logging.Debug($"Actually forcing Portrait from previous: {Screen.orientation}");
|
||||
Screen.orientation = ScreenOrientation.LandscapeRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogDebugMessage($"Skipping Landscape enforcement, device already in: {Screen.orientation}");
|
||||
Logging.Debug($"Skipping Landscape enforcement, device already in: {Screen.orientation}");
|
||||
}
|
||||
|
||||
yield return null;
|
||||
@@ -160,12 +158,12 @@ namespace Core
|
||||
if (!currentlyPortrait)
|
||||
{
|
||||
// Lock it to portrait and allow the device to orient itself
|
||||
LogDebugMessage($"Actually forcing Portrait from previous: {Screen.orientation}");
|
||||
Logging.Debug($"Actually forcing Portrait from previous: {Screen.orientation}");
|
||||
Screen.orientation = ScreenOrientation.PortraitUpsideDown;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogDebugMessage($"Skipping Portrait enforcement, device already in: {Screen.orientation}");
|
||||
Logging.Debug($"Skipping Portrait enforcement, device already in: {Screen.orientation}");
|
||||
}
|
||||
|
||||
yield return null;
|
||||
@@ -181,13 +179,5 @@ namespace Core
|
||||
// Allow device to auto-rotate to correct portrait orientation
|
||||
Screen.orientation = ScreenOrientation.AutoRotation;
|
||||
}
|
||||
|
||||
private void LogDebugMessage(string message)
|
||||
{
|
||||
if (_logVerbosity <= LogVerbosity.Debug)
|
||||
{
|
||||
Logging.Debug($"[SceneOrientationEnforcer] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AppleHills.Core.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Core.Settings
|
||||
{
|
||||
@@ -21,7 +19,7 @@ namespace Core.Settings
|
||||
public static void Register<T>(T service) where T : class
|
||||
{
|
||||
Services[typeof(T)] = service;
|
||||
LogDebugMessage($"Service registered: {typeof(T).Name}");
|
||||
Logging.Debug($"Service registered: {typeof(T).Name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -36,7 +34,7 @@ namespace Core.Settings
|
||||
return service as T;
|
||||
}
|
||||
|
||||
Logging.Warning($"[ServiceLocator] Service of type {typeof(T).Name} not found!");
|
||||
Logging.Warning($"Service of type {typeof(T).Name} not found!");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -46,16 +44,7 @@ namespace Core.Settings
|
||||
public static void Clear()
|
||||
{
|
||||
Services.Clear();
|
||||
LogDebugMessage("All services cleared");
|
||||
}
|
||||
|
||||
private static void LogDebugMessage(string message)
|
||||
{
|
||||
if (DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().settingsLogVerbosity <=
|
||||
LogVerbosity.Debug)
|
||||
{
|
||||
Logging.Debug($"[ServiceLocator] {message}");
|
||||
}
|
||||
Logging.Debug("All services cleared");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using UnityEngine;
|
||||
using Pixelplacement;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Core;
|
||||
using Core.Lifecycle;
|
||||
using Core.SaveLoad;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
public class PicnicBehaviour : MonoBehaviour
|
||||
public class PicnicBehaviour : ManagedBehaviour
|
||||
{
|
||||
[Header("Random Call Settings")]
|
||||
public float getDistractedMin = 2f;
|
||||
@@ -17,7 +18,8 @@ public class PicnicBehaviour : MonoBehaviour
|
||||
private Animator animator;
|
||||
|
||||
[Header("The FakeChocolate to destroy!")]
|
||||
[SerializeField] private GameObject fakeChocolate; // Assign in Inspector
|
||||
[SerializeField] private GameObject fakeChocolate;
|
||||
[SerializeField] private GameObject realChocolate;
|
||||
|
||||
private AppleAudioSource _audioSource;
|
||||
public AudioResource distractedAudioClips;
|
||||
@@ -25,32 +27,44 @@ public class PicnicBehaviour : MonoBehaviour
|
||||
public AudioResource feederClips;
|
||||
public AudioResource moanerClips;
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
StartCoroutine(StateCycleRoutine());
|
||||
}
|
||||
// Save system configuration
|
||||
public override bool AutoRegisterForSave => true;
|
||||
|
||||
void Awake()
|
||||
// Runtime state tracking
|
||||
private bool _fakeChocolateDestroyed;
|
||||
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
stateMachine = GetComponent<AppleMachine>();
|
||||
animator = GetComponent<Animator>();
|
||||
_audioSource = GetComponent<AppleAudioSource>();
|
||||
}
|
||||
|
||||
internal override void OnSceneRestoreCompleted()
|
||||
{
|
||||
if (_fakeChocolateDestroyed)
|
||||
{
|
||||
DestroyChocolateObjects();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartCoroutine(StateCycleRoutine());
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator StateCycleRoutine()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Distracted state
|
||||
float distractedWait = Random.Range(getDistractedMin, getDistractedMax);
|
||||
float distractedWait = UnityEngine.Random.Range(getDistractedMin, getDistractedMax);
|
||||
stateMachine.ChangeState("Picnic PPL Distracted");
|
||||
animator.SetBool("theyDistracted", true);
|
||||
_audioSource.Stop();
|
||||
yield return new WaitForSeconds(distractedWait);
|
||||
|
||||
// Chilling state
|
||||
float chillingWait = Random.Range(getFlirtyMin, getFlirtyMax);
|
||||
float chillingWait = UnityEngine.Random.Range(getFlirtyMin, getFlirtyMax);
|
||||
stateMachine.ChangeState("Picnic PPL Chilling");
|
||||
animator.SetBool("theyDistracted", false);
|
||||
_audioSource.Stop();
|
||||
@@ -58,27 +72,33 @@ public class PicnicBehaviour : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
void StopAudio()
|
||||
{
|
||||
_audioSource.Stop();
|
||||
}
|
||||
|
||||
public void triedToStealChocolate()
|
||||
{
|
||||
_audioSource.Stop();
|
||||
animator.SetTrigger("theyAngry");
|
||||
//stateMachine.ChangeState("Picnic PPL Angry");
|
||||
Logging.Debug("Hey! Don't steal my chocolate!");
|
||||
_audioSource.audioSource.resource = angryAudioClips;
|
||||
_audioSource.Play(0);
|
||||
}
|
||||
|
||||
public void destroyFakeChocolate()
|
||||
{
|
||||
_fakeChocolateDestroyed = true;
|
||||
Destroy(fakeChocolate);
|
||||
Destroy(realChocolate);
|
||||
}
|
||||
|
||||
private void DestroyChocolateObjects()
|
||||
{
|
||||
if (fakeChocolate != null)
|
||||
{
|
||||
Destroy(fakeChocolate);
|
||||
fakeChocolate = null; // Optional: clear reference
|
||||
fakeChocolate = null;
|
||||
}
|
||||
|
||||
if (realChocolate != null)
|
||||
{
|
||||
realChocolate.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,5 +120,33 @@ public class PicnicBehaviour : MonoBehaviour
|
||||
_audioSource.Play(0);
|
||||
}
|
||||
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
var state = new PicnicBehaviourState { fakeChocolateDestroyed = _fakeChocolateDestroyed };
|
||||
return JsonUtility.ToJson(state);
|
||||
}
|
||||
|
||||
internal override void OnSceneRestoreRequested(string serializedData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(serializedData)) return;
|
||||
|
||||
try
|
||||
{
|
||||
var state = JsonUtility.FromJson<PicnicBehaviourState>(serializedData);
|
||||
if (state != null)
|
||||
{
|
||||
_fakeChocolateDestroyed = state.fakeChocolateDestroyed;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[PicnicBehaviour] Failed to restore state: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class PicnicBehaviourState
|
||||
{
|
||||
public bool fakeChocolateDestroyed;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class soundBird_CanFly : ManagedBehaviour
|
||||
|
||||
#region Save/Load Implementation
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
var saveData = new SoundBirdSaveData
|
||||
{
|
||||
@@ -32,7 +32,7 @@ public class soundBird_CanFly : ManagedBehaviour
|
||||
return JsonUtility.ToJson(saveData);
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreRequested(string serializedData)
|
||||
internal override void OnSceneRestoreRequested(string serializedData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(serializedData))
|
||||
{
|
||||
|
||||
@@ -40,25 +40,22 @@ namespace Data.CardSystem
|
||||
// Event callbacks using System.Action
|
||||
public event Action<List<CardData>> OnBoosterOpened;
|
||||
public event Action<CardData> OnCardCollected;
|
||||
public event Action<CardData> OnCardRarityUpgraded;
|
||||
public event Action<int> OnBoosterCountChanged;
|
||||
public event Action<CardData> OnPendingCardAdded;
|
||||
public event Action<CardData> OnCardPlacedInAlbum;
|
||||
|
||||
public override int ManagedAwakePriority => 60; // Data systems
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
|
||||
// Load card definitions from Addressables, then register with save system
|
||||
LoadCardDefinitionsFromAddressables();
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
Logging.Debug("[CardSystemManager] Initialized");
|
||||
}
|
||||
@@ -720,13 +717,13 @@ namespace Data.CardSystem
|
||||
|
||||
#region Save/Load Lifecycle Hooks
|
||||
|
||||
protected override string OnGlobalSaveRequested()
|
||||
internal override string OnGlobalSaveRequested()
|
||||
{
|
||||
var state = ExportCardCollectionState();
|
||||
return JsonUtility.ToJson(state);
|
||||
}
|
||||
|
||||
protected override void OnGlobalRestoreRequested(string serializedData)
|
||||
internal override void OnGlobalRestoreRequested(string serializedData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(serializedData))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Core;
|
||||
using Core.Lifecycle;
|
||||
using Interactions;
|
||||
@@ -37,7 +35,7 @@ namespace Dialogue
|
||||
|
||||
public override int ManagedAwakePriority => 150; // Dialogue systems
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Get required components
|
||||
appleAudioSource = GetComponent<AppleAudioSource>();
|
||||
@@ -186,8 +184,10 @@ namespace Dialogue
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
// Unregister from events
|
||||
if (PuzzleManager.Instance != null)
|
||||
PuzzleManager.Instance.OnStepCompleted -= OnAnyPuzzleStepCompleted;
|
||||
|
||||
@@ -52,11 +52,9 @@ namespace Input
|
||||
|
||||
public override int ManagedAwakePriority => 25; // Input infrastructure
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
|
||||
// Load verbosity settings early
|
||||
@@ -89,10 +87,10 @@ namespace Input
|
||||
SwitchInputOnSceneLoaded(SceneManager.GetActiveScene().name);
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Subscribe to scene load events from SceneManagerService
|
||||
// This must happen in ManagedAwake because SceneManagerService instance needs to be set first
|
||||
// This must happen in ManagedStart because SceneManagerService instance needs to be set first
|
||||
if (SceneManagerService.Instance != null)
|
||||
{
|
||||
SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted;
|
||||
@@ -104,7 +102,7 @@ namespace Input
|
||||
/// </summary>
|
||||
private void OnSceneLoadCompleted(string sceneName)
|
||||
{
|
||||
LogDebugMessage($"Scene loaded: {sceneName}, restoring input mode");
|
||||
Logging.Debug($"Scene loaded: {sceneName}, restoring input mode");
|
||||
SwitchInputOnSceneLoaded(sceneName);
|
||||
}
|
||||
|
||||
@@ -182,24 +180,24 @@ namespace Input
|
||||
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||
LogDebugMessage($"TapMove performed at {worldPos2D}");
|
||||
Logging.Debug($"TapMove performed at {worldPos2D}");
|
||||
|
||||
// First try to delegate to an override consumer if available
|
||||
if (TryDelegateToOverrideConsumer(screenPos, worldPos2D))
|
||||
{
|
||||
LogDebugMessage("Tap delegated to override consumer");
|
||||
Logging.Debug("Tap delegated to override consumer");
|
||||
return;
|
||||
}
|
||||
|
||||
// Then try to delegate to any ITouchInputConsumer (UI or world interactable)
|
||||
if (!TryDelegateToAnyInputConsumer(screenPos, worldPos2D))
|
||||
{
|
||||
LogDebugMessage("No input consumer found, forwarding tap to default consumer");
|
||||
Logging.Debug("No input consumer found, forwarding tap to default consumer");
|
||||
defaultConsumer?.OnTap(worldPos2D);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogDebugMessage("Tap delegated to input consumer");
|
||||
Logging.Debug("Tap delegated to input consumer");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,13 +210,13 @@ namespace Input
|
||||
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||
LogDebugMessage($"HoldMove started at {worldPos2D}");
|
||||
Logging.Debug($"HoldMove started at {worldPos2D}");
|
||||
|
||||
// First check for override consumers
|
||||
if (_overrideConsumers.Count > 0)
|
||||
{
|
||||
_activeHoldConsumer = _overrideConsumers[_overrideConsumers.Count - 1];
|
||||
LogDebugMessage($"Hold delegated to override consumer: {_activeHoldConsumer}");
|
||||
Logging.Debug($"Hold delegated to override consumer: {_activeHoldConsumer}");
|
||||
_activeHoldConsumer.OnHoldStart(worldPos2D);
|
||||
return;
|
||||
}
|
||||
@@ -238,7 +236,7 @@ namespace Input
|
||||
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||
LogDebugMessage($"HoldMove canceled at {worldPos2D}");
|
||||
Logging.Debug($"HoldMove canceled at {worldPos2D}");
|
||||
|
||||
// Notify the active hold consumer that the hold has ended
|
||||
_activeHoldConsumer?.OnHoldEnd(worldPos2D);
|
||||
@@ -302,7 +300,7 @@ namespace Input
|
||||
}
|
||||
if (consumer != null)
|
||||
{
|
||||
LogDebugMessage($"Delegating tap to UI consumer at {screenPos} (GameObject: {result.gameObject.name})");
|
||||
Logging.Debug($"Delegating tap to UI consumer at {screenPos} (GameObject: {result.gameObject.name})");
|
||||
consumer.OnTap(screenPos);
|
||||
return true;
|
||||
}
|
||||
@@ -331,7 +329,7 @@ namespace Input
|
||||
}
|
||||
if (consumer != null)
|
||||
{
|
||||
LogDebugMessage($"Delegating tap to consumer at {worldPos} (GameObject: {hitWithMask.gameObject.name})");
|
||||
Logging.Debug($"Delegating tap to consumer at {worldPos} (GameObject: {hitWithMask.gameObject.name})");
|
||||
consumer.OnTap(worldPos);
|
||||
return true;
|
||||
}
|
||||
@@ -345,15 +343,15 @@ namespace Input
|
||||
var consumer = hit.GetComponent<ITouchInputConsumer>();
|
||||
if (consumer != null)
|
||||
{
|
||||
LogDebugMessage($"Delegating tap to consumer at {worldPos} (GameObject: {hit.gameObject.name})");
|
||||
Logging.Debug($"Delegating tap to consumer at {worldPos} (GameObject: {hit.gameObject.name})");
|
||||
consumer.OnTap(worldPos);
|
||||
return true;
|
||||
}
|
||||
LogDebugMessage($"Collider2D hit at {worldPos} (GameObject: {hit.gameObject.name}), but no ITouchInputConsumer found.");
|
||||
Logging.Debug($"Collider2D hit at {worldPos} (GameObject: {hit.gameObject.name}), but no ITouchInputConsumer found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogDebugMessage($"No Collider2D found at {worldPos} for interactable delegation.");
|
||||
Logging.Debug($"No Collider2D found at {worldPos} for interactable delegation.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -368,7 +366,7 @@ namespace Input
|
||||
return;
|
||||
|
||||
_overrideConsumers.Add(consumer);
|
||||
LogDebugMessage($"Override consumer registered: {consumer}");
|
||||
Logging.Debug($"Override consumer registered: {consumer}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -386,7 +384,7 @@ namespace Input
|
||||
}
|
||||
|
||||
_overrideConsumers.Remove(consumer);
|
||||
LogDebugMessage($"Override consumer unregistered: {consumer}");
|
||||
Logging.Debug($"Override consumer unregistered: {consumer}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -396,7 +394,7 @@ namespace Input
|
||||
{
|
||||
_activeHoldConsumer = null;
|
||||
_overrideConsumers.Clear();
|
||||
LogDebugMessage("All override consumers cleared.");
|
||||
Logging.Debug("All override consumers cleared.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -409,17 +407,9 @@ namespace Input
|
||||
|
||||
// Get the topmost override consumer (last registered)
|
||||
var consumer = _overrideConsumers[_overrideConsumers.Count - 1];
|
||||
LogDebugMessage($"Delegating tap to override consumer at {worldPos} (GameObject: {consumer})");
|
||||
Logging.Debug($"Delegating tap to override consumer at {worldPos} (GameObject: {consumer})");
|
||||
consumer.OnTap(worldPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void LogDebugMessage(string message)
|
||||
{
|
||||
if (_logVerbosity <= LogVerbosity.Debug)
|
||||
{
|
||||
Logging.Debug($"[InputManager] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using Pathfinding;
|
||||
using AppleHills.Core.Settings;
|
||||
using Core;
|
||||
using Core.Lifecycle;
|
||||
using Core.SaveLoad;
|
||||
|
||||
namespace Input
|
||||
{
|
||||
@@ -73,7 +72,7 @@ namespace Input
|
||||
public override string SaveId => $"{gameObject.scene.name}/PlayerController";
|
||||
public override int ManagedAwakePriority => 100; // Player controller
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
aiPath = GetComponent<AIPath>();
|
||||
artTransform = transform.Find("CharacterArt");
|
||||
@@ -103,7 +102,7 @@ namespace Input
|
||||
public void OnTap(Vector2 worldPosition)
|
||||
{
|
||||
InterruptMoveTo();
|
||||
LogDebugMessage($"OnTap at {worldPosition}");
|
||||
Logging.Debug($"OnTap at {worldPosition}");
|
||||
if (aiPath != null)
|
||||
{
|
||||
aiPath.enabled = true;
|
||||
@@ -122,7 +121,7 @@ namespace Input
|
||||
public void OnHoldStart(Vector2 worldPosition)
|
||||
{
|
||||
InterruptMoveTo();
|
||||
LogDebugMessage($"OnHoldStart at {worldPosition}");
|
||||
Logging.Debug($"OnHoldStart at {worldPosition}");
|
||||
lastHoldPosition = worldPosition;
|
||||
isHolding = true;
|
||||
if (_settings.DefaultHoldMovementMode == HoldMovementMode.Pathfinding &&
|
||||
@@ -159,7 +158,7 @@ namespace Input
|
||||
/// </summary>
|
||||
public void OnHoldEnd(Vector2 worldPosition)
|
||||
{
|
||||
LogDebugMessage($"OnHoldEnd at {worldPosition}");
|
||||
Logging.Debug($"OnHoldEnd at {worldPosition}");
|
||||
isHolding = false;
|
||||
directMoveVelocity = Vector3.zero;
|
||||
if (aiPath != null && _settings.DefaultHoldMovementMode ==
|
||||
@@ -335,13 +334,13 @@ namespace Input
|
||||
{
|
||||
_isMoving = true;
|
||||
OnMovementStarted?.Invoke();
|
||||
LogDebugMessage("Movement started");
|
||||
Logging.Debug("Movement started");
|
||||
}
|
||||
else if (!isCurrentlyMoving && _isMoving)
|
||||
{
|
||||
_isMoving = false;
|
||||
OnMovementStopped?.Invoke();
|
||||
LogDebugMessage("Movement stopped");
|
||||
Logging.Debug("Movement stopped");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,17 +424,9 @@ namespace Input
|
||||
}
|
||||
}
|
||||
|
||||
private void LogDebugMessage(string message)
|
||||
{
|
||||
if (_logVerbosity <= LogVerbosity.Debug)
|
||||
{
|
||||
Logging.Debug($"[PlayerTouchController] {message}");
|
||||
}
|
||||
}
|
||||
|
||||
#region Save/Load Lifecycle Hooks
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
var saveData = new PlayerSaveData
|
||||
{
|
||||
@@ -445,7 +436,7 @@ namespace Input
|
||||
return JsonUtility.ToJson(saveData);
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreRequested(string serializedData)
|
||||
internal override void OnSceneRestoreRequested(string serializedData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(serializedData))
|
||||
{
|
||||
|
||||
@@ -68,8 +68,6 @@ namespace Interactions
|
||||
public event Action<PickupItemData, PickupItemData> OnIncorrectItemSlotted;
|
||||
|
||||
public UnityEvent onForbiddenItemSlotted;
|
||||
// Native C# event alternative for code-only subscribers
|
||||
public event Action<PickupItemData, PickupItemData> OnForbiddenItemSlotted;
|
||||
|
||||
public GameObject GetSlottedObject()
|
||||
{
|
||||
@@ -85,9 +83,9 @@ namespace Interactions
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // SaveableInteractable registration
|
||||
base.OnManagedAwake(); // SaveableInteractable registration
|
||||
|
||||
// Setup visuals
|
||||
if (iconRenderer == null)
|
||||
|
||||
@@ -32,9 +32,9 @@ namespace Interactions
|
||||
public event Action<PickupItemData> OnItemPickedUp;
|
||||
public event Action<PickupItemData, PickupItemData, PickupItemData> OnItemsCombined;
|
||||
|
||||
protected override void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // Register with save system
|
||||
base.OnManagedAwake(); // Register with save system
|
||||
|
||||
if (iconRenderer == null)
|
||||
iconRenderer = GetComponent<SpriteRenderer>();
|
||||
@@ -44,8 +44,9 @@ namespace Interactions
|
||||
|
||||
// Always register with ItemManager, even if picked up
|
||||
// This allows the save/load system to find held items when restoring state
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
base.OnManagedStart();
|
||||
ItemManager.Instance?.RegisterPickup(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Interactions
|
||||
|
||||
#region Save/Load Lifecycle Hooks
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
object stateData = GetSerializableState();
|
||||
if (stateData == null)
|
||||
@@ -35,7 +35,7 @@ namespace Interactions
|
||||
return JsonUtility.ToJson(stateData);
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreRequested(string serializedData)
|
||||
internal override void OnSceneRestoreRequested(string serializedData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(serializedData))
|
||||
{
|
||||
|
||||
@@ -21,9 +21,9 @@ namespace Levels
|
||||
/// <summary>
|
||||
/// Unity Awake callback. Sets up icon, interactable, and event handlers.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake();
|
||||
base.OnManagedAwake();
|
||||
|
||||
Logging.Debug($"[LevelSwitch] Awake called for {gameObject.name} in scene {gameObject.scene.name}");
|
||||
|
||||
@@ -36,12 +36,12 @@ namespace Levels
|
||||
ApplySwitchData();
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
Logging.Debug($"[LevelSwitch] OnManagedAwake called for {gameObject.name}");
|
||||
Logging.Debug($"[LevelSwitch] OnManagedStart called for {gameObject.name}");
|
||||
}
|
||||
|
||||
protected override void OnSceneReady()
|
||||
internal override void OnSceneReady()
|
||||
{
|
||||
Logging.Debug($"[LevelSwitch] OnSceneReady called for {gameObject.name}");
|
||||
}
|
||||
|
||||
@@ -35,21 +35,16 @@ namespace Levels
|
||||
[SerializeField] private bool startUnlocked = false;
|
||||
|
||||
private SpriteRenderer iconRenderer;
|
||||
|
||||
// Settings reference
|
||||
private IInteractionSettings interactionSettings;
|
||||
|
||||
private bool switchActive = true;
|
||||
private bool isUnlocked;
|
||||
|
||||
/// <summary>
|
||||
/// Unity Awake callback. Sets up icon, interactable, and event handlers.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake();
|
||||
base.OnManagedAwake();
|
||||
|
||||
switchActive = true;
|
||||
if (iconRenderer == null)
|
||||
iconRenderer = GetComponent<SpriteRenderer>();
|
||||
|
||||
@@ -64,10 +59,9 @@ namespace Levels
|
||||
ApplySwitchData();
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
base.OnManagedStart();
|
||||
// If startUnlocked is true, always start active
|
||||
if (startUnlocked)
|
||||
{
|
||||
@@ -145,14 +139,6 @@ namespace Levels
|
||||
return base.CanBeClicked() && isUnlocked;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup: Prevent re-entry while interaction is in progress.
|
||||
/// </summary>
|
||||
protected override void OnInteractionStarted()
|
||||
{
|
||||
switchActive = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main interaction logic: Spawn menu and switch input mode.
|
||||
/// </summary>
|
||||
@@ -203,7 +189,7 @@ namespace Levels
|
||||
|
||||
private void OnMenuCancel()
|
||||
{
|
||||
switchActive = true; // Allow interaction again if cancelled
|
||||
|
||||
InputManager.Instance.SetInputMode(InputMode.GameAndUI);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ using UI.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Playables;
|
||||
using Svg;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
@@ -106,10 +107,8 @@ namespace Minigames.DivingForPictures
|
||||
public override int ManagedAwakePriority => 190;
|
||||
public override bool AutoRegisterPausable => true; // Automatic GameManager registration
|
||||
|
||||
protected override void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = this;
|
||||
@@ -120,7 +119,7 @@ namespace Minigames.DivingForPictures
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
_settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
|
||||
_currentSpawnProbability = _settings?.BaseSpawnProbability ?? 0.2f;
|
||||
@@ -131,7 +130,7 @@ namespace Minigames.DivingForPictures
|
||||
Logging.Debug("[DivingGameManager] Initialized");
|
||||
}
|
||||
|
||||
protected override void OnSceneReady()
|
||||
internal override void OnSceneReady()
|
||||
{
|
||||
InitializeGame();
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ public class FollowerController : ManagedBehaviour
|
||||
|
||||
public override int ManagedAwakePriority => 110; // Follower after player
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
_aiPath = GetComponent<AIPath>();
|
||||
// Find art prefab and animator
|
||||
@@ -129,7 +129,7 @@ public class FollowerController : ManagedBehaviour
|
||||
_interactionSettings = GameManager.GetSettingsObject<IInteractionSettings>();
|
||||
}
|
||||
|
||||
protected override void OnSceneReady()
|
||||
internal override void OnSceneReady()
|
||||
{
|
||||
// Find player reference when scene is ready (called for every scene load)
|
||||
FindPlayerReference();
|
||||
@@ -727,7 +727,7 @@ public class FollowerController : ManagedBehaviour
|
||||
|
||||
#region Save/Load Lifecycle Hooks
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
var saveData = new FollowerSaveData
|
||||
{
|
||||
@@ -754,7 +754,7 @@ public class FollowerController : ManagedBehaviour
|
||||
return JsonUtility.ToJson(saveData);
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreRequested(string serializedData)
|
||||
internal override void OnSceneRestoreRequested(string serializedData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(serializedData))
|
||||
{
|
||||
@@ -914,7 +914,7 @@ public class FollowerController : ManagedBehaviour
|
||||
/// </summary>
|
||||
public static FollowerController FindInstance()
|
||||
{
|
||||
return FindObjectOfType<FollowerController>();
|
||||
return FindFirstObjectByType<FollowerController>();
|
||||
}
|
||||
|
||||
#endregion Save/Load Lifecycle Hooks
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace PuzzleS
|
||||
// Save system configuration
|
||||
public override bool AutoRegisterForSave => true;
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Initialize after all managers are ready
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace PuzzleS
|
||||
|
||||
#region Save/Load Lifecycle Hooks
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
// Save scene-specific progress
|
||||
var state = new BirdGameState
|
||||
@@ -38,7 +38,7 @@ namespace PuzzleS
|
||||
return JsonUtility.ToJson(state);
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreRequested(string serializedData)
|
||||
internal override void OnSceneRestoreRequested(string serializedData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(serializedData))
|
||||
{
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace PuzzleS
|
||||
// Enum for tracking proximity state (simplified to just Close and Far)
|
||||
public enum ProximityState { Close, Far }
|
||||
|
||||
protected override void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
_interactable = GetComponent<InteractableBase>();
|
||||
|
||||
@@ -56,14 +56,10 @@ namespace PuzzleS
|
||||
Logging.Warning($"[Puzzles] Indicator prefab for {stepData?.stepId} does not implement IPuzzlePrompt");
|
||||
}
|
||||
}
|
||||
|
||||
base.Awake();
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Register with PuzzleManager - safe to access .Instance here
|
||||
if (stepData != null && PuzzleManager.Instance != null)
|
||||
{
|
||||
|
||||
@@ -96,15 +96,13 @@ namespace PuzzleS
|
||||
|
||||
public override int ManagedAwakePriority => 80; // Puzzle systems
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Initialize settings reference
|
||||
_interactionSettings = GameManager.GetSettingsObject<IInteractionSettings>();
|
||||
@@ -591,7 +589,7 @@ namespace PuzzleS
|
||||
|
||||
#region Save/Load Lifecycle Hooks
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
if (_currentLevelData == null)
|
||||
{
|
||||
@@ -611,7 +609,7 @@ namespace PuzzleS
|
||||
return json;
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreRequested(string data)
|
||||
internal override void OnSceneRestoreRequested(string data)
|
||||
{
|
||||
Logging.Debug("[XAXA] PuzzleManager loading with data: " + data);
|
||||
|
||||
|
||||
@@ -20,15 +20,13 @@ public class AppleAudioSource : ManagedBehaviour
|
||||
public int sourcePriority;
|
||||
|
||||
|
||||
protected override void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake();
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
}
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
protected override void OnManagedAwake()
|
||||
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
AudioManager.Instance.RegisterNewAudioSource(this);
|
||||
_audioMixer = AudioManager.Instance.audioMixer;
|
||||
InitializeAudioSource();
|
||||
|
||||
@@ -44,15 +44,13 @@ public class AudioManager : ManagedBehaviour, IPausable
|
||||
public override int ManagedAwakePriority => 30; // Audio infrastructure
|
||||
public override bool AutoRegisterPausable => true; // Auto-register as IPausable
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Initialize lists if they were not set in inspector
|
||||
criticalVOSources = criticalVOSources ?? new List<AppleAudioSource>();
|
||||
@@ -72,7 +70,7 @@ public class AudioManager : ManagedBehaviour, IPausable
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning("[AudioManager] QuickAccess.Instance is null during OnManagedAwake. Some audio references may remain unset.");
|
||||
Logging.Warning("[AudioManager] QuickAccess.Instance is null during OnManagedStart. Some audio references may remain unset.");
|
||||
}
|
||||
|
||||
// Diagnostic
|
||||
|
||||
@@ -23,7 +23,7 @@ public class BushAudioController : ManagedBehaviour
|
||||
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
_eventSource = VOPlayer.audioSource.RequestEventHandlers();
|
||||
_eventSource.AudioStopped += PlayBirdCounter;
|
||||
|
||||
@@ -26,7 +26,7 @@ public class LevelAudioObject : ManagedBehaviour
|
||||
|
||||
public override bool AutoRegisterForSave => isOneTime; // Only save if one-time audio
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
internal override string OnSceneSaveRequested()
|
||||
{
|
||||
if (!isOneTime)
|
||||
return null; // No need to save if not one-time
|
||||
@@ -39,7 +39,7 @@ public class LevelAudioObject : ManagedBehaviour
|
||||
return JsonUtility.ToJson(saveData);
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreRequested(string serializedData)
|
||||
internal override void OnSceneRestoreRequested(string serializedData)
|
||||
{
|
||||
if (!isOneTime || string.IsNullOrEmpty(serializedData))
|
||||
return;
|
||||
@@ -55,7 +55,7 @@ public class LevelAudioObject : ManagedBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreCompleted()
|
||||
internal override void OnSceneRestoreCompleted()
|
||||
{
|
||||
if (isOneTime && !_hasPlayed)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ public class PulverAudioController : ManagedBehaviour
|
||||
private FollowerController followerController;
|
||||
public ItemManager itemManager;
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
followerController = GetComponent<FollowerController>();
|
||||
followerController.PulverIsCombining.AddListener(PulverIsCombining);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using SkiaSharp.Unity;
|
||||
using Input;
|
||||
using AppleHills.Core;
|
||||
using UI.Core;
|
||||
@@ -10,6 +11,8 @@ using UI;
|
||||
|
||||
public class AppSwitcher : UIPage
|
||||
{
|
||||
public GameObject rainbowIn;
|
||||
public GameObject rainbowOut;
|
||||
public GameObject gameLayoutContainer;
|
||||
public GameObject exitButton;
|
||||
public RectMask2D rectMask;
|
||||
@@ -17,21 +20,26 @@ public class AppSwitcher : UIPage
|
||||
[Header("Slide Animation Settings")]
|
||||
public float slideDuration = 0.5f;
|
||||
|
||||
private SkottiePlayerV2 rainbowInPlayer;
|
||||
private SkottiePlayerV2 rainbowOutPlayer;
|
||||
private TweenBase slideInTween;
|
||||
private TweenBase slideOutTween;
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
PageName = "AppSwitcher";
|
||||
rainbowInPlayer = rainbowIn.GetComponent<SkottiePlayerV2>();
|
||||
rainbowOutPlayer = rainbowOut.GetComponent<SkottiePlayerV2>();
|
||||
|
||||
if (rectMask == null)
|
||||
{
|
||||
rectMask = GetComponent<RectMask2D>();
|
||||
}
|
||||
|
||||
// Initially hide
|
||||
// Initially hide both
|
||||
rainbowIn.SetActive(true);
|
||||
exitButton.SetActive(false);
|
||||
}
|
||||
|
||||
@@ -41,19 +49,27 @@ public class AppSwitcher : UIPage
|
||||
|
||||
gameLayoutContainer.SetActive(true);
|
||||
|
||||
// Hide rainbow out, show rainbow in
|
||||
rainbowOut.SetActive(false);
|
||||
|
||||
// Play animation on rainbow in without resetting state
|
||||
rainbowInPlayer.PlayAnimation(true);
|
||||
|
||||
// Slide in animation - tween padding.left from 1700 to 0
|
||||
slideInTween = TweenPaddingLeft(1700f, 0f, Tween.EaseOut, () =>
|
||||
{
|
||||
onComplete?.Invoke();
|
||||
rainbowOut.SetActive(true);
|
||||
exitButton.SetActive(true);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void DoTransitionOut(Action onComplete)
|
||||
{
|
||||
rainbowIn.SetActive(false);
|
||||
|
||||
// Play animation on rainbow out with resetting state
|
||||
rainbowOutPlayer.PlayAnimation(true);
|
||||
|
||||
// Hide the exit button
|
||||
exitButton.SetActive(false);
|
||||
@@ -61,6 +77,7 @@ public class AppSwitcher : UIPage
|
||||
// Slide out animation - tween padding.left from 0 to 1700
|
||||
slideOutTween = TweenPaddingLeft(0f, 1700f, Tween.EaseIn, () => {
|
||||
gameLayoutContainer.SetActive(false);
|
||||
rainbowIn.SetActive(true);
|
||||
onComplete?.Invoke();
|
||||
InputManager.Instance.SetInputMode(InputMode.GameAndUI);
|
||||
});
|
||||
|
||||
@@ -44,10 +44,8 @@ namespace UI.CardSystem
|
||||
private List<AlbumCardPlacementDraggable> _activeCards = new List<AlbumCardPlacementDraggable>();
|
||||
private const int MAX_VISIBLE_CARDS = 3;
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Discover zone tabs from container
|
||||
DiscoverZoneTabs();
|
||||
|
||||
|
||||
@@ -40,10 +40,8 @@ namespace UI.CardSystem
|
||||
|
||||
private TweenBase _activeTween;
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Store original scale for pulse animation
|
||||
if (dotBackground != null)
|
||||
{
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace UI.CardSystem
|
||||
[SerializeField] private float cardSpacing = 150f;
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private float cardRevealDelay = 0.5f;
|
||||
[SerializeField] private float boosterDisappearDuration = 0.5f;
|
||||
[SerializeField] private CinemachineImpulseSource impulseSource;
|
||||
[SerializeField] private ParticleSystem openingParticleSystem;
|
||||
@@ -77,8 +76,10 @@ namespace UI.CardSystem
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
// Unsubscribe from dismiss button
|
||||
if (_dismissButton != null)
|
||||
{
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace UI.CardSystem
|
||||
albumCard.SetParentSlot(this);
|
||||
|
||||
// Register with AlbumViewPage for enlarge/shrink handling
|
||||
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
|
||||
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
|
||||
if (albumPage != null)
|
||||
{
|
||||
albumPage.RegisterAlbumCard(albumCard);
|
||||
@@ -208,7 +208,7 @@ namespace UI.CardSystem
|
||||
}
|
||||
|
||||
// Register with AlbumViewPage for enlarge/shrink handling
|
||||
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
|
||||
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
|
||||
if (albumPage != null)
|
||||
{
|
||||
albumPage.RegisterAlbumCard(albumCard);
|
||||
@@ -284,7 +284,7 @@ namespace UI.CardSystem
|
||||
previewCardDisplay.transform.localScale = _previewOriginalScale;
|
||||
|
||||
// Get AlbumViewPage to show backdrop and reparent
|
||||
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
|
||||
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
|
||||
if (albumPage != null)
|
||||
{
|
||||
albumPage.ShowSlotPreview(this, previewCardDisplay.transform);
|
||||
@@ -311,7 +311,7 @@ namespace UI.CardSystem
|
||||
previewCardDisplay.SetPreviewMode(false, null);
|
||||
|
||||
// Get AlbumViewPage to hide backdrop
|
||||
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
|
||||
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
|
||||
if (albumPage != null)
|
||||
{
|
||||
albumPage.HideSlotPreview(this, previewCardDisplay.transform, () =>
|
||||
|
||||
@@ -41,7 +41,6 @@ namespace UI.CardSystem
|
||||
// State
|
||||
private bool _isFlipped = false;
|
||||
private bool _isFlipping = false;
|
||||
private bool _isHovering = false;
|
||||
private TweenBase _idleHoverTween;
|
||||
private CardData _cardData;
|
||||
private Vector2 _originalPosition; // Track original spawn position
|
||||
@@ -242,8 +241,6 @@ namespace UI.CardSystem
|
||||
if (_isFlipped || _isFlipping)
|
||||
return;
|
||||
|
||||
_isHovering = true;
|
||||
|
||||
// Scale up slightly on hover
|
||||
Tween.LocalScale(transform, Vector3.one * hoverScaleMultiplier, 0.2f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
@@ -253,8 +250,6 @@ namespace UI.CardSystem
|
||||
if (_isFlipped || _isFlipping)
|
||||
return;
|
||||
|
||||
_isHovering = false;
|
||||
|
||||
// Scale back to normal
|
||||
Tween.LocalScale(transform, Vector3.one, 0.2f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace UI.CardSystem
|
||||
[Header("Animation Settings")]
|
||||
[SerializeField] private float hoverAmount = 20f;
|
||||
[SerializeField] private float hoverDuration = 1.5f;
|
||||
[SerializeField] private float glowPulseMin = 0.9f;
|
||||
[SerializeField] private float glowPulseMax = 1.1f;
|
||||
[SerializeField] private float glowPulseDuration = 1.2f;
|
||||
|
||||
|
||||
@@ -39,15 +39,13 @@ namespace UI.Core
|
||||
|
||||
public override int ManagedAwakePriority => 50; // UI infrastructure
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
Logging.Debug("[UIPageController] Initialized");
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ namespace UI.DragAndDrop.Core
|
||||
|
||||
protected virtual void FindAndSnapToSlot()
|
||||
{
|
||||
SlotContainer[] containers = FindObjectsOfType<SlotContainer>();
|
||||
SlotContainer[] containers = FindObjectsByType<SlotContainer>(FindObjectsSortMode.None);
|
||||
DraggableSlot closestSlot = null;
|
||||
float closestDistance = float.MaxValue;
|
||||
|
||||
|
||||
@@ -56,11 +56,9 @@ namespace UI
|
||||
// ManagedBehaviour configuration
|
||||
public override int ManagedAwakePriority => 45; // UI infrastructure, before UIPageController
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
|
||||
// Set up container reference early
|
||||
@@ -74,7 +72,7 @@ namespace UI
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
Logging.Debug("[LoadingScreenController] Initialized");
|
||||
}
|
||||
|
||||
@@ -31,11 +31,9 @@ namespace UI
|
||||
// After UIPageController (50)
|
||||
public override int ManagedAwakePriority => 55;
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
||||
|
||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
|
||||
// Ensure we have a CanvasGroup for transitions
|
||||
@@ -51,9 +49,9 @@ namespace UI
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Subscribe to scene-dependent events - must be in OnManagedAwake, not OnSceneReady
|
||||
// Subscribe to scene-dependent events - must be in OnManagedStart, not OnSceneReady
|
||||
// because PauseMenu is in DontDestroyOnLoad and OnSceneReady only fires once
|
||||
if (SceneManagerService.Instance != null)
|
||||
{
|
||||
@@ -73,7 +71,7 @@ namespace UI
|
||||
Logging.Debug("[PauseMenu] Subscribed to SceneManagerService events");
|
||||
}
|
||||
|
||||
protected override void OnSceneReady()
|
||||
internal override void OnSceneReady()
|
||||
{
|
||||
// This only fires once for DontDestroyOnLoad objects, so we handle scene loads in OnManagedAwake
|
||||
}
|
||||
|
||||
@@ -115,14 +115,14 @@ namespace UI
|
||||
private UIPageController _uiPageController;
|
||||
private AppSwitcher _appSwitcherComponent;
|
||||
|
||||
private new void Awake()
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.Awake();
|
||||
if (Instance != null)
|
||||
{
|
||||
Destroy(this);
|
||||
return;
|
||||
}
|
||||
// Set instance immediately (early initialization)
|
||||
_instance = this;
|
||||
|
||||
// Get UIPageController on same GameObject
|
||||
@@ -135,7 +135,7 @@ namespace UI
|
||||
InitializeReferences();
|
||||
}
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Subscribe to UIPageController page changes for auto HUD management
|
||||
if (_uiPageController != null)
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace UI.Tutorial
|
||||
|
||||
public override int ManagedAwakePriority => 200; // Tutorial runs late, after other systems
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
// Ensure prompt is hidden initially (even before tutorial initialization)
|
||||
if (tapPrompt != null)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d60f8ff6bf46b494b80e210228c43e61
|
||||
guid: 53ec386bba82c1748886a5beb8468ecf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
48
Assets/Settings/Build Profiles/iOS.asset
Normal file
48
Assets/Settings/Build Profiles/iOS.asset
Normal file
@@ -0,0 +1,48 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 15003, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_Name: iOS
|
||||
m_EditorClassIdentifier: UnityEditor.dll::UnityEditor.Build.Profile.BuildProfile
|
||||
m_AssetVersion: 1
|
||||
m_BuildTarget: 9
|
||||
m_Subtarget: 0
|
||||
m_PlatformId: ad48d16a66894befa4d8181998c3cb09
|
||||
m_PlatformBuildProfile:
|
||||
rid: 3475452038477774988
|
||||
m_OverrideGlobalSceneList: 0
|
||||
m_Scenes: []
|
||||
m_ScriptingDefines: []
|
||||
m_PlayerSettingsYaml:
|
||||
m_Settings: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 3475452038477774988
|
||||
type: {class: iOSPlatformSettings, ns: UnityEditor.iOS, asm: UnityEditor.iOS.Extensions}
|
||||
data:
|
||||
m_Development: 0
|
||||
m_ConnectProfiler: 0
|
||||
m_BuildWithDeepProfilingSupport: 0
|
||||
m_AllowDebugging: 0
|
||||
m_WaitForManagedDebugger: 0
|
||||
m_ManagedDebuggerFixedPort: 0
|
||||
m_ExplicitNullChecks: 0
|
||||
m_ExplicitDivideByZeroChecks: 0
|
||||
m_ExplicitArrayBoundsChecks: 0
|
||||
m_CompressionType: -1
|
||||
m_InstallInBuildFolder: 0
|
||||
m_InsightsSettingsContainer:
|
||||
m_BuildProfileEngineDiagnosticsState: 2
|
||||
m_iOSXcodeBuildConfig: 1
|
||||
m_SymlinkSources: 0
|
||||
m_PreferredXcode:
|
||||
m_SymlinkTrampoline: 0
|
||||
8
Assets/Settings/Build Profiles/iOS.asset.meta
Normal file
8
Assets/Settings/Build Profiles/iOS.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 110a4eabb37dbaa428e55c751696cd1e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"com.ammariqais.skiaforunity": "https://github.com/ammariqais/SkiaForUnity.git?path=SkiaUnity/Assets/SkiaSharp",
|
||||
"com.coplaydev.unity-mcp": "https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity",
|
||||
"com.moolt.packages.net": "git+https://github.com/Moolt/UnityAudioSourceEvents?path=Packages/AudioSourceEvents",
|
||||
"com.unity.2d.sprite": "1.0.0",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"com.ammariqais.skiaforunity": {
|
||||
"version": "https://github.com/ammariqais/SkiaForUnity.git?path=SkiaUnity/Assets/SkiaSharp",
|
||||
"depth": 0,
|
||||
"source": "git",
|
||||
"dependencies": {
|
||||
"com.unity.nuget.newtonsoft-json": "3.2.0"
|
||||
},
|
||||
"hash": "11e82b71012bf8b4f68172080d6e4969d42120ef"
|
||||
},
|
||||
"com.coplaydev.unity-mcp": {
|
||||
"version": "https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity",
|
||||
"depth": 0,
|
||||
|
||||
@@ -12,8 +12,8 @@ PlayerSettings:
|
||||
targetDevice: 2
|
||||
useOnDemandResources: 0
|
||||
accelerometerFrequency: 60
|
||||
companyName: DR
|
||||
productName: "DR \xC6blerup"
|
||||
companyName: DefaultCompany
|
||||
productName: AppleHills
|
||||
defaultCursor: {fileID: 0}
|
||||
cursorHotspot: {x: 0, y: 0}
|
||||
m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}
|
||||
@@ -142,7 +142,7 @@ PlayerSettings:
|
||||
loadStoreDebugModeEnabled: 0
|
||||
visionOSBundleVersion: 1.0
|
||||
tvOSBundleVersion: 1.0
|
||||
bundleVersion: 1.1
|
||||
bundleVersion: 1.0.2
|
||||
preloadedAssets: []
|
||||
metroInputSource: 0
|
||||
wsaTransparentSwapchain: 0
|
||||
@@ -168,7 +168,7 @@ PlayerSettings:
|
||||
Android: com.DefaultCompany.com.unity.template.mobile2D
|
||||
Lumin: com.DefaultCompany.com.unity.template.mobile2D
|
||||
Standalone: com.DefaultCompany.com.unity.template.mobile2D
|
||||
iPhone: dk.dr.ramasjang.aeblerup
|
||||
iPhone: com.DefaultCompany.com.unity.template.mobile2D
|
||||
tvOS: com.DefaultCompany.com.unity.template.mobile2D
|
||||
buildNumber:
|
||||
Standalone: 0
|
||||
@@ -403,31 +403,25 @@ PlayerSettings:
|
||||
m_SubKind:
|
||||
- m_BuildTarget: iPhone
|
||||
m_Icons:
|
||||
- m_Textures:
|
||||
- {fileID: 2800000, guid: a8ae11f948f0cfa4791e954c315376ee, type: 3}
|
||||
m_Width: 1024
|
||||
m_Height: 1024
|
||||
m_Kind: 4
|
||||
m_SubKind: App Store
|
||||
- m_Textures: []
|
||||
m_Width: 60
|
||||
m_Height: 60
|
||||
m_Kind: 2
|
||||
m_Width: 120
|
||||
m_Height: 120
|
||||
m_Kind: 3
|
||||
m_SubKind: iPhone
|
||||
- m_Textures: []
|
||||
m_Width: 40
|
||||
m_Height: 40
|
||||
m_Kind: 2
|
||||
m_Width: 80
|
||||
m_Height: 80
|
||||
m_Kind: 3
|
||||
m_SubKind: iPhone
|
||||
- m_Textures: []
|
||||
m_Width: 40
|
||||
m_Height: 40
|
||||
m_Kind: 2
|
||||
m_Width: 80
|
||||
m_Height: 80
|
||||
m_Kind: 3
|
||||
m_SubKind: iPad
|
||||
- m_Textures: []
|
||||
m_Width: 20
|
||||
m_Height: 20
|
||||
m_Kind: 2
|
||||
m_Width: 40
|
||||
m_Height: 40
|
||||
m_Kind: 3
|
||||
m_SubKind: iPad
|
||||
- m_Textures: []
|
||||
m_Width: 87
|
||||
@@ -455,25 +449,30 @@ PlayerSettings:
|
||||
m_Kind: 1
|
||||
m_SubKind: iPad
|
||||
- m_Textures: []
|
||||
m_Width: 120
|
||||
m_Height: 120
|
||||
m_Kind: 3
|
||||
m_Width: 60
|
||||
m_Height: 60
|
||||
m_Kind: 2
|
||||
m_SubKind: iPhone
|
||||
- m_Textures: []
|
||||
m_Width: 80
|
||||
m_Height: 80
|
||||
m_Kind: 3
|
||||
m_SubKind: iPhone
|
||||
- m_Textures: []
|
||||
m_Width: 80
|
||||
m_Height: 80
|
||||
m_Kind: 3
|
||||
m_SubKind: iPad
|
||||
- m_Textures: []
|
||||
m_Width: 40
|
||||
m_Height: 40
|
||||
m_Kind: 3
|
||||
m_Kind: 2
|
||||
m_SubKind: iPhone
|
||||
- m_Textures: []
|
||||
m_Width: 40
|
||||
m_Height: 40
|
||||
m_Kind: 2
|
||||
m_SubKind: iPad
|
||||
- m_Textures: []
|
||||
m_Width: 20
|
||||
m_Height: 20
|
||||
m_Kind: 2
|
||||
m_SubKind: iPad
|
||||
- m_Textures: []
|
||||
m_Width: 1024
|
||||
m_Height: 1024
|
||||
m_Kind: 4
|
||||
m_SubKind: App Store
|
||||
- m_Textures:
|
||||
- {fileID: 2800000, guid: a8ae11f948f0cfa4791e954c315376ee, type: 3}
|
||||
m_Width: 180
|
||||
@@ -508,9 +507,6 @@ PlayerSettings:
|
||||
- m_BuildTarget: Android
|
||||
m_StaticBatching: 1
|
||||
m_DynamicBatching: 0
|
||||
- m_BuildTarget: iPhone
|
||||
m_StaticBatching: 1
|
||||
m_DynamicBatching: 0
|
||||
m_BuildTargetShaderSettings: []
|
||||
m_BuildTargetGraphicsJobs:
|
||||
- m_BuildTarget: MacStandaloneSupport
|
||||
@@ -840,7 +836,7 @@ PlayerSettings:
|
||||
webEnableSubmoduleStrippingCompatibility: 0
|
||||
scriptingDefineSymbols:
|
||||
Android: ENABLE_LOG
|
||||
iPhone:
|
||||
iPhone: __UNIFIED__;__IOS__
|
||||
additionalCompilerArguments: {}
|
||||
platformArchitecture: {}
|
||||
scriptingBackend:
|
||||
|
||||
120
docs/custom_log_console.md
Normal file
120
docs/custom_log_console.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Custom Log Console
|
||||
|
||||
## Overview
|
||||
|
||||
A centralized logging system with an advanced filtering console that automatically tags log entries with class and method names. Provides powerful filtering capabilities beyond Unity's default console, including multi-select filters, text search, time-range filtering, and log export.
|
||||
|
||||
## Using the Logging System in Code
|
||||
|
||||
All logging automatically captures the calling class and method name using `CallerMemberName` and `CallerFilePath` attributes. Simply call the static logging methods:
|
||||
|
||||
```csharp
|
||||
using Core;
|
||||
|
||||
public class MyClass : ManagedBehaviour
|
||||
{
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
Logging.Debug("Initialization complete");
|
||||
Logging.Info("Player spawned at position");
|
||||
Logging.Warning("Missing configuration, using defaults");
|
||||
Logging.Error("Failed to load required asset");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Output format:**
|
||||
```
|
||||
[ClassName][MethodName] Your message
|
||||
```
|
||||
|
||||
**Available methods:**
|
||||
- `Logging.Debug(string message)` - Detailed diagnostic information
|
||||
- `Logging.Info(string message)` - General informational messages
|
||||
- `Logging.Warning(string message)` - Non-critical issues
|
||||
- `Logging.Error(string message)` - Critical errors
|
||||
|
||||
**Note:** All logs are broadcast via the `Logging.OnLogEntryAdded` event and stored in a central buffer accessible via `Logging.GetRecentLogs()`.
|
||||
|
||||
---
|
||||
|
||||
## Opening the Console Window
|
||||
|
||||
**Menu:** `AppleHills > Custom Log Console`
|
||||
|
||||
You can open multiple independent console instances with different filter configurations to monitor separate systems simultaneously
|
||||
|
||||
---
|
||||
|
||||
## Console Interface
|
||||
|
||||

|
||||
|
||||
### Toolbar Controls
|
||||
|
||||
#### 🔵 Basic Controls (Blue outline)
|
||||
- **Clear** - Clears all log entries and resets tag lists
|
||||
- **Auto-scroll** - Automatically scrolls to newest entries when enabled
|
||||
|
||||
#### Filter Buttons (Persistent Popups)
|
||||
|
||||
All filter buttons open persistent popup windows that remain open during multi-selection. Changes apply when you click "Apply" or dismiss with "Close".
|
||||
|
||||
- **🔴 Classes Filter (Red outline)**
|
||||
- Multi-select which classes to display
|
||||
- Includes search box for quick filtering
|
||||
- All/None quick actions
|
||||
|
||||
- **🟢 Methods Filter (Green outline)**
|
||||
- Multi-select which methods to display
|
||||
- Includes search box for quick filtering
|
||||
- All/None quick actions
|
||||
|
||||
- **🟡 Levels Filter (Yellow outline)**
|
||||
- Toggle Debug, Info, Warning, Error levels
|
||||
- All/None quick actions
|
||||
|
||||
- **⏱ Time Filter**
|
||||
- Opens utility window with MinMaxSlider
|
||||
- Filter logs by timestamp range
|
||||
- Enable/disable toggle with reset option
|
||||
|
||||
#### Search & Export
|
||||
- **Search** - Full-text search across class names, method names, and message content
|
||||
- **Export** - Save filtered logs to .txt file with timestamp
|
||||
- **Count** - Shows `filtered/total` log count
|
||||
|
||||
---
|
||||
|
||||
## Visual Indicators
|
||||
|
||||
**Color Coding:**
|
||||
- White: Debug/Info (normal operation)
|
||||
- Yellow: Warning (non-critical issues)
|
||||
- Red: Error (critical failures)
|
||||
|
||||
**Alternating Rows:** Light/dark grey backgrounds improve readability for dense log output.
|
||||
|
||||
---
|
||||
|
||||
## Technical Details
|
||||
|
||||
**Event Broadcasting:**
|
||||
```csharp
|
||||
Logging.OnLogEntryAdded += (LogEntry entry) => { /* handle */ };
|
||||
```
|
||||
|
||||
**Manual Log Retrieval:**
|
||||
```csharp
|
||||
List<LogEntry> recentLogs = Logging.GetRecentLogs();
|
||||
```
|
||||
|
||||
**LogEntry Structure:**
|
||||
- `ClassName` - Captured from calling file path
|
||||
- `MethodName` - Captured from `CallerMemberName`
|
||||
- `Message` - User-provided message text
|
||||
- `Level` - Debug/Info/Warning/Error enum
|
||||
- `Timestamp` - Time.realtimeSinceStartup
|
||||
- `FullFormattedMessage` - Complete formatted string
|
||||
|
||||
---
|
||||
BIN
docs/media/custom_console.png
Normal file
BIN
docs/media/custom_console.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
Reference in New Issue
Block a user