using System; using System.Linq; using UnityEngine; using UnityEngine.InputSystem; namespace InputGlyphsFramework { /// /// Central input service. Holds reference to PlayerInput, loads/saves binding overrides, /// exposes simple helpers and broadcasts control-scheme and binding change events. /// [DefaultExecutionOrder(-200)] public class InputService : MonoBehaviour { public static InputService Instance { get; private set; } [Header("References")] public PlayerInput playerInput; [Header("Persistence")] public string bindingsPrefsKey = "bindings_v1"; // Events public event Action OnBindingChanged; public event Action OnControlSchemeChanged; void Awake() { if (Instance != null && Instance != this) { Destroy(this); return; } Instance = this; if (playerInput == null) playerInput = GetComponent(); // subscribe to device watcher (user provided) try { InputDeviceWatcher.OnDeviceChanged += DeviceChanged; } catch { /* if DeviceInputWatcher not present at compile time ignore */ } if (playerInput != null) playerInput.onControlsChanged += PlayerInput_onControlsChanged; // Load overrides if exist LoadOverridesFromPrefs(); } void OnDestroy() { if (Instance == this) Instance = null; try { InputDeviceWatcher.OnDeviceChanged -= DeviceChanged; } catch { } if (playerInput != null) playerInput.onControlsChanged -= PlayerInput_onControlsChanged; } private void PlayerInput_onControlsChanged(PlayerInput obj) { OnControlSchemeChanged?.Invoke(obj.currentControlScheme); } private void DeviceChanged(InputDeviceWatcher.InputDeviceCategory cat) { // Map DeviceInputWatcher category to simple scheme string string scheme = "Keyboard"; switch (cat) { case InputDeviceWatcher.InputDeviceCategory.Keyboard: scheme = "Keyboard"; break; case InputDeviceWatcher.InputDeviceCategory.Xbox: scheme = "Gamepad"; break; case InputDeviceWatcher.InputDeviceCategory.PlayStation: scheme = "Gamepad"; break; case InputDeviceWatcher.InputDeviceCategory.Other: scheme = "Keyboard"; break; // default to keyboard } OnControlSchemeChanged?.Invoke(scheme); } // Save overrides JSON public void SaveOverridesToPrefs() { if (playerInput == null || playerInput.actions == null) return; try { string json = playerInput.actions.SaveBindingOverridesAsJson(); PlayerPrefs.SetString(bindingsPrefsKey, json); PlayerPrefs.Save(); } catch (Exception e) { Debug.LogWarning("InputService: Failed to save binding overrides: " + e.Message); } } // Load overrides; if none present the action asset keeps defaults public void LoadOverridesFromPrefs() { if (playerInput == null || playerInput.actions == null) return; if (!PlayerPrefs.HasKey(bindingsPrefsKey)) return; var json = PlayerPrefs.GetString(bindingsPrefsKey); if (string.IsNullOrEmpty(json)) return; try { playerInput.actions.LoadBindingOverridesFromJson(json); // Notify listeners for all actions (best-effort) foreach (var map in playerInput.actions.actionMaps) { foreach (var action in map.actions) OnBindingChanged?.Invoke(action, -1); } } catch (Exception e) { Debug.LogWarning("InputService: Failed to load binding overrides: " + e.Message); } } public static string GetBindingDisplay(InputAction action) { if (action == null) return string.Empty; InputControl control = GetBindingControl(action); return control != null ? control.displayName : string.Empty; } public static string GetBindingControlPath(InputAction action) { if (action == null) return string.Empty; InputControl control = GetBindingControl(action); return control != null ? control.path : string.Empty; } public static InputControl GetBindingControl(InputAction action) { if (action == null) return null; var curCategory = InputDeviceWatcher.CurrentCategory; var curDeviceName = InputDeviceWatcher.CurrentDeviceName ?? ""; var hints = GetDeviceHintsForCategory(curCategory); foreach (var control in action.controls) { if (!string.IsNullOrEmpty(curDeviceName) && (control.device.displayName ?? "").Equals(curDeviceName, StringComparison.OrdinalIgnoreCase) || hints.Any(h => (control.device.layout ?? "").ToLowerInvariant().Contains(h.ToLowerInvariant()))) { return control; } } return null; } static string[] GetDeviceHintsForCategory(InputDeviceWatcher.InputDeviceCategory cat) { switch (cat) { case InputDeviceWatcher.InputDeviceCategory.Keyboard: return new[] { "Keyboard", "Mouse" }; case InputDeviceWatcher.InputDeviceCategory.Xbox: return new[] { "XInput", "Xbox", "Gamepad" }; case InputDeviceWatcher.InputDeviceCategory.PlayStation: return new[] { "DualShock", "DualSense", "PlayStation", "Gamepad" }; default: return new[] { "Gamepad", "Joystick", "Keyboard", "Mouse" }; } } // Notify when a binding was changed programmatically public void NotifyBindingChanged(InputAction action, int bindingIndex) { OnBindingChanged?.Invoke(action, bindingIndex); } } }