Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Selectionで表示されるプレハブを、キーボードで操作したい

66 views
Skip to first unread message

田中せいや

unread,
Feb 4, 2025, 12:26:52 PMFeb 4
to 宴ユーザーグループ
宴のバージョン: 宴4.1.5
UnityのバージョンとOS: Unity6000.0.34f1 Windows
Unityの習熟度:初心者。プログラムはできません。

いつも宴を使わせていただいております。
掲題の件ですが「Selectionで表示されるプレハブを、キーボードで操作する」ことは可能でしょうか。

「Selectionコマンドにより表示されたプレハブを、キーボードの上下左右キーで選択し、エンターキーで決定する」ようなものを想定しております。
動機としましては『RPGツクール』のような4方向移動での会話シーンで使用できればと考え、操作性の観点から希望しました。

https://madnesslabo.net/utage/?page_id=9794
こちらのページを参考にし、AdvUguiSelectionManagerスクリプトなども見ましたが、なかなか実現が難しく感じました。
なにか簡単に実現することは可能でしょうか?

マッドネスラボ

unread,
Feb 4, 2025, 7:58:37 PMFeb 4
to 宴ユーザーグループ
簡単には無理です。
宴のUIは、UnityのUGUIを基本としてますので、キーボードによるボタン操作もそれに従って実装する形になります。
キーボード入力は、プロジェクトごとに仕様が全く異なるのでプログラムが必須になります。


2025年2月5日水曜日 2:26:52 UTC+9 chanshi...@gmail.com:

マッドネスラボ

unread,
Feb 4, 2025, 8:42:15 PMFeb 4
to 宴ユーザーグループ
サンプルを追加しましたので、添付のパッケージファイルを適用してください。
(ダウンロードした添付ファイルの拡張子が *. gzになっている場合は、*.unitypackageに変えてからプロジェクトに適用してください)
この変更は、次回の宴のアップデートにも反映させる予定です。

パッケージを適用すると、Utage/Sample/Scripts/SampleSelectionKeyboardInput.cs というコンポーネントが追加されますので、
これをAdvEngine>UI>SelectionオブジェクトにAddComponentしてください。

サンプルのソースコードは次のようになっています。
選択肢が表示された時に、
・キーボードの選択対象を選択肢の最初のボタンにする
・上下キーで各ボタンをループ選択する
という形になっています。

ただ、これはSelectionコマンドで位置指定なく、選択肢が標準的な形で縦に並んでいる場合にのみ通用します。
位置指定など使って、縦並びではなく横並びにもボタンがあって、左右キーでボタンが移動をしたい場合は、その仕様に合わせてプログラムを改変しなければいけません。
また、選択肢以外へ入力対象が遷移した場合(バックログを開いた場合など)、適切なタイミングで再び入力対象を選択肢に戻すといったことも必要になります。
下部のメニューボタンへのボタン遷移は考慮されていないので、必要に応じてそれらのボタン遷移も繋げる必要があります。

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UtageExtensions;

namespace Utage
{
    // 選択肢のキーボード入力を制御するクラス
    public class SampleSelectionKeyboardInput : MonoBehaviour
    {
        AdvEngine Engine => this.GetAdvEngineCacheFindIfMissing(ref engine);
        [SerializeField] private AdvEngine engine;
       
        AdvUguiSelectionManager UguiSelectionManager => this.GetComponentCacheInChildren(ref uguiSelectionManager);
        [SerializeField] AdvUguiSelectionManager uguiSelectionManager;
       
        void Awake()
        {
            Engine.SelectionManager.OnBeginWaitInput.AddListener(OnInit);
        }

        void OnInit(AdvSelectionManager selectionManager)
        {
            if(UguiSelectionManager.Items.Count <= 0)  return;

            //最初のボタンを選択状態にする
            EventSystem.current.SetSelectedGameObject(UguiSelectionManager.Items[0]);
           
            //上下のボタンで選択肢のボタンを移動する
            for (int i = 0; i < UguiSelectionManager.Items.Count; i++)
            {
                var button = UguiSelectionManager.Items[i].GetComponent<Button>();
                int count = UguiSelectionManager.Items.Count;
                int prevIndex = (i - 1 + count)% count;
                int nextIndex = (i + 1) % count;
                var prevButton = UguiSelectionManager.Items[prevIndex].GetComponent<Button>();
                var nextButton = UguiSelectionManager.Items[nextIndex].GetComponent<Button>();
               
                //ボタンの移動先を設定
                button.navigation = new Navigation {mode = Navigation.Mode.Explicit, selectOnUp = prevButton, selectOnDown = nextButton};
            }
        }
    }
}
2025年2月5日水曜日 9:58:37 UTC+9 マッドネスラボ:
SampleSelectionKeyBoradInput.unitypackage

田中せいや

unread,
Feb 12, 2025, 12:17:10 PMFeb 12
to 宴ユーザーグループ
ご返信いただき誠にありがとうございます。
こちらのサンプルを元に、最終的に望む挙動が実現できました。
上記のサンプルで試してみたところ、私の環境下ではなぜかキャラ移動のためのWASDのWとSで選択肢のナビゲーションが動いてしまったこともあり、最終的にはプロジェクト的にInputSystemを使用してキーを管理したところで、たしかに、キーボードによる操作はプロジェクトごとの仕様で大きく異なることがわかりました。

以下、私の環境下で動くものであることと、コードの内容を理解できているわけではないのですが、
一応、望む挙動を実現できたと思われるコードを超参考レベルで載せておきます。(同じような質問があったように思えましたので)
ただ私の場合では「4方向移動のゲームで会話シーンに宴を使い、会話中もキャラ移動ができるようにしたい」など特殊な仕様が混じっているため参考になるかは全くわかりません…
(完全に無責任なコード掲載ですので、もしやめたほうがいいということであれば、削除します)

【前提】
・InputSystemを使い、キーボードとゲームパッドでも「Selectionの選択肢のナビゲーション」と「文字送り」ができるようにしたかった。
・InputSystemの設定は
  ActionMaps - "Player" "UI"
  Actions - Player - "Move","Jump","Interact"
             UI - "EnterMessage","Select"
 でそれぞれ適当に設定していました。

【要点】
・使ったスクリプトは2点で「(Selectionの選択肢のナビゲーションのための)上記サンプルを改造したSelectionKeyboardInput」と「(文字送りのための)CustomAdvUguiManager」
・SelectionKeyboardInputは「InputSystemで設定したもの以外で選択肢のナビゲーションを動かさない」「ActionMapsのPlayerとキーがバッティングするようであれば、UIを優先しPlayerの方を無効にする」などの要素が入っていると思います
・CustomAdvUguiManagerはAdvUiManagerをおそらく継承する形にしている

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using System.Collections;
using UtageExtensions;

namespace Utage
{
    public class SelectionKeyboardInput : MonoBehaviour
    {
        private PlayerControls controls;
        private int currentIndex = 0;


        AdvEngine Engine => this.GetAdvEngineCacheFindIfMissing(ref engine);
        [SerializeField] private AdvEngine engine;

        AdvUguiSelectionManager UguiSelectionManager => this.GetComponentCacheInChildren(ref uguiSelectionManager);
        [SerializeField] private AdvUguiSelectionManager uguiSelectionManager;

        void Awake()
        {
            controls = new PlayerControls();
            controls.UI.Select.performed += OnSelectPerformed;
            controls.UI.EnterMessage.performed += OnEnterPerformed;
            Engine.SelectionManager.OnBeginWaitInput.AddListener(OnInit);
        }

        void OnEnable()
        {
            controls.UI.Enable();
            controls.Player.Disable(); // UIを優先し、Playerの入力を無効化
        }

        void OnDisable()
        {
            controls.UI.Disable();
            controls.Player.Enable(); // UIを抜けたらPlayerの入力を有効化
        }

        void OnInit(AdvSelectionManager selectionManager)
        {
            if (UguiSelectionManager.Items.Count <= 0) return;

            // UIナビゲーションをInputSystemのみに制限する
            EventSystem.current.sendNavigationEvents = false;

            currentIndex = 0;
            StartCoroutine(SetSelectedButton(UguiSelectionManager.Items[currentIndex]));
        }

        void OnSelectPerformed(InputAction.CallbackContext context)
        {
            if (UguiSelectionManager.Items.Count <= 0) return;

            Vector2 input = context.ReadValue<Vector2>();
            if (Mathf.Abs(input.y) > Mathf.Abs(input.x)) // 上下のみを処理
            {
                if (input.y > 0) // 上移動
                {
                    currentIndex = Mathf.Max(currentIndex - 1, 0);
                }
                else if (input.y < 0) // 下移動
                {
                    currentIndex = Mathf.Min(currentIndex + 1, UguiSelectionManager.Items.Count - 1);
                }

                StartCoroutine(SetSelectedButton(UguiSelectionManager.Items[currentIndex]));
            }
        }

        void OnEnterPerformed(InputAction.CallbackContext context)
        {
            if (UguiSelectionManager.Items.Count <= 0) return;

            GameObject selected = EventSystem.current.currentSelectedGameObject;
            if (selected != null)
            {
                selected.GetComponent<Button>()?.onClick.Invoke();
            }
        }

        private IEnumerator SetSelectedButton(GameObject button)
        {
            yield return new WaitForEndOfFrame(); // フレーム遅延を挟む
            EventSystem.current.SetSelectedGameObject(null); // 既存の選択をクリア
            EventSystem.current.SetSelectedGameObject(button); // ボタンを選択
            ExecuteEvents.Execute(button, new BaseEventData(EventSystem.current), ExecuteEvents.selectHandler); // UI フォーカス適用
        }
    }
}



using UnityEngine;
using UnityEngine.InputSystem;
using Utage;

public class CustomAdvUguiManager : AdvUguiManager
{
    private PlayerControls playerControls;

    private void Awake()
    {
        // InputSystem の PlayerControls を初期化
        playerControls = new PlayerControls();

        // UI マップの EnterMessage アクションにイベントを設定
        playerControls.UI.EnterMessage.performed += OnEnterMessage;

        // 入力を有効化
        playerControls.Enable();
    }

    private void OnEnterMessage(InputAction.CallbackContext context)
    {
        // Enterキーでメッセージ送りを行う
        if (Status == UiStatus.Default && IsShowingMessageWindow)
        {
            Engine.Page.InputSendMessage();
        }
    }

    private void OnDestroy()
    {
        // InputSystem のイベントを解除
        if (playerControls != null)
        {
            playerControls.UI.EnterMessage.performed -= OnEnterMessage;
            playerControls.Disable();
        }
    }
}



SelectionKeyboardInput は上記サンプルと同じ使用方法
CustomAdvUguiManager はAdvEngine-UIのAdvUguiManagerをRemoveComponentしてアタッチ
です。
2025年2月5日水曜日 10:42:15 UTC+9 マッドネスラボ:

マッドネスラボ

unread,
Feb 12, 2025, 4:51:33 PMFeb 12
to 宴ユーザーグループ
コードのご提供ありがとうございます。
詳細な説明もありますので、同じ目的の人の参考になるかと思います。



2025年2月13日木曜日 2:17:10 UTC+9 chanshi...@gmail.com:

田中せいや

unread,
Feb 25, 2025, 7:53:10 AMFeb 25
to 宴ユーザーグループ
(追記です)
上記操作で「クリックで文字送りができなくなってしまった」ことに気が付いたので、一応ですが解決方法をのせておきます。
CustomAdvUguiManagerをアタッチする際に AdvUguiManagerをRemoveComponentした影響で、InputManager(AdvEngine-UI-)のEventTriggerコンポーネントからAdvUguiManagerが外れてしまい、検知できなくなってしまっていました。
その場合は、デフォルトの宴と同じようにEventTriggerにUIオブジェクトのCustomAdvUguiManagerのOnPointerDownを設定すれば、なおりました…。
2025年2月13日木曜日 6:51:33 UTC+9 マッドネスラボ:
Reply all
Reply to author
Forward
0 new messages