2026-03-21 19:57:49 +08:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
2026-03-23 14:45:12 +08:00
|
|
|
namespace UnityEngine.UI
|
2026-03-21 19:57:49 +08:00
|
|
|
{
|
|
|
|
|
public enum UXBindingFallbackMode
|
|
|
|
|
{
|
|
|
|
|
KeepCurrent = 0,
|
|
|
|
|
RestoreCapturedDefault = 1,
|
|
|
|
|
UseCustomValue = 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum UXBindingValueKind
|
|
|
|
|
{
|
|
|
|
|
Boolean = 0,
|
|
|
|
|
Float = 1,
|
|
|
|
|
String = 2,
|
|
|
|
|
Color = 3,
|
|
|
|
|
Vector2 = 4,
|
|
|
|
|
Vector3 = 5,
|
|
|
|
|
ObjectReference = 6
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum UXBindingProperty
|
|
|
|
|
{
|
|
|
|
|
GameObjectActive = 0,
|
|
|
|
|
CanvasGroupAlpha = 1,
|
|
|
|
|
CanvasGroupInteractable = 2,
|
|
|
|
|
CanvasGroupBlocksRaycasts = 3,
|
|
|
|
|
GraphicColor = 4,
|
|
|
|
|
GraphicMaterial = 5,
|
|
|
|
|
ImageSprite = 6,
|
|
|
|
|
TextContent = 7,
|
|
|
|
|
TextColor = 8,
|
|
|
|
|
RectTransformAnchoredPosition = 9,
|
|
|
|
|
TransformLocalScale = 10,
|
|
|
|
|
TransformLocalEulerAngles = 11
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public sealed class UXBindingValue
|
|
|
|
|
{
|
|
|
|
|
[SerializeField] private bool _boolValue;
|
|
|
|
|
[SerializeField] private float _floatValue;
|
|
|
|
|
[SerializeField] private string _stringValue = string.Empty;
|
|
|
|
|
[SerializeField] private Color _colorValue = Color.white;
|
|
|
|
|
[SerializeField] private Vector2 _vector2Value;
|
|
|
|
|
[SerializeField] private Vector3 _vector3Value;
|
|
|
|
|
[SerializeField] private UnityEngine.Object _objectValue;
|
|
|
|
|
|
|
|
|
|
public bool BoolValue
|
|
|
|
|
{
|
|
|
|
|
get => _boolValue;
|
|
|
|
|
set => _boolValue = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public float FloatValue
|
|
|
|
|
{
|
|
|
|
|
get => _floatValue;
|
|
|
|
|
set => _floatValue = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string StringValue
|
|
|
|
|
{
|
|
|
|
|
get => _stringValue;
|
|
|
|
|
set => _stringValue = value ?? string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Color ColorValue
|
|
|
|
|
{
|
|
|
|
|
get => _colorValue;
|
|
|
|
|
set => _colorValue = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Vector2 Vector2Value
|
|
|
|
|
{
|
|
|
|
|
get => _vector2Value;
|
|
|
|
|
set => _vector2Value = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Vector3 Vector3Value
|
|
|
|
|
{
|
|
|
|
|
get => _vector3Value;
|
|
|
|
|
set => _vector3Value = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public UnityEngine.Object ObjectValue
|
|
|
|
|
{
|
|
|
|
|
get => _objectValue;
|
|
|
|
|
set => _objectValue = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void CopyFrom(UXBindingValue other)
|
|
|
|
|
{
|
|
|
|
|
if (other == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_boolValue = other._boolValue;
|
|
|
|
|
_floatValue = other._floatValue;
|
|
|
|
|
_stringValue = other._stringValue;
|
|
|
|
|
_colorValue = other._colorValue;
|
|
|
|
|
_vector2Value = other._vector2Value;
|
|
|
|
|
_vector3Value = other._vector3Value;
|
|
|
|
|
_objectValue = other._objectValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[DisallowMultipleComponent]
|
|
|
|
|
[AddComponentMenu("UX/UX Binding")]
|
|
|
|
|
public sealed class UXBinding : MonoBehaviour
|
|
|
|
|
{
|
|
|
|
|
[Serializable]
|
|
|
|
|
public sealed class BindingEntry
|
|
|
|
|
{
|
|
|
|
|
[SerializeField] private string _controllerId = string.Empty;
|
|
|
|
|
[SerializeField] private int _controllerIndex;
|
|
|
|
|
[SerializeField] private UXBindingProperty _property = UXBindingProperty.GameObjectActive;
|
|
|
|
|
[SerializeField] private UXBindingValue _value = new UXBindingValue();
|
|
|
|
|
[SerializeField] private UXBindingFallbackMode _fallbackMode = UXBindingFallbackMode.RestoreCapturedDefault;
|
|
|
|
|
[SerializeField] private UXBindingValue _fallbackValue = new UXBindingValue();
|
|
|
|
|
[HideInInspector] [SerializeField] private UXBindingValue _capturedDefault = new UXBindingValue();
|
|
|
|
|
[HideInInspector] [SerializeField] private bool _hasCapturedDefault;
|
|
|
|
|
[HideInInspector] [SerializeField] private UXBindingProperty _capturedProperty = UXBindingProperty.GameObjectActive;
|
|
|
|
|
|
|
|
|
|
public string ControllerId
|
|
|
|
|
{
|
|
|
|
|
get => _controllerId;
|
|
|
|
|
set => _controllerId = value ?? string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int ControllerIndex
|
|
|
|
|
{
|
|
|
|
|
get => Mathf.Max(0, _controllerIndex);
|
|
|
|
|
set => _controllerIndex = Mathf.Max(0, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public UXBindingProperty Property
|
|
|
|
|
{
|
|
|
|
|
get => _property;
|
|
|
|
|
set => _property = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public UXBindingValue Value => _value;
|
|
|
|
|
|
|
|
|
|
public UXBindingFallbackMode FallbackMode
|
|
|
|
|
{
|
|
|
|
|
get => _fallbackMode;
|
|
|
|
|
set => _fallbackMode = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public UXBindingValue FallbackValue => _fallbackValue;
|
|
|
|
|
public bool HasCapturedDefault => _hasCapturedDefault;
|
|
|
|
|
|
|
|
|
|
internal void Normalize()
|
|
|
|
|
{
|
|
|
|
|
if (_property != UXBindingProperty.GameObjectActive)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_fallbackMode == UXBindingFallbackMode.RestoreCapturedDefault)
|
|
|
|
|
{
|
|
|
|
|
_fallbackMode = UXBindingFallbackMode.UseCustomValue;
|
|
|
|
|
_fallbackValue.BoolValue = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void CaptureDefault(GameObject target)
|
|
|
|
|
{
|
|
|
|
|
if (target == null || !UXBindingPropertyUtility.IsSupported(target, _property))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UXBindingPropertyUtility.CaptureValue(target, _property, _capturedDefault);
|
|
|
|
|
_capturedProperty = _property;
|
|
|
|
|
_hasCapturedDefault = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void CaptureCurrentAsValue(GameObject target)
|
|
|
|
|
{
|
|
|
|
|
if (target == null || !UXBindingPropertyUtility.IsSupported(target, _property))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UXBindingPropertyUtility.CaptureValue(target, _property, _value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void CaptureCurrentAsFallback(GameObject target)
|
|
|
|
|
{
|
|
|
|
|
if (target == null || !UXBindingPropertyUtility.IsSupported(target, _property))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UXBindingPropertyUtility.CaptureValue(target, _property, _fallbackValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void ResetToCapturedDefault(GameObject target)
|
|
|
|
|
{
|
|
|
|
|
if (target == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!_hasCapturedDefault || _capturedProperty != _property)
|
|
|
|
|
{
|
|
|
|
|
CaptureDefault(target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UXBindingPropertyUtility.ApplyValue(target, _property, _capturedDefault);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void Apply(GameObject target, string controllerId, int selectedIndex)
|
|
|
|
|
{
|
|
|
|
|
if (target == null || !string.Equals(_controllerId, controllerId, StringComparison.Ordinal))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!_hasCapturedDefault || _capturedProperty != _property)
|
|
|
|
|
{
|
|
|
|
|
CaptureDefault(target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!UXBindingPropertyUtility.IsSupported(target, _property))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedIndex == _controllerIndex)
|
|
|
|
|
{
|
|
|
|
|
UXBindingPropertyUtility.ApplyValue(target, _property, _value);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (_fallbackMode)
|
|
|
|
|
{
|
|
|
|
|
case UXBindingFallbackMode.KeepCurrent:
|
|
|
|
|
return;
|
|
|
|
|
case UXBindingFallbackMode.RestoreCapturedDefault:
|
|
|
|
|
if (_hasCapturedDefault)
|
|
|
|
|
{
|
|
|
|
|
UXBindingPropertyUtility.ApplyValue(target, _property, _capturedDefault);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
case UXBindingFallbackMode.UseCustomValue:
|
|
|
|
|
UXBindingPropertyUtility.ApplyValue(target, _property, _fallbackValue);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[SerializeField] private UXController _controller;
|
|
|
|
|
[SerializeField] private List<BindingEntry> _entries = new List<BindingEntry>();
|
|
|
|
|
|
|
|
|
|
private bool _initialized;
|
|
|
|
|
|
|
|
|
|
public UXController Controller => _controller;
|
|
|
|
|
public List<BindingEntry> Entries => _entries;
|
|
|
|
|
|
|
|
|
|
public void Initialize()
|
|
|
|
|
{
|
|
|
|
|
if (_initialized)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_initialized = true;
|
|
|
|
|
NormalizeEntries();
|
|
|
|
|
EnsureControllerReference();
|
|
|
|
|
RegisterToController();
|
|
|
|
|
CaptureDefaults();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetController(UXController controller)
|
|
|
|
|
{
|
|
|
|
|
if (_controller == controller)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_controller != null)
|
|
|
|
|
{
|
|
|
|
|
_controller.UnregisterBinding(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_controller = controller;
|
|
|
|
|
RegisterToController();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void CaptureDefaults()
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < _entries.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (_entries[i] != null)
|
|
|
|
|
{
|
|
|
|
|
_entries[i].CaptureDefault(gameObject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ResetToDefaults()
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < _entries.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (_entries[i] != null)
|
|
|
|
|
{
|
|
|
|
|
_entries[i].ResetToCapturedDefault(gameObject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void PreviewEntry(int entryIndex)
|
|
|
|
|
{
|
|
|
|
|
if (_controller == null || entryIndex < 0 || entryIndex >= _entries.Count)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BindingEntry entry = _entries[entryIndex];
|
|
|
|
|
if (entry == null || string.IsNullOrWhiteSpace(entry.ControllerId))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_controller.SetControllerIndex(entry.ControllerId, entry.ControllerIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void CaptureEntryValue(int entryIndex)
|
|
|
|
|
{
|
|
|
|
|
if (entryIndex < 0 || entryIndex >= _entries.Count || _entries[entryIndex] == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_entries[entryIndex].CaptureCurrentAsValue(gameObject);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void CaptureEntryFallbackValue(int entryIndex)
|
|
|
|
|
{
|
|
|
|
|
if (entryIndex < 0 || entryIndex >= _entries.Count || _entries[entryIndex] == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_entries[entryIndex].CaptureCurrentAsFallback(gameObject);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void OnControllerChanged(string controllerId, int selectedIndex)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < _entries.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
BindingEntry entry = _entries[i];
|
|
|
|
|
if (entry != null)
|
|
|
|
|
{
|
|
|
|
|
entry.Apply(gameObject, controllerId, selectedIndex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Reset()
|
|
|
|
|
{
|
|
|
|
|
EnsureControllerReference();
|
|
|
|
|
RegisterToController();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Awake()
|
|
|
|
|
{
|
|
|
|
|
Initialize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnValidate()
|
|
|
|
|
{
|
|
|
|
|
NormalizeEntries();
|
|
|
|
|
EnsureControllerReference();
|
|
|
|
|
RegisterToController();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnDestroy()
|
|
|
|
|
{
|
|
|
|
|
if (_controller != null)
|
|
|
|
|
{
|
|
|
|
|
_controller.UnregisterBinding(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void EnsureControllerReference()
|
|
|
|
|
{
|
|
|
|
|
if (_controller == null)
|
|
|
|
|
{
|
|
|
|
|
_controller = GetComponentInParent<UXController>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RegisterToController()
|
|
|
|
|
{
|
|
|
|
|
if (_controller != null)
|
|
|
|
|
{
|
|
|
|
|
_controller.RegisterBinding(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NormalizeEntries()
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < _entries.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (_entries[i] != null)
|
|
|
|
|
{
|
|
|
|
|
_entries[i].Normalize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|