概要
自分用に作成したLimeLibraryを用いたUIの実装例についての続きになります。
前回はこちら。
limegame.hatenablog.com
このライブラリでどのようなことができるかについてはこちらの記事をご覧ください。
limegame.hatenablog.com
今回の内容
アイテムリストを選択可能にして、フローに組み込みます。
実装手順
InputModeUpdaterの追加
ゲームパッドやマウスの操作モード変更イベントを取得するため、シーンの任意の場所にInputModeUpdaterをアタッチしたGameObjectを追加します。
カーソルの用意
選択がわかりやすいようにカーソルを用意し追加します。
(半透明色を設定したImageComponentで大丈夫です)
アイテムにButtonComponentを追加
アイテムを選択可能にするために、ButtonComponentを追加します。
今回はLimeLibrary側のラッパークラスであるUIButtonをアタッチします。
(自動的にButtonもアタッチされます)
選択時挙動のセットアップとイベントの追加
ItemUiElementsに以下のコードを追加し、選択時の挙動の追加とイベントの公開を行います。
// ItemUiElements.cs // 省略 public class ItemUiElements : MonoBehaviour, IInstantiatableUiElements<ItemUiElements> { [SerializeField] private UIText _text; // 追加 [SerializeField] private UIButton _button; public ItemUiElements Self => this; // 追加 public IObservable<Unit> OnHighlightedObservable => _button.GetEventObservable(UIButtonEventType.PointerEnter).AsUnitObservable(); public IObservable<Unit> OnSelectObservable => _button.GetEventObservable(UIButtonEventType.Select).AsUnitObservable(); public IObservable<Unit> OnClickObservable => _button.OnClickObservable; // 省略 // 追加 public void SetupSelectable(IUIView parentView, SelectCursor selectCursor) { // 選択時挙動制御クラスをセットアップ var selectableExtender = new SelectableExtender(parentView, _button.Button); // カーソル移動の選択時挙動をセットアップ var appearanceCursor = new CursorMover(gameObject, selectCursor); // Gamepad操作時はSelect時にカーソル移動 selectableExtender.AddSelectableAppearance(ExtendSelectionState.Selected, appearanceCursor, InputMode.Gamepad); // Mouse操作時はHighlighted時にカーソル移動 selectableExtender.AddSelectableAppearance(ExtendSelectionState.Highlighted, appearanceCursor, InputMode.MouseKeyboard); } // 省略
SetupSelectableという関数を追加しています。
SelectableExtenderというクラスを使用することで、SelectableAppearanceを継承した挙動を表すインスタンス、その挙動の行うタイミングのステート、その挙動を行う入力タイプを渡すことで自動的にその挙動が行われるようになります。
また、今後の実装のためHighlited、Select、ClickのイベントもIObservableとして追加しています。
ItemListUiElements側から呼び出しとViewへのイベントの追加
先ほど追加した関数をItemListUiElements側から呼び出します。
また、カーソルが必要なためプレハブ側で追加したカーソル画像をGameObjectとしてSerializeしSelectCursorインスタンスを作成します。
最後に、ItemUiElements側で追加したイベント(IObservable)をViewから呼び出せるようにするため、
ItemUiElments→ItemListUIElments(ここでインデックス情報を追加)→ItemListViewまで伝播させます。
// ItemListUiElements.cs // 省略 public class ItemListUiElements : MonoBehaviour { [SerializeField] private ItemUiElements _originalItemUiElements; // 追加 [SerializeField] private GameObject _cursorObject; private readonly List<ItemUiElements> _createdItemUiElementsList = new(); // 追加 private SelectCursor _selectCursor; // 追加 private readonly Subject<int> _onHighlightedSubject = new(); private readonly Subject<int> _onSelectSubject = new(); private readonly Subject<int> _onClickSubject = new(); public IObservable<int> OnHighlightedObservable => _onHighlightedSubject; public IObservable<int> OnSelectObservable => _onSelectSubject; public IObservable<int> OnClickObservable => _onClickSubject; public void Initialize(IUIView parentView) { _originalItemUiElements.gameObject.SetActive(false); // 追加 _selectCursor = new SelectCursor(parentView, _cursorObject); } public void CreateList(IUIView parentView, IReadOnlyList<ItemData> itemDataList) { // リストを作る前に前回生成したリストを削除 Clear(); if (itemDataList == null) return; // リスト分生成しデータを流し込む for (var i = 0; i < itemDataList.Count; i++) { var itemData = itemDataList[i]; var itemUiElements = _originalItemUiElements.Instantiate(parentView); _createdItemUiElementsList.Add(itemUiElements); itemUiElements.SetItemName(itemData.Name); // 追加 itemUiElements.SetupSelectable(parentView, _selectCursor); // Index情報を追加しイベント発行 int index = i; itemUiElements.OnSelectObservable.Subscribe(_ => _onSelectSubject.OnNext(index)).AddTo(itemUiElements); itemUiElements.OnClickObservable.Subscribe(_ => _onClickSubject.OnNext(index)).AddTo(itemUiElements); } } // 省略 }
// ItemListView.cs // 省略 public class ItemListView : UIView { [SerializeField] private ItemListUiElements _uiElements; // 追加 public IObservable<int> OnHighlightedObservable => _uiElements.OnHighlightedObservable; public IObservable<int> OnSelectObservable => _uiElements.OnSelectObservable; public IObservable<int> OnClickObservable => _uiElements.OnClickObservable; // 省略 }
イベントをトリガーにしてフローに組み込む
クリック時イベントがViewに組み込めたので、InventoryFlowSelectItemを以下のように変更しアイテムのクリックに反応させます。
// InventoryFlowSelectItem.cs using Cysharp.Threading.Tasks; using LimeLibrary.UI; namespace Sample { public class InventoryFlowSelectItem : UIAppFlowState<InventoryFlowState, InventoryFlowContext> { public override void Initialize() { } public override async UniTask<InventoryFlowState> Execute() { var itemListView = GetView<ItemListView>(); // ItemListの作成 itemListView.CreateList(Context.ItemDataList); // ItemListViewの表示 await itemListView.Show(CancellationToken); // キャンセルキーが押されるかアイテムがクリックされるまで待機 var result = await UniTask.WhenAny( Context.CancelInputReceiver.OnInputObservable.ToUniTask(true, cancellationToken: CancellationToken), itemListView.OnClickObservable.ToUniTask(true, cancellationToken: CancellationToken)); switch (result.winArgumentIndex) { case 0: // キャンセル RequestEnd(); break; case 1: // アイテムがクリックされた await UIApp.GetView<ActionListView>().Show(CancellationToken); break; } return InventoryFlowState.SelectItem; } } }
アイテムをクリックすることでアクションリストが表示されれば成功です。