Lifecycle System Refactor & Logging Centralization (#56)
## ManagedBehaviour System Refactor - **Sealed `Awake()`** to prevent override mistakes that break singleton registration - **Added `OnManagedAwake()`** for early initialization (fires during registration) - **Renamed lifecycle hook:** `OnManagedAwake()` → `OnManagedStart()` (fires after boot, mirrors Unity's Awake→Start) - **40 files migrated** to new pattern (2 core, 38 components) - Eliminated all fragile `private new void Awake()` patterns - Zero breaking changes - backward compatible ## Centralized Logging System - **Automatic tagging** via `CallerMemberName` and `CallerFilePath` - logs auto-tagged as `[ClassName][MethodName] message` - **Unified API:** Single `Logging.Debug/Info/Warning/Error()` replaces custom `LogDebugMessage()` implementations - **~90 logging call sites** migrated across 10 files - **10 redundant helper methods** removed - All logs broadcast via `Logging.OnLogEntryAdded` event for real-time monitoring ## Custom Log Console (Editor Window) - **Persistent filter popups** for multi-selection (classes, methods, log levels) - windows stay open during selection - **Search** across class names, methods, and message content - **Time range filter** with MinMaxSlider - **Export** filtered logs to timestamped `.txt` files - **Right-click context menu** for quick filtering and copy actions - **Visual improvements:** White text, alternating row backgrounds, color-coded log levels - **Multiple instances** supported for simultaneous system monitoring - Open via `AppleHills > Custom Log Console` Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com> Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Reviewed-on: #56
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,18 +423,10 @@ namespace Input
|
||||
OnArrivedAtTarget?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user