336 lines
9.2 KiB
C#
336 lines
9.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace AlicizaX.UI
|
|
{
|
|
[DisallowMultipleComponent]
|
|
[AddComponentMenu("UX/UX Controller")]
|
|
public sealed class UXController : MonoBehaviour
|
|
{
|
|
[Serializable]
|
|
public sealed class ControllerDefinition
|
|
{
|
|
[SerializeField] private string _id = string.Empty;
|
|
[SerializeField] private string _name = "Controller";
|
|
[SerializeField] private int _length = 2;
|
|
[SerializeField] private string _description = string.Empty;
|
|
[NonSerialized] private int _selectedIndex = -1;
|
|
[NonSerialized] private UXController _owner;
|
|
|
|
public string Id => _id;
|
|
|
|
public string Name
|
|
{
|
|
get => _name;
|
|
set => _name = value;
|
|
}
|
|
|
|
public int Length
|
|
{
|
|
get => Mathf.Max(1, _length);
|
|
set => _length = Mathf.Max(1, value);
|
|
}
|
|
|
|
public string Description
|
|
{
|
|
get => _description;
|
|
set => _description = value;
|
|
}
|
|
|
|
public int SelectedIndex
|
|
{
|
|
get => _selectedIndex;
|
|
set
|
|
{
|
|
if (_owner == null)
|
|
{
|
|
SetSelectedIndexSilently(Mathf.Clamp(value, 0, Length - 1));
|
|
return;
|
|
}
|
|
|
|
_owner.SetControllerIndexInternal(this, value);
|
|
}
|
|
}
|
|
|
|
internal void EnsureId()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_id))
|
|
{
|
|
_id = Guid.NewGuid().ToString("N");
|
|
}
|
|
}
|
|
|
|
internal void SetOwner(UXController owner)
|
|
{
|
|
_owner = owner;
|
|
}
|
|
|
|
internal void SetSelectedIndexSilently(int selectedIndex)
|
|
{
|
|
_selectedIndex = selectedIndex;
|
|
}
|
|
}
|
|
|
|
[SerializeField] private List<ControllerDefinition> _controllers = new List<ControllerDefinition>();
|
|
[SerializeField] private List<UXBinding> _bindings = new List<UXBinding>();
|
|
|
|
private readonly Dictionary<string, int> _controllerIdMap = new Dictionary<string, int>();
|
|
private readonly Dictionary<string, int> _controllerNameMap = new Dictionary<string, int>();
|
|
|
|
public IReadOnlyList<ControllerDefinition> Controllers
|
|
{
|
|
get
|
|
{
|
|
EnsureInitialized();
|
|
return _controllers;
|
|
}
|
|
}
|
|
public IReadOnlyList<UXBinding> Bindings => _bindings;
|
|
public int ControllerCount => _controllers.Count;
|
|
|
|
public bool TryGetControllerById(string controllerId, out ControllerDefinition controller)
|
|
{
|
|
controller = null;
|
|
if (string.IsNullOrWhiteSpace(controllerId))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
EnsureInitialized();
|
|
if (_controllerIdMap.TryGetValue(controllerId, out int index))
|
|
{
|
|
controller = _controllers[index];
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool TryGetControllerByName(string controllerName, out ControllerDefinition controller)
|
|
{
|
|
controller = null;
|
|
if (string.IsNullOrWhiteSpace(controllerName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
EnsureInitialized();
|
|
if (_controllerNameMap.TryGetValue(controllerName, out int index))
|
|
{
|
|
controller = _controllers[index];
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public ControllerDefinition GetControllerAt(int index)
|
|
{
|
|
EnsureInitialized();
|
|
|
|
if (index < 0 || index >= _controllers.Count)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return _controllers[index];
|
|
}
|
|
|
|
public int GetControllerIndex(string controllerId)
|
|
{
|
|
return TryGetControllerById(controllerId, out ControllerDefinition controller)
|
|
? controller.SelectedIndex
|
|
: 0;
|
|
}
|
|
|
|
public bool SetControllerIndex(string controllerId, int selectedIndex)
|
|
{
|
|
if (!TryGetControllerById(controllerId, out ControllerDefinition controller))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return SetControllerIndexInternal(controller, selectedIndex);
|
|
}
|
|
|
|
public bool SetControllerIndexByName(string controllerName, int selectedIndex)
|
|
{
|
|
if (!TryGetControllerByName(controllerName, out ControllerDefinition controller))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return SetControllerIndexInternal(controller, selectedIndex);
|
|
}
|
|
|
|
public void ResetAllControllers()
|
|
{
|
|
EnsureInitialized();
|
|
|
|
for (int i = 0; i < _controllers.Count; i++)
|
|
{
|
|
SetControllerIndexInternal(_controllers[i], 0, true);
|
|
}
|
|
}
|
|
|
|
internal bool HasBinding(UXBinding binding)
|
|
{
|
|
return binding != null && _bindings.Contains(binding);
|
|
}
|
|
|
|
internal void RegisterBinding(UXBinding binding)
|
|
{
|
|
if (binding == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!_bindings.Contains(binding))
|
|
{
|
|
_bindings.Add(binding);
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
EditorUtility.SetDirty(this);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
internal void UnregisterBinding(UXBinding binding)
|
|
{
|
|
if (binding == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_bindings.Remove(binding);
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
EditorUtility.SetDirty(this);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private void Reset()
|
|
{
|
|
if (_controllers.Count == 0)
|
|
{
|
|
_controllers.Add(new ControllerDefinition());
|
|
}
|
|
|
|
RebuildMaps();
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
EnsureInitialized();
|
|
|
|
for (int i = 0; i < _bindings.Count; i++)
|
|
{
|
|
if (_bindings[i] != null)
|
|
{
|
|
_bindings[i].Initialize();
|
|
}
|
|
}
|
|
|
|
ResetAllControllers();
|
|
}
|
|
|
|
private void OnValidate()
|
|
{
|
|
RebuildMaps();
|
|
CleanupBindings();
|
|
}
|
|
|
|
private void EnsureInitialized()
|
|
{
|
|
if (_controllerIdMap.Count == 0 && _controllerNameMap.Count == 0)
|
|
{
|
|
RebuildMaps();
|
|
}
|
|
}
|
|
|
|
private void RebuildMaps()
|
|
{
|
|
_controllerIdMap.Clear();
|
|
_controllerNameMap.Clear();
|
|
|
|
var usedNames = new HashSet<string>(StringComparer.Ordinal);
|
|
|
|
for (int i = 0; i < _controllers.Count; i++)
|
|
{
|
|
ControllerDefinition controller = _controllers[i];
|
|
if (controller == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
controller.EnsureId();
|
|
controller.SetOwner(this);
|
|
controller.SetSelectedIndexSilently(Mathf.Clamp(controller.SelectedIndex, -1, controller.Length - 1));
|
|
|
|
if (string.IsNullOrWhiteSpace(controller.Name))
|
|
{
|
|
controller.Name = $"Controller{i + 1}";
|
|
}
|
|
|
|
if (!usedNames.Add(controller.Name))
|
|
{
|
|
controller.Name = $"{controller.Name}_{i + 1}";
|
|
usedNames.Add(controller.Name);
|
|
}
|
|
|
|
_controllerIdMap[controller.Id] = i;
|
|
_controllerNameMap[controller.Name] = i;
|
|
}
|
|
}
|
|
|
|
private void CleanupBindings()
|
|
{
|
|
for (int i = _bindings.Count - 1; i >= 0; i--)
|
|
{
|
|
if (_bindings[i] == null)
|
|
{
|
|
_bindings.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool SetControllerIndexInternal(ControllerDefinition controller, int selectedIndex, bool force = false)
|
|
{
|
|
if (controller == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
selectedIndex = Mathf.Clamp(selectedIndex, 0, controller.Length - 1);
|
|
if (!force && controller.SelectedIndex == selectedIndex)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
controller.SetSelectedIndexSilently(selectedIndex);
|
|
NotifyBindings(controller.Id, selectedIndex);
|
|
return true;
|
|
}
|
|
|
|
private void NotifyBindings(string controllerId, int selectedIndex)
|
|
{
|
|
for (int i = 0; i < _bindings.Count; i++)
|
|
{
|
|
UXBinding binding = _bindings[i];
|
|
if (binding != null)
|
|
{
|
|
binding.OnControllerChanged(controllerId, selectedIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|