feat: debug scene navigation system

This commit is contained in:
Ferlat Thibaud
2026-06-02 12:53:44 +02:00
parent 5f3a527cc5
commit dec1bed017
25 changed files with 7099 additions and 2055 deletions
+8 -1
View File
@@ -184,7 +184,14 @@ MonoBehaviour:
SaveAudio: {fileID: 0}
Overrides: []
Scenes:
SceneDatas: []
SceneDatas:
- {fileID: 11400000, guid: b09fa30cf6bafad46a041153b0bdb17e, type: 2}
- {fileID: 11400000, guid: a996cfbfd3cdcf94fbcc944840699825, type: 2}
- {fileID: 11400000, guid: b092f756642b19043bec9bab913906f9, type: 2}
- {fileID: 11400000, guid: 95381629b7e563a45b923c8eca96a142, type: 2}
- {fileID: 11400000, guid: eef5b93bc2c31ae48ac01a27f20884d5, type: 2}
- {fileID: 11400000, guid: 23d4f22660e26344b8e2e2488c560c4c, type: 2}
- {fileID: 11400000, guid: ecd5a6faaacb2244fa810987501a9b0b, type: 2}
UseAddressables: 0
FadeSettings:
FadeInDuration: 0.5
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -15,4 +15,4 @@ MonoBehaviour:
objectName: Play_Amb_Scarecrowr_Placeholder
id: 2236081422
guid: 77D59EA9-AA28-4E2C-814C-DDD7A675CCBC
IsInUserDefinedSoundBank: 0
IsInUserDefinedSoundBank: 1
@@ -0,0 +1,21 @@
%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: 11500000, guid: 9089d8b7e41057e4cac3aad004af4b93, type: 3}
m_Name: debug_navigation
m_EditorClassIdentifier: Assembly-CSharp::Unheimlich.Navigation.Debug.DebugNavigation
_scenes:
- {fileID: 11400000, guid: a996cfbfd3cdcf94fbcc944840699825, type: 2}
- {fileID: 11400000, guid: b092f756642b19043bec9bab913906f9, type: 2}
- {fileID: 11400000, guid: 95381629b7e563a45b923c8eca96a142, type: 2}
- {fileID: 11400000, guid: eef5b93bc2c31ae48ac01a27f20884d5, type: 2}
- {fileID: 11400000, guid: 23d4f22660e26344b8e2e2488c560c4c, type: 2}
- {fileID: 11400000, guid: ecd5a6faaacb2244fa810987501a9b0b, type: 2}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f20090b5a50ef084fa449b094cd9fa09
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,17 @@
%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: 11500000, guid: a1f04313388f6a846a9e2a6bfe6e985a, type: 3}
m_Name: scene_Omnissiah
m_EditorClassIdentifier: OctoCore.Runtime::OctoCore.Navigation.Data.OctoSceneData
Id: scene_Omnissiah
SceneName: Omnissiah
UseFadeTransition: 1
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b092f756642b19043bec9bab913906f9
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,17 @@
%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: 11500000, guid: a1f04313388f6a846a9e2a6bfe6e985a, type: 3}
m_Name: scene_titleScreen
m_EditorClassIdentifier: OctoCore.Runtime::OctoCore.Navigation.Data.OctoSceneData
Id: scene_titleScreen
SceneName: TitleScreen
UseFadeTransition: 1
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b09fa30cf6bafad46a041153b0bdb17e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+3 -3
View File
@@ -2342,7 +2342,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: => To the next scene
m_text: To the next scene =>
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
@@ -20848,7 +20848,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
m_IsActive: 1
--- !u!224 &1962392721
RectTransform:
m_ObjectHideFlags: 0
@@ -21173,7 +21173,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 'R2 to the next scene '
m_text: To the next scene R2
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+4 -111
View File
@@ -307,7 +307,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchoredPosition.x
value: 240.00002
value: 240
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchoredPosition.y
@@ -1293,7 +1293,6 @@ RectTransform:
- {fileID: 1869488458}
- {fileID: 1841450551}
- {fileID: 1903583299}
- {fileID: 1880389255}
m_Father: {fileID: 2109208143}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -1982,7 +1981,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchoredPosition.x
value: 720.00006
value: 960
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchoredPosition.y
@@ -2229,7 +2228,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchoredPosition.x
value: 240.00002
value: 320
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchoredPosition.y
@@ -2261,112 +2260,6 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
m_PrefabInstance: {fileID: 1869488457}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &1880389254
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 1354814231}
m_Modifications:
- target: {fileID: 148381449121762492, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_Name
value: Button Variant (3)
objectReference: {fileID: 0}
- target: {fileID: 710399409615174608, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_text
value: 8
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchorMax.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_SizeDelta.x
value: 200
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_SizeDelta.y
value: 200
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_LocalRotation.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_LocalRotation.y
value: -0
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchoredPosition.x
value: 1680.0001
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchoredPosition.y
value: -690
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
--- !u!224 &1880389255 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
m_PrefabInstance: {fileID: 1880389254}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &1903583298
PrefabInstance:
m_ObjectHideFlags: 0
@@ -2449,7 +2342,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchoredPosition.x
value: 1200.0001
value: 1600
objectReference: {fileID: 0}
- target: {fileID: 5856627074613423090, guid: 10b589291a51b4a4f858c6c79ab6772a, type: 3}
propertyPath: m_AnchoredPosition.y
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0412faee34f1f1e4a9163b338262c618
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: de5e8b6dd52514441b1115e22ce12f85
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using OctoCore.Navigation.Data;
using OctoRef = OctoCore.OctoCore;
namespace Unheimlich.Navigation.Debuging
{
/// <summary>
/// ScriptableObject holding the ordered list of debug scenes.
/// Drag OctoSceneData assets in the desired order in the inspector.
/// </summary>
[CreateAssetMenu(fileName = "DebugNavigation", menuName = "Unheimlich/Debug/Debug Navigation")]
public class DebugNavigation : ScriptableObject
{
[Tooltip("Ordered list of scenes available in debug mode.")]
[SerializeField] private List<OctoSceneData> _scenes = new List<OctoSceneData>();
public IReadOnlyList<OctoSceneData> Scenes => _scenes;
/// <summary>Invoked when a debug scene change is requested.</summary>
public Action<OctoSceneData> OnDebugSceneRequested;
private int _currentIndex = -1;
public int CurrentIndex => _currentIndex;
/// <summary>
/// Sync the current index with the active scene.
/// If the active scene is not in the list, _currentIndex stays at -1
/// so Next() will correctly start at index 0.
/// </summary>
public void SyncWithCurrentScene(OctoSceneData currentScene)
{
_currentIndex = currentScene != null ? _scenes.IndexOf(currentScene) : -1;
if (_currentIndex == -1)
UnityEngine.Debug.Log("[DebugNavigation] Scene not found in debug list — Next() will start at index 0.");
}
/// <summary>Load the previous scene in the list (wraps).</summary>
public void Previous()
{
if (_scenes == null || _scenes.Count == 0) return;
// If not in list, go to last
_currentIndex = (_currentIndex <= 0) ? _scenes.Count - 1 : _currentIndex - 1;
RequestScene(_currentIndex);
}
/// <summary>Load the next scene in the list (wraps).</summary>
public void Next()
{
if (_scenes == null || _scenes.Count == 0) return;
// If not in list (-1), go to 0. Otherwise increment.
_currentIndex = (_currentIndex >= _scenes.Count - 1) ? 0 : _currentIndex + 1;
RequestScene(_currentIndex);
}
/// <summary>Load a scene by index directly (called by Debug Menu UI buttons).</summary>
public void LoadByIndex(int index)
{
if (index < 0 || index >= _scenes.Count)
{
UnityEngine.Debug.LogWarning($"[DebugNavigation] Index {index} out of range.");
return;
}
_currentIndex = index;
RequestScene(_currentIndex);
}
private void RequestScene(int index)
{
OctoSceneData scene = _scenes[index];
if (scene == null)
{
UnityEngine.Debug.LogWarning($"[DebugNavigation] Scene at index {index} is null.");
return;
}
// Don't reload the scene that's already active
if (OctoRef.Instance != null &&
OctoRef.Instance.Navigation.CurrentScene == scene)
{
UnityEngine.Debug.Log($"[DebugNavigation] Scene '{scene.SceneName}' is already loaded, skipping.");
return;
}
OnDebugSceneRequested?.Invoke(scene);
if (OctoRef.Instance)
OctoRef.Instance.Navigation.LoadScene(scene);
else
UnityEngine.Debug.LogError("[DebugNavigation] OctoCore instance not found.");
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9089d8b7e41057e4cac3aad004af4b93
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 767e525b8e268ce40b879a01f27987aa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,320 @@
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using OctoRef = OctoCore.OctoCore;
namespace Unheimlich.Navigation.Debuging
{
/// <summary>
/// Listens for debug input combos and drives DebugNavigation.
///
/// — Gamepad : L2+R2 held 0.5s → unlock/lock. L2/R2 short press → prev/next (menu must be open).
/// — Keyboard : backquote (² AZERTY / ~ QWERTY) → unlock/lock. ←/→ → prev/next.
///
/// Device display: switches based on last active input, not physical connection.
/// </summary>
public class DebugMenuUnlocker : MonoBehaviour
{
[Header("Data")]
[SerializeField] private DebugNavigation _debugNavigation;
[Header("Timing")]
[Tooltip("Duration in seconds L2+R2 must be held to toggle the debug menu.")]
[SerializeField] private float _holdDuration = 0.5f;
[Header("Unlock Events")]
public UnityEvent OnDebugMenuUnlockedGamepad;
public UnityEvent OnDebugMenuUnlockedKeyboard;
public UnityEvent OnDebugMenuLocked;
[Header("Navigation Events")]
public UnityEvent OnPreviousScene;
public UnityEvent OnNextScene;
[Header("Device Switch Events")]
[Tooltip("Fired when gamepad input is detected and last input was keyboard/mouse.")]
public UnityEvent OnSwitchedToGamepad;
[Tooltip("Fired when keyboard/mouse input is detected and last input was gamepad.")]
public UnityEvent OnSwitchedToKeyboard;
// ── State ────────────────────────────────────────────────────────────
private bool _isUnlocked = false;
private bool _isL2Held = false;
private bool _isR2Held = false;
private float _holdTimer = 0f;
private bool _holdFired = false;
private bool _lastWasGamepad = false;
// Input Actions
private InputAction _keyboardUnlockAction;
private InputAction _gamepadL2Action;
private InputAction _gamepadR2Action;
private InputAction _keyboardPreviousAction;
private InputAction _keyboardNextAction;
private InputAction _anyGamepadAction;
private InputAction _anyKeyboardAction;
// ── Unity lifecycle ──────────────────────────────────────────────────
private void Awake() => BuildInputActions();
private void OnEnable() => EnableActions();
private void Start()
{
StartCoroutine(InitWhenReady());
}
private IEnumerator InitWhenReady()
{
yield return new WaitUntil(() => OctoRef.Instance != null);
yield return new WaitUntil(() => OctoRef.Instance.Navigation.CurrentScene != null);
SyncDebugNavigation();
SubscribeToSceneEvents();
}
private void OnDisable()
{
DisableActions();
UnsubscribeFromSceneEvents();
}
private void OnDestroy() => DisposeActions();
private void Update() => TickHoldTimer();
// ── Build ────────────────────────────────────────────────────────────
private void BuildInputActions()
{
// Keyboard unlock toggle
_keyboardUnlockAction = new InputAction("DebugUnlockKeyboard", InputActionType.Button);
_keyboardUnlockAction.AddBinding("<Keyboard>/backquote");
_keyboardUnlockAction.performed += _ => ToggleDebugMenu(false);
// Gamepad L2 / R2
_gamepadL2Action = new InputAction("DebugL2", InputActionType.Button);
_gamepadL2Action.AddBinding("<Gamepad>/leftTrigger")
.WithProcessor("AxisDeadzone(min=0.5,max=1)");
_gamepadL2Action.performed += _ => OnL2Down();
_gamepadL2Action.canceled += _ => OnL2Up();
_gamepadR2Action = new InputAction("DebugR2", InputActionType.Button);
_gamepadR2Action.AddBinding("<Gamepad>/rightTrigger")
.WithProcessor("AxisDeadzone(min=0.5,max=1)");
_gamepadR2Action.performed += _ => OnR2Down();
_gamepadR2Action.canceled += _ => OnR2Up();
// Keyboard navigation
_keyboardPreviousAction = new InputAction("DebugPrevKeyboard", InputActionType.Button);
_keyboardPreviousAction.AddBinding("<Keyboard>/leftArrow");
_keyboardPreviousAction.performed += _ => TryPrevious();
_keyboardNextAction = new InputAction("DebugNextKeyboard", InputActionType.Button);
_keyboardNextAction.AddBinding("<Keyboard>/rightArrow");
_keyboardNextAction.performed += _ => TryNext();
// Device detection — wildcard bindings
// Note: on exclut les triggers du anyGamepad pour éviter les faux positifs
// lors du relâchement après un hold unlock
_anyGamepadAction = new InputAction("AnyGamepad", InputActionType.PassThrough);
_anyGamepadAction.AddBinding("<Gamepad>/leftStick");
_anyGamepadAction.AddBinding("<Gamepad>/rightStick");
_anyGamepadAction.AddBinding("<Gamepad>/buttonSouth");
_anyGamepadAction.AddBinding("<Gamepad>/buttonNorth");
_anyGamepadAction.AddBinding("<Gamepad>/buttonEast");
_anyGamepadAction.AddBinding("<Gamepad>/buttonWest");
_anyGamepadAction.AddBinding("<Gamepad>/leftShoulder");
_anyGamepadAction.AddBinding("<Gamepad>/rightShoulder");
_anyGamepadAction.AddBinding("<Gamepad>/start");
_anyGamepadAction.AddBinding("<Gamepad>/select");
_anyGamepadAction.AddBinding("<Gamepad>/dpad");
_anyGamepadAction.performed += _ => OnAnyGamepadInput();
_anyKeyboardAction = new InputAction("AnyKeyboard", InputActionType.PassThrough);
_anyKeyboardAction.AddBinding("<Keyboard>/anyKey");
_anyKeyboardAction.AddBinding("<Mouse>/delta");
_anyKeyboardAction.AddBinding("<Mouse>/leftButton");
_anyKeyboardAction.AddBinding("<Mouse>/rightButton");
_anyKeyboardAction.performed += _ => OnAnyKeyboardInput();
}
private void EnableActions()
{
_keyboardUnlockAction?.Enable();
_gamepadL2Action?.Enable();
_gamepadR2Action?.Enable();
_keyboardPreviousAction?.Enable();
_keyboardNextAction?.Enable();
_anyGamepadAction?.Enable();
_anyKeyboardAction?.Enable();
}
private void DisableActions()
{
_keyboardUnlockAction?.Disable();
_gamepadL2Action?.Disable();
_gamepadR2Action?.Disable();
_keyboardPreviousAction?.Disable();
_keyboardNextAction?.Disable();
_anyGamepadAction?.Disable();
_anyKeyboardAction?.Disable();
}
private void DisposeActions()
{
_keyboardUnlockAction?.Dispose();
_gamepadL2Action?.Dispose();
_gamepadR2Action?.Dispose();
_keyboardPreviousAction?.Dispose();
_keyboardNextAction?.Dispose();
_anyGamepadAction?.Dispose();
_anyKeyboardAction?.Dispose();
}
// ── L2 / R2 hold logic ───────────────────────────────────────────────
private void OnL2Down()
{
_isL2Held = true;
if (_isR2Held) StartHold();
}
private void OnL2Up()
{
// Short press navigation — only if hold didn't fire
if (_isUnlocked && !_holdFired && !_isR2Held)
TryPrevious();
_isL2Held = false;
// Reset hold only when both triggers are released
if (!_isR2Held) ResetHold();
}
private void OnR2Down()
{
_isR2Held = true;
if (_isL2Held) StartHold();
}
private void OnR2Up()
{
// Short press navigation — only if hold didn't fire
if (_isUnlocked && !_holdFired && !_isL2Held)
TryNext();
_isR2Held = false;
// Reset hold only when both triggers are released
if (!_isL2Held) ResetHold();
}
private void StartHold()
{
_holdTimer = 0f;
_holdFired = false;
}
private void ResetHold()
{
_holdTimer = 0f;
_holdFired = false;
}
private void TickHoldTimer()
{
if (!_isL2Held || !_isR2Held || _holdFired) return;
_holdTimer += Time.unscaledDeltaTime;
if (_holdTimer >= _holdDuration)
{
_holdFired = true; // marque comme fired AVANT le toggle pour bloquer OnR2Up/OnL2Up
ToggleDebugMenu(true);
}
}
// ── Device switch ────────────────────────────────────────────────────
private void OnAnyGamepadInput()
{
if (_lastWasGamepad) return;
_lastWasGamepad = true;
OnSwitchedToGamepad?.Invoke();
}
private void OnAnyKeyboardInput()
{
if (!_lastWasGamepad) return;
_lastWasGamepad = false;
OnSwitchedToKeyboard?.Invoke();
}
// ── Logic ────────────────────────────────────────────────────────────
private void SubscribeToSceneEvents()
{
if (OctoRef.Instance == null) return;
OctoRef.Instance.Navigation.OnSceneLoadCompleted += OnSceneLoadCompleted;
}
private void UnsubscribeFromSceneEvents()
{
if (OctoRef.Instance == null) return;
OctoRef.Instance.Navigation.OnSceneLoadCompleted -= OnSceneLoadCompleted;
}
private void OnSceneLoadCompleted(OctoCore.Navigation.Data.OctoSceneData sceneData)
{
_debugNavigation?.SyncWithCurrentScene(sceneData);
}
private void SyncDebugNavigation()
{
if (_debugNavigation == null || OctoRef.Instance == null) return;
_debugNavigation.SyncWithCurrentScene(OctoRef.Instance.Navigation.CurrentScene);
}
private void ToggleDebugMenu(bool isGamepad)
{
_isUnlocked = !_isUnlocked;
if (_isUnlocked)
{
SyncDebugNavigation();
if (isGamepad) OnDebugMenuUnlockedGamepad?.Invoke();
else OnDebugMenuUnlockedKeyboard?.Invoke();
UnityEngine.Debug.Log($"[DebugMenuUnlocker] Unlocked ({(isGamepad ? "Gamepad" : "Keyboard")}).");
}
else
{
OnDebugMenuLocked?.Invoke();
UnityEngine.Debug.Log("[DebugMenuUnlocker] Locked.");
}
}
private void TryPrevious()
{
if (!_isUnlocked || _debugNavigation == null) return;
OnPreviousScene?.Invoke();
_debugNavigation.Previous();
}
private void TryNext()
{
if (!_isUnlocked || _debugNavigation == null) return;
OnNextScene?.Invoke();
_debugNavigation.Next();
}
/// <summary>Called directly by Debug Menu UI buttons (0-based index).</summary>
public void LoadSceneByIndex(int index)
{
if (_debugNavigation == null) return;
_debugNavigation.LoadByIndex(index);
}
public bool IsUnlocked => _isUnlocked;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e578ffb82b0e1c74bbb3a7e914b5b305
@@ -5,9 +5,15 @@ EditorBuildSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Scenes:
- enabled: 1
path: Assets/_Content/Scenes/TitleScreen.unity
guid: 2b03899301d0b354382e3608b8ed52a8
- enabled: 1
path: Assets/_Content/Scenes/Theatre.unity
guid: d8ad4f16aafb1e54cbd157a8059be2c5
- enabled: 1
path: Assets/_Content/Scenes/Omnissiah.unity
guid: 78c6b217405801e40818f8e440fb7174
- enabled: 1
path: Assets/_Content/Scenes/HouseOfLeaves.unity
guid: 05519f88e1cce5146843472b6b858607