2025-12-01 16:44:19 +08:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using UnityEngine;
|
2026-03-21 19:57:49 +08:00
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
using UnityEditor;
|
|
|
|
|
#endif
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-23 14:45:12 +08:00
|
|
|
namespace UnityEngine.UI
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
|
|
|
|
[DisallowMultipleComponent]
|
2026-02-26 17:14:47 +08:00
|
|
|
[AddComponentMenu("UX/UX Controller")]
|
2025-12-01 16:44:19 +08:00
|
|
|
public sealed class UXController : MonoBehaviour
|
|
|
|
|
{
|
|
|
|
|
[Serializable]
|
2026-03-21 19:57:49 +08:00
|
|
|
public sealed class ControllerDefinition
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
[SerializeField] private string _id = string.Empty;
|
2026-02-26 17:14:47 +08:00
|
|
|
[SerializeField] private string _name = "Controller";
|
|
|
|
|
[SerializeField] private int _length = 2;
|
2026-04-28 20:52:06 +08:00
|
|
|
[SerializeField] private int _defaultIndex;
|
2026-03-21 19:57:49 +08:00
|
|
|
[SerializeField] private string _description = string.Empty;
|
|
|
|
|
[NonSerialized] private int _selectedIndex = -1;
|
2026-03-23 14:04:26 +08:00
|
|
|
[NonSerialized] private UXController _owner;
|
2026-03-21 19:57:49 +08:00
|
|
|
|
|
|
|
|
public string Id => _id;
|
2026-02-26 17:14:47 +08:00
|
|
|
|
|
|
|
|
public string Name
|
|
|
|
|
{
|
|
|
|
|
get => _name;
|
|
|
|
|
set => _name = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Length
|
|
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
get => Mathf.Max(1, _length);
|
2026-02-26 17:14:47 +08:00
|
|
|
set => _length = Mathf.Max(1, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Description
|
|
|
|
|
{
|
|
|
|
|
get => _description;
|
|
|
|
|
set => _description = value;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-28 20:52:06 +08:00
|
|
|
public int DefaultIndex
|
|
|
|
|
{
|
|
|
|
|
get => Mathf.Clamp(_defaultIndex, 0, Length - 1);
|
|
|
|
|
set => _defaultIndex = Mathf.Clamp(value, 0, Length - 1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
public int SelectedIndex
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
get => _selectedIndex;
|
2026-03-23 14:04:26 +08:00
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_owner == null)
|
|
|
|
|
{
|
|
|
|
|
SetSelectedIndexSilently(Mathf.Clamp(value, 0, Length - 1));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_owner.SetControllerIndexInternal(this, value);
|
|
|
|
|
}
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
internal void EnsureId()
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(_id))
|
|
|
|
|
{
|
|
|
|
|
_id = Guid.NewGuid().ToString("N");
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-23 14:04:26 +08:00
|
|
|
|
|
|
|
|
internal void SetOwner(UXController owner)
|
|
|
|
|
{
|
|
|
|
|
_owner = owner;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void SetSelectedIndexSilently(int selectedIndex)
|
|
|
|
|
{
|
|
|
|
|
_selectedIndex = selectedIndex;
|
|
|
|
|
}
|
2026-03-21 19:57:49 +08:00
|
|
|
}
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
[SerializeField] private List<ControllerDefinition> _controllers = new List<ControllerDefinition>();
|
|
|
|
|
[SerializeField] private List<UXBinding> _bindings = new List<UXBinding>();
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
private readonly Dictionary<string, int> _controllerIdMap = new Dictionary<string, int>();
|
|
|
|
|
private readonly Dictionary<string, int> _controllerNameMap = new Dictionary<string, int>();
|
2026-04-28 20:52:06 +08:00
|
|
|
private RuntimeBindingEntry[][] _runtimeEntriesByController = Array.Empty<RuntimeBindingEntry[]>();
|
|
|
|
|
private int[] _runtimeEntryCounts = Array.Empty<int>();
|
|
|
|
|
private bool _runtimeReady;
|
|
|
|
|
|
|
|
|
|
private struct RuntimeBindingEntry
|
|
|
|
|
{
|
|
|
|
|
public UXBinding Binding;
|
|
|
|
|
public int EntryIndex;
|
|
|
|
|
}
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-23 14:04:26 +08:00
|
|
|
public IReadOnlyList<ControllerDefinition> Controllers
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
EnsureInitialized();
|
|
|
|
|
return _controllers;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-23 14:45:12 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
public IReadOnlyList<UXBinding> Bindings => _bindings;
|
2026-02-26 17:14:47 +08:00
|
|
|
public int ControllerCount => _controllers.Count;
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
public bool TryGetControllerById(string controllerId, out ControllerDefinition controller)
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
controller = null;
|
|
|
|
|
if (string.IsNullOrWhiteSpace(controllerId))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-26 17:14:47 +08:00
|
|
|
EnsureInitialized();
|
2026-03-21 19:57:49 +08:00
|
|
|
if (_controllerIdMap.TryGetValue(controllerId, out int index))
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
controller = _controllers[index];
|
|
|
|
|
return true;
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
return false;
|
2025-12-01 16:44:19 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 20:52:06 +08:00
|
|
|
internal bool TryGetControllerSlot(string controllerId, out int slot)
|
|
|
|
|
{
|
|
|
|
|
slot = -1;
|
|
|
|
|
if (string.IsNullOrEmpty(controllerId))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EnsureInitialized();
|
|
|
|
|
return _controllerIdMap.TryGetValue(controllerId, out slot);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
public bool TryGetControllerByName(string controllerName, out ControllerDefinition controller)
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
controller = null;
|
|
|
|
|
if (string.IsNullOrWhiteSpace(controllerName))
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
return false;
|
2025-12-01 16:44:19 +08:00
|
|
|
}
|
|
|
|
|
|
2026-02-26 17:14:47 +08:00
|
|
|
EnsureInitialized();
|
2026-03-21 19:57:49 +08:00
|
|
|
if (_controllerNameMap.TryGetValue(controllerName, out int index))
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
controller = _controllers[index];
|
|
|
|
|
return true;
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
2026-02-26 17:14:47 +08:00
|
|
|
|
2026-03-23 14:45:12 +08:00
|
|
|
public ControllerDefinition GetControllerByName(string controllerName)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(controllerName))
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EnsureInitialized();
|
|
|
|
|
if (_controllerNameMap.TryGetValue(controllerName, out int index))
|
|
|
|
|
{
|
|
|
|
|
return _controllers[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
public ControllerDefinition GetControllerAt(int index)
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-23 14:04:26 +08:00
|
|
|
EnsureInitialized();
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
if (index < 0 || index >= _controllers.Count)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
return _controllers[index];
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
public int GetControllerIndex(string controllerId)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
return TryGetControllerById(controllerId, out ControllerDefinition controller)
|
|
|
|
|
? controller.SelectedIndex
|
|
|
|
|
: 0;
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
public bool SetControllerIndex(string controllerId, int selectedIndex)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
if (!TryGetControllerById(controllerId, out ControllerDefinition controller))
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
return false;
|
2025-12-01 16:44:19 +08:00
|
|
|
}
|
2026-03-21 19:57:49 +08:00
|
|
|
|
|
|
|
|
return SetControllerIndexInternal(controller, selectedIndex);
|
2025-12-01 16:44:19 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
public bool SetControllerIndexByName(string controllerName, int selectedIndex)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
if (!TryGetControllerByName(controllerName, out ControllerDefinition controller))
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
return false;
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
return SetControllerIndexInternal(controller, selectedIndex);
|
|
|
|
|
}
|
2026-02-26 17:14:47 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
public void ResetAllControllers()
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
EnsureInitialized();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < _controllers.Count; i++)
|
|
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
ControllerDefinition controller = _controllers[i];
|
|
|
|
|
if (controller != null)
|
|
|
|
|
{
|
|
|
|
|
SetControllerIndexInternal(controller, controller.DefaultIndex, true);
|
|
|
|
|
}
|
2026-03-21 19:57:49 +08:00
|
|
|
}
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
internal bool HasBinding(UXBinding binding)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
return binding != null && _bindings.Contains(binding);
|
2025-12-01 16:44:19 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
internal void RegisterBinding(UXBinding binding)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
if (binding == null)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!_bindings.Contains(binding))
|
|
|
|
|
{
|
|
|
|
|
_bindings.Add(binding);
|
2026-04-28 20:52:06 +08:00
|
|
|
_runtimeReady = false;
|
|
|
|
|
if (Application.isPlaying)
|
|
|
|
|
{
|
|
|
|
|
RebuildRuntimeEntries();
|
|
|
|
|
ApplyCurrentStateToBinding(binding);
|
|
|
|
|
}
|
2026-03-21 19:57:49 +08:00
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
if (!Application.isPlaying)
|
|
|
|
|
{
|
|
|
|
|
EditorUtility.SetDirty(this);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
internal void UnregisterBinding(UXBinding binding)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
if (binding == null)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
return;
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
2026-03-21 19:57:49 +08:00
|
|
|
|
|
|
|
|
_bindings.Remove(binding);
|
2026-04-28 20:52:06 +08:00
|
|
|
_runtimeReady = false;
|
2026-03-21 19:57:49 +08:00
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
if (!Application.isPlaying)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
EditorUtility.SetDirty(this);
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
2026-03-21 19:57:49 +08:00
|
|
|
#endif
|
|
|
|
|
}
|
2026-02-26 17:14:47 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
private void Reset()
|
|
|
|
|
{
|
|
|
|
|
if (_controllers.Count == 0)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
_controllers.Add(new ControllerDefinition());
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
RebuildMaps();
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
private void Awake()
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
|
|
|
|
EnsureInitialized();
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
for (int i = 0; i < _bindings.Count; i++)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
if (_bindings[i] != null)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
_bindings[i].RebuildRuntime(this);
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-28 20:52:06 +08:00
|
|
|
RebuildRuntimeEntries();
|
2026-03-21 19:57:49 +08:00
|
|
|
ResetAllControllers();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnValidate()
|
|
|
|
|
{
|
|
|
|
|
RebuildMaps();
|
|
|
|
|
CleanupBindings();
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
private void EnsureInitialized()
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
if (_controllerIdMap.Count == 0 && _controllerNameMap.Count == 0)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
RebuildMaps();
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
private void RebuildMaps()
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
_controllerIdMap.Clear();
|
|
|
|
|
_controllerNameMap.Clear();
|
|
|
|
|
|
2026-04-28 20:52:06 +08:00
|
|
|
#if UNITY_EDITOR
|
2026-03-21 19:57:49 +08:00
|
|
|
var usedNames = new HashSet<string>(StringComparer.Ordinal);
|
2026-04-28 20:52:06 +08:00
|
|
|
#endif
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
for (int i = 0; i < _controllers.Count; i++)
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
ControllerDefinition controller = _controllers[i];
|
|
|
|
|
if (controller == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
controller.EnsureId();
|
2026-03-23 14:04:26 +08:00
|
|
|
controller.SetOwner(this);
|
|
|
|
|
controller.SetSelectedIndexSilently(Mathf.Clamp(controller.SelectedIndex, -1, controller.Length - 1));
|
2026-04-28 20:52:06 +08:00
|
|
|
controller.DefaultIndex = controller.DefaultIndex;
|
2026-02-26 17:14:47 +08:00
|
|
|
|
2026-04-28 20:52:06 +08:00
|
|
|
#if UNITY_EDITOR
|
2026-03-21 19:57:49 +08:00
|
|
|
if (string.IsNullOrWhiteSpace(controller.Name))
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
controller.Name = $"Controller{i + 1}";
|
2025-12-01 16:44:19 +08:00
|
|
|
}
|
2026-03-21 19:57:49 +08:00
|
|
|
|
|
|
|
|
if (!usedNames.Add(controller.Name))
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
controller.Name = $"{controller.Name}_{i + 1}";
|
|
|
|
|
usedNames.Add(controller.Name);
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
2026-04-28 20:52:06 +08:00
|
|
|
#endif
|
2026-03-21 19:57:49 +08:00
|
|
|
|
|
|
|
|
_controllerIdMap[controller.Id] = i;
|
|
|
|
|
_controllerNameMap[controller.Name] = i;
|
2025-12-01 16:44:19 +08:00
|
|
|
}
|
2026-04-28 20:52:06 +08:00
|
|
|
|
|
|
|
|
_runtimeReady = false;
|
2026-03-21 19:57:49 +08:00
|
|
|
}
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
private void CleanupBindings()
|
|
|
|
|
{
|
|
|
|
|
for (int i = _bindings.Count - 1; i >= 0; i--)
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
if (_bindings[i] == null)
|
2025-12-01 16:44:19 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
_bindings.RemoveAt(i);
|
2025-12-01 16:44:19 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-21 19:57:49 +08:00
|
|
|
}
|
2025-12-01 16:44:19 +08:00
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
private bool SetControllerIndexInternal(ControllerDefinition controller, int selectedIndex, bool force = false)
|
|
|
|
|
{
|
|
|
|
|
if (controller == null)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
return false;
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 19:57:49 +08:00
|
|
|
selectedIndex = Mathf.Clamp(selectedIndex, 0, controller.Length - 1);
|
|
|
|
|
if (!force && controller.SelectedIndex == selectedIndex)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-03-21 19:57:49 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-23 14:04:26 +08:00
|
|
|
controller.SetSelectedIndexSilently(selectedIndex);
|
2026-04-28 20:52:06 +08:00
|
|
|
if (TryGetControllerSlot(controller.Id, out int slot))
|
|
|
|
|
{
|
|
|
|
|
NotifyBindings(slot, selectedIndex);
|
|
|
|
|
}
|
2026-03-21 19:57:49 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-28 20:52:06 +08:00
|
|
|
private void NotifyBindings(int controllerSlot, int selectedIndex)
|
2026-03-21 19:57:49 +08:00
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
if (!_runtimeReady)
|
2026-03-21 19:57:49 +08:00
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
RebuildRuntimeEntries();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((uint)controllerSlot >= (uint)_runtimeEntriesByController.Length)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RuntimeBindingEntry[] entries = _runtimeEntriesByController[controllerSlot];
|
|
|
|
|
int count = _runtimeEntryCounts[controllerSlot];
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
UXBinding binding = entries[i].Binding;
|
2026-03-21 19:57:49 +08:00
|
|
|
if (binding != null)
|
2026-02-26 17:14:47 +08:00
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
binding.ApplyRuntimeEntry(entries[i].EntryIndex, selectedIndex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RebuildRuntimeEntries()
|
|
|
|
|
{
|
|
|
|
|
int controllerCount = _controllers.Count;
|
|
|
|
|
if (_runtimeEntriesByController.Length != controllerCount)
|
|
|
|
|
{
|
|
|
|
|
_runtimeEntriesByController = new RuntimeBindingEntry[controllerCount][];
|
|
|
|
|
_runtimeEntryCounts = new int[controllerCount];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < controllerCount; i++)
|
|
|
|
|
{
|
|
|
|
|
_runtimeEntryCounts[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int bindingIndex = 0; bindingIndex < _bindings.Count; bindingIndex++)
|
|
|
|
|
{
|
|
|
|
|
UXBinding binding = _bindings[bindingIndex];
|
|
|
|
|
if (binding == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
binding.RebuildRuntime(this);
|
|
|
|
|
|
|
|
|
|
for (int entryIndex = 0; entryIndex < binding.RuntimeEntryCount; entryIndex++)
|
|
|
|
|
{
|
|
|
|
|
int controllerSlot = binding.GetRuntimeControllerSlot(entryIndex);
|
|
|
|
|
if ((uint)controllerSlot >= (uint)controllerCount || !binding.IsRuntimeEntrySupported(entryIndex))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int nextIndex = _runtimeEntryCounts[controllerSlot];
|
|
|
|
|
RuntimeBindingEntry[] entries = _runtimeEntriesByController[controllerSlot];
|
|
|
|
|
if (entries == null || nextIndex >= entries.Length)
|
|
|
|
|
{
|
|
|
|
|
int nextLength = entries == null ? 4 : entries.Length << 1;
|
|
|
|
|
RuntimeBindingEntry[] nextEntries = new RuntimeBindingEntry[nextLength];
|
|
|
|
|
if (entries != null)
|
|
|
|
|
{
|
|
|
|
|
Array.Copy(entries, nextEntries, entries.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entries = nextEntries;
|
|
|
|
|
_runtimeEntriesByController[controllerSlot] = entries;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entries[nextIndex].Binding = binding;
|
|
|
|
|
entries[nextIndex].EntryIndex = entryIndex;
|
|
|
|
|
_runtimeEntryCounts[controllerSlot] = nextIndex + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_runtimeReady = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ApplyCurrentStateToBinding(UXBinding binding)
|
|
|
|
|
{
|
|
|
|
|
if (binding == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int entryIndex = 0; entryIndex < binding.RuntimeEntryCount; entryIndex++)
|
|
|
|
|
{
|
|
|
|
|
int controllerSlot = binding.GetRuntimeControllerSlot(entryIndex);
|
|
|
|
|
if ((uint)controllerSlot >= (uint)_controllers.Count)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ControllerDefinition controller = _controllers[controllerSlot];
|
|
|
|
|
if (controller != null)
|
|
|
|
|
{
|
|
|
|
|
binding.ApplyRuntimeEntry(entryIndex, controller.SelectedIndex);
|
2026-02-26 17:14:47 +08:00
|
|
|
}
|
|
|
|
|
}
|
2025-12-01 16:44:19 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|