172 lines
5.6 KiB
C#
172 lines
5.6 KiB
C#
// TestRebindScript.cs
|
||
using System;
|
||
using System.Threading.Tasks;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.InputSystem;
|
||
using InputRemapper;
|
||
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 int targetBindingIndex = -1;
|
||
private IDisposable prepareSub;
|
||
private IDisposable applySub;
|
||
private IDisposable rebindEndSub;
|
||
|
||
private void Start()
|
||
{
|
||
if (btn != null) btn.onClick.AddListener(OnBtnClicked);
|
||
InputDeviceWatcher.OnDeviceChanged += OnDeviceChanged;
|
||
|
||
ResolveBindingIndex();
|
||
UpdateBindingText();
|
||
|
||
if (InputBindingManager.Instance != null)
|
||
{
|
||
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();
|
||
}
|
||
});
|
||
|
||
applySub = InputBindingManager.Instance.OnApply.Subscribe(_ => UpdateBindingText());
|
||
rebindEndSub = InputBindingManager.Instance.OnRebindEnd.Subscribe(_ => UpdateBindingText());
|
||
}
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
if (btn != null) btn.onClick.RemoveListener(OnBtnClicked);
|
||
InputDeviceWatcher.OnDeviceChanged -= OnDeviceChanged;
|
||
prepareSub?.Dispose();
|
||
applySub?.Dispose();
|
||
rebindEndSub?.Dispose();
|
||
}
|
||
|
||
private void OnDeviceChanged(InputDeviceWatcher.InputDeviceCategory _)
|
||
{
|
||
UpdateBindingText();
|
||
}
|
||
|
||
private InputAction GetAction()
|
||
{
|
||
return InputBindingManager.Action(actionName);
|
||
}
|
||
|
||
private void ResolveBindingIndex()
|
||
{
|
||
var action = GetAction();
|
||
if (action == null)
|
||
{
|
||
Debug.LogError($"TestRebindScript: Action not found ('{actionName}').");
|
||
targetBindingIndex = -1;
|
||
return;
|
||
}
|
||
|
||
// Ask manager to choose best index for keyboard and optional composite part
|
||
targetBindingIndex = InputBindingManager.Instance.FindBestBindingIndexForKeyboard(action, string.IsNullOrEmpty(compositePartName) ? null : compositePartName);
|
||
}
|
||
|
||
private bool IsTargetContext(InputRemapper.InputBindingManager.RebindContext ctx)
|
||
{
|
||
if (ctx == null || ctx.action == null) return false;
|
||
var action = GetAction();
|
||
if (action == null) return false;
|
||
return ctx.action == action && ctx.bindingIndex == targetBindingIndex;
|
||
}
|
||
|
||
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();
|
||
if (task == null) return false;
|
||
return await task;
|
||
}
|
||
catch (Exception ex) { Debug.LogError(ex); return false; }
|
||
}
|
||
|
||
public void CancelPrepared()
|
||
{
|
||
InputBindingManager.DiscardPrepared();
|
||
UpdateBindingText();
|
||
}
|
||
|
||
private void UpdateBindingText()
|
||
{
|
||
var action = GetAction();
|
||
if (action == null)
|
||
{
|
||
bindKeyText.text = "<no action>";
|
||
if (targetImage != null) targetImage.sprite = null;
|
||
return;
|
||
}
|
||
|
||
// Ensure binding index is refreshed (action/bindings may change)
|
||
if (targetBindingIndex < 0 || targetBindingIndex >= action.bindings.Count)
|
||
ResolveBindingIndex();
|
||
|
||
if (targetBindingIndex < 0)
|
||
{
|
||
bindKeyText.text = "<no binding>";
|
||
if (targetImage != null) targetImage.sprite = null;
|
||
return;
|
||
}
|
||
|
||
string disp = action.GetBindingDisplayString(targetBindingIndex);
|
||
bindKeyText.text = string.IsNullOrEmpty(disp) ? "<unbound>" : disp;
|
||
|
||
// **Important**: use GlyphService with InputAction and device category (not manager-internal path)
|
||
try
|
||
{
|
||
var deviceCat = InputDeviceWatcher.CurrentCategory;
|
||
string controlPath = GlyphService.GetBindingControlPath(action, deviceCat);
|
||
if (!string.IsNullOrEmpty(controlPath) && GlyphService.TryGetUISpriteForActionPath(controlPath, deviceCat, out Sprite sprite))
|
||
{
|
||
if (targetImage != null) targetImage.sprite = sprite;
|
||
}
|
||
else
|
||
{
|
||
if (targetImage != null) targetImage.sprite = null;
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
if (targetImage != null) targetImage.sprite = null;
|
||
}
|
||
}
|
||
}
|