Fix: cleanup and minor perf improvements

This commit is contained in:
Ferlat Thibaud
2026-03-25 01:28:18 +01:00
parent 9fd251f6e9
commit e69bdb93f8
21 changed files with 130 additions and 136 deletions
@@ -8,6 +8,21 @@ namespace Unheimlich.Core
/// </summary> /// </summary>
public static class PlayerEvents public static class PlayerEvents
{ {
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetAll()
{
OnJump = null;
OnTeleport = null;
OnCameraShake = null;
OnDiapasonEquip = null;
OnDiapasonUnequip = null;
OnDiapasonTap = null;
OnEnterDialogue = null;
OnExitDialogue = null;
OnEnterListening = null;
OnExitListening = null;
}
// Movement events // Movement events
public static event System.Action OnJump; public static event System.Action OnJump;
public static event System.Action<Vector3, Quaternion> OnTeleport; public static event System.Action<Vector3, Quaternion> OnTeleport;
@@ -17,7 +17,8 @@ namespace Unheimlich.Core.Input
Normal, // All inputs enabled Normal, // All inputs enabled
Dialogue, // Only look and interact Dialogue, // Only look and interact
Listening, // Only look and diapason Listening, // Only look and diapason
Inspection // Only look and interact (for item inspection) Inspection, // Only look and interact (for item inspection)
DetectiveBoard // Mouse cursor free, movement disabled
} }
[Header("Current State")] [Header("Current State")]
@@ -30,6 +31,14 @@ namespace Unheimlich.Core.Input
public static Vector2 MoveInput { get; private set; } public static Vector2 MoveInput { get; private set; }
public static Vector2 LookInput { get; private set; } public static Vector2 LookInput { get; private set; }
// Cached InputAction references — populated once during initialization
private static UnityEngine.InputSystem.InputAction _cachedMoveAction;
private static UnityEngine.InputSystem.InputAction _cachedLookAction;
private static UnityEngine.InputSystem.InputAction _cachedInteractAction;
private static UnityEngine.InputSystem.InputAction _cachedDiapasonAction;
private static UnityEngine.InputSystem.InputAction _cachedFireAction;
private static UnityEngine.InputSystem.InputAction _cachedExitAction;
// Legacy compatibility // Legacy compatibility
public static bool IsInputEnabled => CurrentContext == InputContext.Normal; public static bool IsInputEnabled => CurrentContext == InputContext.Normal;
public static bool IsLookingEnabled => CurrentContext != InputContext.Dialogue; public static bool IsLookingEnabled => CurrentContext != InputContext.Dialogue;
@@ -38,6 +47,7 @@ namespace Unheimlich.Core.Input
{ {
// Try to subscribe, will retry in Start if needed // Try to subscribe, will retry in Start if needed
SubscribeAllInputs(); SubscribeAllInputs();
CacheAllActions();
SetContext(InputContext.Normal); SetContext(InputContext.Normal);
} }
@@ -56,8 +66,9 @@ namespace Unheimlich.Core.Input
} }
// Ensure we're subscribed // Ensure we're subscribed and actions are cached
SubscribeAllInputs(); SubscribeAllInputs();
CacheAllActions();
} }
private void OnDestroy() private void OnDestroy()
@@ -79,6 +90,38 @@ namespace Unheimlich.Core.Input
OctoCore.OctoCore.Instance.Inputs.SubscribeAction(OctoActions.Player_Exit, OnExit); OctoCore.OctoCore.Instance.Inputs.SubscribeAction(OctoActions.Player_Exit, OnExit);
} }
/// <summary>
/// Caches all InputAction references from OctoCore once at startup to avoid
/// per-frame iteration over the Actions list.
/// </summary>
private void CacheAllActions()
{
var asset = OctoCore.OctoCore.Instance?.Inputs?.Actions;
if (asset == null) return;
_cachedMoveAction = null;
_cachedLookAction = null;
_cachedInteractAction = null;
_cachedDiapasonAction = null;
_cachedFireAction = null;
_cachedExitAction = null;
foreach (var actionInfo in asset)
{
if (actionInfo.Reference == null) continue;
switch (actionInfo.Name)
{
case "Move": _cachedMoveAction = actionInfo.Reference.action; break;
case "Look": _cachedLookAction = actionInfo.Reference.action; break;
case "Interact": _cachedInteractAction = actionInfo.Reference.action; break;
case "Diapason": _cachedDiapasonAction = actionInfo.Reference.action; break;
case "Fire": _cachedFireAction = actionInfo.Reference.action; break;
case "Exit": _cachedExitAction = actionInfo.Reference.action; break;
}
}
}
private void UnsubscribeAllInputs() private void UnsubscribeAllInputs()
{ {
if (OctoCore.OctoCore.Instance == null) return; if (OctoCore.OctoCore.Instance == null) return;
@@ -126,6 +169,12 @@ namespace Unheimlich.Core.Input
Cursor.lockState = CursorLockMode.Locked; Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false; Cursor.visible = false;
break; break;
case InputContext.DetectiveBoard:
// Mouse cursor free for board interaction, movement disabled
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
break;
} }
} }
@@ -180,83 +229,12 @@ namespace Unheimlich.Core.Input
public static Vector2 GetMoveInput() => MoveInput; public static Vector2 GetMoveInput() => MoveInput;
public static Vector2 GetLookInput() => LookInput; public static Vector2 GetLookInput() => LookInput;
// Action accessors for direct button checks // Action accessors — return pre-cached references, no per-frame allocation
public static UnityEngine.InputSystem.InputAction GetMoveAction() public static UnityEngine.InputSystem.InputAction GetMoveAction() => _cachedMoveAction;
{ public static UnityEngine.InputSystem.InputAction GetLookAction() => _cachedLookAction;
var asset = OctoCore.OctoCore.Instance?.Inputs?.Actions; public static UnityEngine.InputSystem.InputAction GetInteractAction() => _cachedInteractAction;
if (asset == null) return null; public static UnityEngine.InputSystem.InputAction GetDiapasonAction() => _cachedDiapasonAction;
public static UnityEngine.InputSystem.InputAction GetFireAction() => _cachedFireAction;
foreach (var actionInfo in asset) public static UnityEngine.InputSystem.InputAction GetExitAction() => _cachedExitAction;
{
if (actionInfo.Name == "Move" && actionInfo.Reference != null)
return actionInfo.Reference.action;
}
return null;
}
public static UnityEngine.InputSystem.InputAction GetDiapasonAction()
{
var asset = OctoCore.OctoCore.Instance?.Inputs?.Actions;
if (asset == null) return null;
foreach (var actionInfo in asset)
{
if (actionInfo.Name == "Diapason" && actionInfo.Reference != null)
return actionInfo.Reference.action;
}
return null;
}
public static UnityEngine.InputSystem.InputAction GetInteractAction()
{
var asset = OctoCore.OctoCore.Instance?.Inputs?.Actions;
if (asset == null) return null;
foreach (var actionInfo in asset)
{
if (actionInfo.Name == "Interact" && actionInfo.Reference != null)
return actionInfo.Reference.action;
}
return null;
}
public static UnityEngine.InputSystem.InputAction GetFireAction()
{
var asset = OctoCore.OctoCore.Instance?.Inputs?.Actions;
if (asset == null) return null;
foreach (var actionInfo in asset)
{
if (actionInfo.Name == "Fire" && actionInfo.Reference != null)
return actionInfo.Reference.action;
}
return null;
}
public static UnityEngine.InputSystem.InputAction GetExitAction()
{
var asset = OctoCore.OctoCore.Instance?.Inputs?.Actions;
if (asset == null) return null;
foreach (var actionInfo in asset)
{
if (actionInfo.Name == "Exit" && actionInfo.Reference != null)
return actionInfo.Reference.action;
}
return null;
}
public static UnityEngine.InputSystem.InputAction GetLookAction()
{
var asset = OctoCore.OctoCore.Instance?.Inputs?.Actions;
if (asset == null) return null;
foreach (var actionInfo in asset)
{
if (actionInfo.Name == "Look" && actionInfo.Reference != null)
return actionInfo.Reference.action;
}
return null;
}
} }
} }
@@ -88,6 +88,9 @@ namespace Unheimlich.Core.Interaction
private void ResetSessionState() private void ResetSessionState()
{ {
// With Domain Reload disabled, static fields and per-instance flags persist between
// Play Mode sessions. This method is called from OnEnable() to reset all transient
// animation state so merged objects start fresh each session without needing a full reload.
if (_isActive) if (_isActive)
{ {
foreach (var word in _spawnedWords) foreach (var word in _spawnedWords)
@@ -108,17 +111,6 @@ namespace Unheimlich.Core.Interaction
_currentConvergenceRadius = 0f; _currentConvergenceRadius = 0f;
} }
// Resets _hasCompletedMergeOnce at the very start of each Play Mode session so
// merged objects start fresh without requiring a full Domain Reload.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void OnPlayModeReset()
{
// Finding all instances isn't possible from a static context with SubsystemRegistration.
// The reset is handled per-instance in OnEnable via _hasCompletedMergeOnce being
// re-initialized when the scene reloads. For Enter Play Mode without scene reload,
// ResetSessionState() is called from OnEnable and now correctly clears the flag.
}
void Update() void Update()
{ {
if (_playerTransform == null || _settings == null) return; if (_playerTransform == null || _settings == null) return;
@@ -21,6 +21,6 @@ namespace Unheimlich.Core.Utils
public const float GAMEPAD_DEADZONE = 0.01f; public const float GAMEPAD_DEADZONE = 0.01f;
public const int RENDER_TEXTURE_DEPTH = 24; public const int RENDER_TEXTURE_DEPTH = 24;
public const RenderTextureFormat RENDER_TEXTURE_FORMAT = RenderTextureFormat.ARGB32; public const RenderTextureFormat RENDER_TEXTURE_FORMAT = RenderTextureFormat.DefaultHDR;
} }
} }
@@ -1,3 +1,4 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
using UnityEngine; using UnityEngine;
using Unheimlich.Features.Player; using Unheimlich.Features.Player;
@@ -125,3 +126,4 @@ namespace Unheimlich.Dev
} }
} }
} }
#endif
@@ -1,3 +1,4 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
using UnityEngine; using UnityEngine;
using Unheimlich.Features.Player; using Unheimlich.Features.Player;
using Unheimlich.Features.Diapason; using Unheimlich.Features.Diapason;
@@ -120,3 +121,4 @@ namespace Unheimlich.Dev
} }
} }
} }
#endif
@@ -1,3 +1,4 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
using UnityEngine; using UnityEngine;
using Unheimlich.Features.OctoArms; using Unheimlich.Features.OctoArms;
@@ -179,3 +180,4 @@ public class DiapasonPoseTweaker : MonoBehaviour
} }
} }
#endif
@@ -1,3 +1,4 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
using UnityEngine; using UnityEngine;
using Unheimlich.Features.Portals; using Unheimlich.Features.Portals;
@@ -50,3 +51,4 @@ namespace Unheimlich.Dev
} }
} }
} }
#endif
@@ -1,3 +1,4 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem; using UnityEngine.InputSystem;
using Unheimlich.Features.Diapason; using Unheimlich.Features.Diapason;
@@ -45,3 +46,4 @@ public class DiapasonEquipmentTester : MonoBehaviour
} }
} }
#endif
@@ -1,3 +1,4 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
using UnityEngine; using UnityEngine;
using OctoCore.Localization; using OctoCore.Localization;
using UnityEngine.InputSystem; using UnityEngine.InputSystem;
@@ -63,3 +64,4 @@ namespace Unheimlich.Dev
} }
} }
} }
#endif
@@ -107,7 +107,7 @@ namespace Unheimlich.Features.DetectiveBoard
arms.HideArms(3f); arms.HideArms(3f);
} }
InputManager.SetContext(InputManager.InputContext.Listening); InputManager.SetContext(InputManager.InputContext.DetectiveBoard);
if (_cameraHeadBob != null) if (_cameraHeadBob != null)
{ {
@@ -119,9 +119,6 @@ namespace Unheimlich.Features.DetectiveBoard
CrosshairController.Instance.Hide(); CrosshairController.Instance.Hide();
} }
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
var cameraPan = PlayerController.Instance.CameraPanController; var cameraPan = PlayerController.Instance.CameraPanController;
var originalSettings = cameraPan.Settings; var originalSettings = cameraPan.Settings;
@@ -200,9 +197,6 @@ namespace Unheimlich.Features.DetectiveBoard
{ {
CrosshairController.Instance.Show(); CrosshairController.Instance.Show();
} }
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
} }
private void RestoreHeadBob() private void RestoreHeadBob()
@@ -63,7 +63,7 @@ namespace Unheimlich.Features.Dialogue
/// </summary> /// </summary>
public void Update() public void Update()
{ {
if (!_manager.IsInDialogue || !_manager.CurrentDialogueUI.AreChoicesVisible()) if (!_manager.IsDialogueActive || !_manager.CurrentDialogueUI.AreChoicesVisible())
return; return;
_raycastTimer += Time.deltaTime; _raycastTimer += Time.deltaTime;
@@ -16,7 +16,7 @@ namespace Unheimlich.Features.Dialogue
{ {
[Header("State")] [Header("State")]
public bool IsInDialogue = false; [SerializeField] private bool IsInDialogue = false;
[Header("Focus Settings")] [Header("Focus Settings")]
[SerializeField] private LayerMask _choiceLayer; [SerializeField] private LayerMask _choiceLayer;
@@ -65,19 +65,7 @@ namespace Unheimlich.Features.Dialogue
if (_focusSystem != null) if (_focusSystem != null)
{ {
_mainCamera = Camera.main; _mainCamera = Camera.main;
_focusSystem = new DialogueFocusSystem( InitFocusSystem();
this,
_mainCamera,
PlayerController.Instance?.CameraEffects,
_choiceLayer,
_focusRange,
_focusTime,
_raycastInterval,
_recoveryDuration,
_focusRetentionTime,
_fallbackShakeIntensity
);
_focusSystem.OnChoiceMade += (choice) => OnChoiceMade?.Invoke(choice);
// Also reset dialogue state in case we exited mid-dialogue // Also reset dialogue state in case we exited mid-dialogue
IsInDialogue = false; IsInDialogue = false;
@@ -89,7 +77,15 @@ namespace Unheimlich.Features.Dialogue
private void Start() private void Start()
{ {
_mainCamera = Camera.main; _mainCamera = Camera.main;
InitFocusSystem();
}
/// <summary>
/// Creates or recreates the DialogueFocusSystem with the current inspector settings.
/// Called from Start and OnEnabled (domain-reload-less re-entry).
/// </summary>
private void InitFocusSystem()
{
_focusSystem = new DialogueFocusSystem( _focusSystem = new DialogueFocusSystem(
this, this,
_mainCamera, _mainCamera,
@@ -10,14 +10,16 @@ namespace Unheimlich.Features.Dialogue.UI
{ {
private Transform _cameraTransform; private Transform _cameraTransform;
private void Start() private void Awake()
{ {
_cameraTransform = Camera.main.transform; _cameraTransform = Camera.main != null ? Camera.main.transform : null;
if (_cameraTransform == null) if (_cameraTransform == null)
Debug.LogWarning($"BillboardUI on {gameObject.name}: Main camera not found!"); Debug.LogWarning($"BillboardUI on {gameObject.name}: Main camera not found!");
} }
private void Start() { }
private void LateUpdate() private void LateUpdate()
{ {
if (_cameraTransform != null) if (_cameraTransform != null)
@@ -48,11 +48,14 @@ namespace Unheimlich.Features.Dialogue.UI
private int _fadeCanvasTweenId = -1; private int _fadeCanvasTweenId = -1;
private int _rotationTweenId = -1; private int _rotationTweenId = -1;
private List<int> _floatingTweenIds = new List<int>(); private List<int> _floatingTweenIds = new List<int>();
private Camera _mainCamera;
private static readonly Vector3 ViewportCenter = new Vector3(0.5f, 0.5f, 0f); private static readonly Vector3 ViewportCenter = new Vector3(0.5f, 0.5f, 0f);
private void Awake() private void Awake()
{ {
_mainCamera = Camera.main;
if (_dialogueCanvasGroup == null && _dialoguePanel != null) if (_dialogueCanvasGroup == null && _dialoguePanel != null)
{ {
_dialogueCanvasGroup = _dialoguePanel.GetComponent<CanvasGroup>(); _dialogueCanvasGroup = _dialoguePanel.GetComponent<CanvasGroup>();
@@ -366,7 +369,7 @@ namespace Unheimlich.Features.Dialogue.UI
_sortTimer = 0f; _sortTimer = 0f;
FloatingWordsSpawner.SortByDistance(_activeChoices, Camera.main); FloatingWordsSpawner.SortByDistance(_activeChoices, _mainCamera);
} }
} }
} }
@@ -44,8 +44,6 @@ namespace Unheimlich.Features.Diapason
_cameraHeadBob = GetComponentInChildren<CameraHeadBob>(); _cameraHeadBob = GetComponentInChildren<CameraHeadBob>();
} }
void OnDestroy() { }
void Update() void Update()
{ {
var diapasonAction = InputManager.GetDiapasonAction(); var diapasonAction = InputManager.GetDiapasonAction();
@@ -101,11 +101,6 @@ namespace Unheimlich.Features.Items
public float RotationSpeed => _rotationSpeed; public float RotationSpeed => _rotationSpeed;
private void OnEnable()
{
LeanTween.init();
}
private void OnDestroy() private void OnDestroy()
{ {
LeanTween.cancel(gameObject); LeanTween.cancel(gameObject);
@@ -508,8 +508,7 @@ namespace Unheimlich.Features.Portals
if (_viewTexture != null) if (_viewTexture != null)
_viewTexture.Release(); _viewTexture.Release();
// Use DefaultHDR for better shadow and lighting compatibility in URP _viewTexture = new RenderTexture(Screen.width, Screen.height, PortalConstants.RENDER_TEXTURE_DEPTH, PortalConstants.RENDER_TEXTURE_FORMAT);
_viewTexture = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.DefaultHDR);
_viewTexture.filterMode = FilterMode.Bilinear; _viewTexture.filterMode = FilterMode.Bilinear;
_viewTexture.antiAliasing = 1; _viewTexture.antiAliasing = 1;
_viewTexture.Create(); _viewTexture.Create();
@@ -31,6 +31,7 @@ namespace Unheimlich.Features.Portals
private Portal _portal; private Portal _portal;
private Dictionary<AudioSource, AudioSource> _proxyMapping = new Dictionary<AudioSource, AudioSource>(); private Dictionary<AudioSource, AudioSource> _proxyMapping = new Dictionary<AudioSource, AudioSource>();
private Dictionary<AudioSource, AudioLowPassFilter> _filterMapping = new Dictionary<AudioSource, AudioLowPassFilter>(); private Dictionary<AudioSource, AudioLowPassFilter> _filterMapping = new Dictionary<AudioSource, AudioLowPassFilter>();
private Camera _playerCamera;
private GameObject _lastLoadedRoomInstance; private GameObject _lastLoadedRoomInstance;
private bool _needsRefresh; private bool _needsRefresh;
@@ -38,6 +39,7 @@ namespace Unheimlich.Features.Portals
private void Awake() private void Awake()
{ {
_portal = GetComponent<Portal>(); _portal = GetComponent<Portal>();
_playerCamera = Camera.main;
} }
private void OnEnable() private void OnEnable()
@@ -123,7 +125,7 @@ namespace Unheimlich.Features.Portals
/// </summary> /// </summary>
private void UpdateProxies() private void UpdateProxies()
{ {
Camera playerCam = Camera.main; Camera playerCam = _playerCamera;
if (playerCam == null) if (playerCam == null)
return; return;
@@ -32,7 +32,7 @@ namespace Unheimlich.Features.Portals
/// <summary> /// <summary>
/// Represents a room instance that has been loaded through a portal. /// Represents a room instance that has been loaded through a portal.
/// Caches room bounds for efficient distance calculations. /// Caches room bounds and colliders for efficient distance/containment calculations.
/// </summary> /// </summary>
private class LoadedRoom private class LoadedRoom
{ {
@@ -42,6 +42,7 @@ namespace Unheimlich.Features.Portals
public Portal SourcePortal; public Portal SourcePortal;
public Bounds CachedBounds; public Bounds CachedBounds;
public bool BoundsCalculated = false; public bool BoundsCalculated = false;
public Collider[] CachedColliders;
} }
private void Start() private void Start()
@@ -184,7 +185,7 @@ namespace Unheimlich.Features.Portals
/// <summary> /// <summary>
/// Determines which room (if any) currently contains the player. /// Determines which room (if any) currently contains the player.
/// Uses collider bounds checks to detect room occupancy. /// Uses cached collider bounds to detect room occupancy.
/// </summary> /// </summary>
private void UpdateCurrentPlayerRoom() private void UpdateCurrentPlayerRoom()
{ {
@@ -196,10 +197,12 @@ namespace Unheimlich.Features.Portals
if (loadedRoom.RoomInstance == null) if (loadedRoom.RoomInstance == null)
continue; continue;
Collider[] roomColliders = loadedRoom.RoomInstance.GetComponentsInChildren<Collider>(); Collider[] roomColliders = loadedRoom.CachedColliders;
if (roomColliders == null) continue;
foreach (Collider col in roomColliders) foreach (Collider col in roomColliders)
{ {
if (col.bounds.Contains(_player.position)) if (col != null && col.bounds.Contains(_player.position))
{ {
_currentPlayerRoom = loadedRoom.RoomInstance; _currentPlayerRoom = loadedRoom.RoomInstance;
return; return;
@@ -247,7 +250,8 @@ namespace Unheimlich.Features.Portals
RoomInstance = roomInstance, RoomInstance = roomInstance,
RoomId = roomId, RoomId = roomId,
LoadPosition = portal.transform.position, LoadPosition = portal.transform.position,
SourcePortal = portal SourcePortal = portal,
CachedColliders = roomInstance.GetComponentsInChildren<Collider>()
}; };
_loadedRooms.Add(roomId, loadedRoom); _loadedRooms.Add(roomId, loadedRoom);
@@ -26,9 +26,13 @@ namespace Unheimlich.World.CameraSystem
private float _nextCheckTime; private float _nextCheckTime;
private Transform _playerTransform; private Transform _playerTransform;
private void Awake()
{
_playerTransform = Camera.main != null ? Camera.main.transform : null;
}
private void Start() private void Start()
{ {
_playerTransform = Camera.main.transform;
_currentShadowDistance = _baseShadowDistance; _currentShadowDistance = _baseShadowDistance;
_targetShadowDistance = _baseShadowDistance; _targetShadowDistance = _baseShadowDistance;
QualitySettings.shadowDistance = _baseShadowDistance; QualitySettings.shadowDistance = _baseShadowDistance;