AlicizaX/Client/Assets/InputGlyph/TestRebindScript.cs
2026-03-09 20:38:15 +08:00

201 lines
6.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 IDisposable prepareSub;
private IDisposable applySub;
private IDisposable rebindEndSub;
private IDisposable conflictSub;
private void Start()
{
if (btn != null) btn.onClick.AddListener(OnBtnClicked);
InputDeviceWatcher.OnDeviceChanged += OnDeviceChanged;
UpdateBindingText();
if (InputBindingManager.Instance != null)
{
// Subscribe to prepare events - already filtered by IsTargetContext
prepareSub = InputBindingManager.Instance.OnRebindPrepare.Subscribe(ctx =>
{
if (IsTargetContext(ctx))
{
var disp = ctx.overridePath == InputBindingManager.NULL_BINDING ? "<Cleared>" : ctx.overridePath;
bindKeyText.text = disp;
if (autoConfirm) _ = ConfirmPreparedAsync();
}
});
// Subscribe to apply events - only update if this instance's binding was applied or discarded
applySub = InputBindingManager.Instance.OnApply.Subscribe(evt =>
{
var (success, appliedContexts) = evt;
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;
}
}
}
});
// Subscribe to rebind end events - only update if this instance's binding ended
rebindEndSub = InputBindingManager.Instance.OnRebindEnd.Subscribe(evt =>
{
var (success, context) = evt;
if (IsTargetContext(context))
{
UpdateBindingText();
}
});
// Subscribe to conflict events - update if this instance is involved in conflict
conflictSub = InputBindingManager.Instance.OnRebindConflict.Subscribe(evt =>
{
var (prepared, conflict) = evt;
// Update if either the prepared or conflict context matches this instance
if (IsTargetContext(prepared) || IsTargetContext(conflict))
{
UpdateBindingText();
}
});
}
}
private void OnDisable()
{
if (btn != null) btn.onClick.RemoveListener(OnBtnClicked);
InputDeviceWatcher.OnDeviceChanged -= OnDeviceChanged;
prepareSub?.Dispose();
applySub?.Dispose();
rebindEndSub?.Dispose();
conflictSub?.Dispose();
}
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<bool> 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 = "<no action>";
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;
}
}
}