AlicizaX/Client/Assets/InputGlyph/InputService.cs
2025-12-05 19:06:22 +08:00

182 lines
6.4 KiB
C#

using System;
using System.Linq;
using UnityEngine;
using UnityEngine.InputSystem;
namespace InputGlyphsFramework
{
/// <summary>
/// Central input service. Holds reference to PlayerInput, loads/saves binding overrides,
/// exposes simple helpers and broadcasts control-scheme and binding change events.
/// </summary>
[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<InputAction, int> OnBindingChanged;
public event Action<string> OnControlSchemeChanged;
void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(this);
return;
}
Instance = this;
if (playerInput == null) playerInput = GetComponent<PlayerInput>();
// 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);
}
}
}