今回の目的
- UITKのUIElementのイベントをR3の
Observable
で受け取れるようにしたい。
定義した拡張メソッド
Button
とINotifyValueChanged<T>
に拡張メソッドを定義すればひとまずイベントは取れそう。
using System;
using System.Threading;
using R3;
using UnityEngine.UIElements;
namespace R3Extensions
{
public static class R3UitkExtensions
{
public static Observable<Unit> OnClickAsObservable(this Button button, CancellationToken token = default)
{
return Observable.FromEvent(
e => button.clicked += e,
e => button.clicked -= e, token);
}
public static Observable<T> OnValueChangedAsObservable<T>(this INotifyValueChanged<T> changed,
CancellationToken token = default)
{
if (changed is CallbackEventHandler handler)
{
return Observable.FromEvent<EventCallback<ChangeEvent<T>>, T>(
convert => evt => convert(evt.newValue),
add => handler.RegisterCallback(add),
remove => handler.UnregisterCallback(remove),
token
);
}
throw new ArgumentException($"{changed} does not implement CallbackEventHandler.");
}
}
}
使用例
using R3;
using R3Extensions; // here
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace Sandbox
{
public class UIControlSample : MonoBehaviour
{
[SerializeField] private UIDocument _uiDocument;
void Start()
{
// 各種UIElementを作成してUIに追加する
var button = new Button();
_uiDocument.rootVisualElement.Add(button);
var dropdown = new DropdownField();
dropdown.choices.Add("Option 1");
dropdown.choices.Add("Option 2");
dropdown.choices.Add("Option 3");
_uiDocument.rootVisualElement.Add(dropdown);
var textField = new TextField
{
value = "Initial Text"
};
_uiDocument.rootVisualElement.Add(textField);
var toggle = new Toggle("Toggle Me");
_uiDocument.rootVisualElement.Add(toggle);
var slider = new Slider("Slide Me", 0, 100);
_uiDocument.rootVisualElement.Add(slider);
var colorField = new ColorField("Pick a Color")
{
value = Color.red
};
_uiDocument.rootVisualElement.Add(colorField);
var layerMaskField = new LayerMaskField("Select Layer Mask")
{
value = LayerMask.GetMask("Default")
};
_uiDocument.rootVisualElement.Add(layerMaskField);
// --- ここからイベント購読 ---
// Button
button.OnClickAsObservable(destroyCancellationToken)
.Subscribe(_ => Debug.Log("Button clicked!"))
.AddTo(this);
// Dropdown
dropdown.OnValueChangedAsObservable(destroyCancellationToken)
.Subscribe(x => Debug.Log($"Dropdown changed: {x}, Index: {dropdown.index}"))
.AddTo(this);
// TextField
textField.OnValueChangedAsObservable()
.Subscribe(x => Debug.Log($"TextField changed: {x}"))
.AddTo(this);
// Toggle
toggle.OnValueChangedAsObservable()
.Subscribe(isOn => Debug.Log($"Toggle is now: {isOn}"))
.AddTo(this);
// Slider
slider.OnValueChangedAsObservable()
.Subscribe(value => Debug.Log($"Slider value changed: {value}"))
.AddTo(this);
// ColorField and LayerMaskField
colorField.OnValueChangedAsObservable()
.Subscribe(color => Debug.Log($"Color changed: {color}"))
.AddTo(this);
// LayerMaskField
layerMaskField.OnValueChangedAsObservable()
.Subscribe(mask => Debug.Log($"Layer Mask changed: {mask}"))
.AddTo(this);
// etc...
}
}
}
所感
-
UIElement
は基本的にINotifyValueChanged<T>
を実装しており、ここからイベントを取得することができる- ただし
Button
のclicked
イベントは別扱いだった
- ただし
- uGUIの
InputField
にあったonEditEnd
のイベントはUITKでは取れない?- Unity6.0以降では IEditableElement というインタフェースが追加されているっぽくここから将来的に取れるかも
- しかし
internal
なためアクセスはできず、ScriptReferenceにもまだ書かれていない
- しかし
- Unity6.0以降では IEditableElement というインタフェースが追加されているっぽくここから将来的に取れるかも