using System; using System.Threading.Tasks; using TMPro; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.UI; public class TestRebindScript : MonoBehaviour { [Header("UI")] public UXButton btn; public TextMeshProUGUI bindKeyText; public Image targetImage; [Tooltip("如果不使用 actionReference,则用 name 在全局 manager 查找")] public string actionName = "movement"; [Header("Optional composite part (WASD style)")] [Tooltip("如果需要绑定 composite 的某一部分(例如 Up/Down/Left/Right),填这个;留空表示绑定非 composite 或整体 binding")] public string compositePartName = ""; [Header("Behavior")] [Tooltip("如果 true,在 Prepare 后自动调用 ConfirmApply() 并保存;否则等待手动 ConfirmPrepared()/CancelPrepared()")] public bool autoConfirm = false; private void Start() { if (btn != null) btn.onClick.AddListener(OnBtnClicked); InputDeviceWatcher.OnDeviceChanged += OnDeviceChanged; UpdateBindingText(); if (InputBindingManager.Instance != null) { // Subscribe to events InputBindingManager.Instance.OnRebindPrepare += OnRebindPrepareHandler; InputBindingManager.Instance.OnApply += OnApplyHandler; InputBindingManager.Instance.OnRebindEnd += OnRebindEndHandler; InputBindingManager.Instance.OnRebindConflict += OnRebindConflictHandler; } } private void OnDisable() { if (btn != null) btn.onClick.RemoveListener(OnBtnClicked); InputDeviceWatcher.OnDeviceChanged -= OnDeviceChanged; if (InputBindingManager.Instance != null) { InputBindingManager.Instance.OnRebindPrepare -= OnRebindPrepareHandler; InputBindingManager.Instance.OnApply -= OnApplyHandler; InputBindingManager.Instance.OnRebindEnd -= OnRebindEndHandler; InputBindingManager.Instance.OnRebindConflict -= OnRebindConflictHandler; } } private void OnRebindPrepareHandler(InputBindingManager.RebindContext ctx) { if (IsTargetContext(ctx)) { var disp = ctx.overridePath == InputBindingManager.NULL_BINDING ? "" : ctx.overridePath; bindKeyText.text = disp; if (autoConfirm) _ = ConfirmPreparedAsync(); } } private void OnApplyHandler(bool success, HashSet appliedContexts) { if (appliedContexts != null) { // Only update if any of the applied/discarded contexts match this instance foreach (var ctx in appliedContexts) { if (IsTargetContext(ctx)) { UpdateBindingText(); break; } } } } private void OnRebindEndHandler(bool success, InputBindingManager.RebindContext context) { if (IsTargetContext(context)) { UpdateBindingText(); } } private void OnRebindConflictHandler(InputBindingManager.RebindContext prepared, InputBindingManager.RebindContext conflict) { // Update if either the prepared or conflict context matches this instance if (IsTargetContext(prepared) || IsTargetContext(conflict)) { UpdateBindingText(); } } private void OnDeviceChanged(InputDeviceWatcher.InputDeviceCategory _) { UpdateBindingText(); } private InputAction GetAction() { return InputBindingManager.Action(actionName); } private bool IsTargetContext(InputBindingManager.RebindContext ctx) { if (ctx == null || ctx.action == null) return false; var action = GetAction(); if (action == null) return false; // Must match the action if (ctx.action != action) return false; // If we have a composite part specified, we need to match the binding index if (!string.IsNullOrEmpty(compositePartName)) { // Get the binding at the context's index if (ctx.bindingIndex < 0 || ctx.bindingIndex >= action.bindings.Count) return false; var binding = action.bindings[ctx.bindingIndex]; // Check if the binding's name matches our composite part return string.Equals(binding.name, compositePartName, StringComparison.OrdinalIgnoreCase); } // If no composite part specified, just matching the action is enough return true; } private void OnBtnClicked() { // Use manager API (we pass part name so manager can pick proper binding if needed) InputBindingManager.StartRebind(actionName, string.IsNullOrEmpty(compositePartName) ? null : compositePartName); } public async void ConfirmPrepared() { bool ok = await ConfirmPreparedAsync(); if (!ok) Debug.LogError("ConfirmPrepared: apply failed."); } private async Task ConfirmPreparedAsync() { try { var task = InputBindingManager.ConfirmApply(); return await task; } catch (Exception ex) { Debug.LogError(ex); return false; } } public void CancelPrepared() { InputBindingManager.DiscardPrepared(); // UpdateBindingText will be called automatically via OnApply event } private void UpdateBindingText() { var action = GetAction(); if (action == null) { bindKeyText.text = ""; if (targetImage != null) targetImage.sprite = null; return; } bindKeyText.text = GlyphService.GetDisplayNameFromInputAction(action, compositePartName); try { var deviceCat = InputDeviceWatcher.CurrentCategory; InputActionReference refr=default; // string controlPath = GlyphService.GetBindingControlPath(action, compositePartName, deviceCat); if ( GlyphService.TryGetUISpriteForActionPath(action,compositePartName, deviceCat, out Sprite sprite)) { if (targetImage != null) targetImage.sprite = sprite; } else { if (targetImage != null) targetImage.sprite = null; } } catch { if (targetImage != null) targetImage.sprite = null; } } }