将焦点放在 UI 对象之外

更新页 :
页面创建日期 :

验证环境

窗户
  • 窗户 11
Unity 编辑器
  • 2020.3.25f1

此提示的先决条件

作为解释这些提示的先决条件,已提前进行了以下设置。

你觉得这个提示有什么帮助?

原来的故事在某个地方,但我无法通过搜索关键字找到它。

关于Focus

与任何其他应用程序一样,Unity UI 对象可以具有一个焦点,指示它们作为输入目标处于活动状态。 但是,如果单击或触摸对象以外的任何对象,则具有焦点的对象将消失。 可能无法接受使用键盘或游戏手柄的操作。

本节介绍如何使用脚本来控制焦点,使其不会在伪焦点中丢失焦点。 请注意,此支持不是 Unity 功能,可能会导致您暂时失去注意力。

对象放置

对象的类型在焦点控制中无关紧要,因此请适当放置。

它是彩色的,因此很容易看出它是焦点。

目前,如果在此状态下运行它,可以看到,如果在对象获得焦点后单击空白位置,它将失去焦点。

控制,这样你就不会失去注意力

为此,请创建一个脚本。 脚本名称可以是任何名称,但 FocusRequired 将其保留为 .

按如下方式输入代码:

using System.Collections;
using System.Linq;                 // 追加
using UnityEngine;
using UnityEngine.EventSystems;    // 追加
using UnityEngine.UI;              // 追加

public class FocusRequired : MonoBehaviour
{
  /// <summary>
  /// <see cref="Selectable"/> をフックするクラスです。
  /// </summary>
  private class SelectionHooker : MonoBehaviour, IDeselectHandler
  {
    /// <summary>親コンポーネント。</summary>
    public FocusRequired Restrictor;

    /// <summary>
    /// 選択解除時にそれまで選択されていたオブジェクトを覚えておく。
    /// </summary>
    /// <param name="eventData"></param>
    public void OnDeselect(BaseEventData eventData)
    {
      Restrictor.PreviousSelection = eventData.selectedObject;
    }
  }

  /// <summary>選択させないオブジェクト一覧。</summary>
  [SerializeField] private GameObject[] NotSelectables;

  /// <summary>直前まで選択されていたオブジェクト。</summary>
  private GameObject PreviousSelection = null;

  /// <summary>
  /// 選択対象のオブジェクト一覧。
  /// </summary>
  private GameObject[] _selectables;

  private void Awake()
  {
    // すべての Selectable を取得する
    var selectableList = (FindObjectsOfType(typeof(Selectable)) as Selectable[]).ToList();

    // 選択除外がある場合は外す
    if (NotSelectables != null)
    {
      foreach (var item in NotSelectables)
      {
        var sel = item?.GetComponent<Selectable>();
        if (sel != null) selectableList.Remove(sel);
      }
    }

    _selectables = selectableList.Select(x => x.gameObject).ToArray();

    // フォーカス許可オブジェクトに SelectionHooker をアタッチ
    foreach (var selectable in this._selectables)
    {
      var hooker = selectable.AddComponent<SelectionHooker>();
      hooker.Restrictor = this;
    }

    // フォーカス制御用コルーチンをスタート
    StartCoroutine(RestrictSelection());
  }

  /// <summary>
  /// フォーカス制御処理。
  /// </summary>
  /// <returns></returns>
  private IEnumerator RestrictSelection()
  {
    while (true)
    {
      // 別なオブジェクトを選択するまで待機
      yield return new WaitUntil(
          () => (EventSystem.current != null) && (EventSystem.current.currentSelectedGameObject != PreviousSelection));

      // まだオブジェクトを未選択、または許可リストを選択しているなら何もしない
      if ((PreviousSelection == null) || _selectables.Contains(EventSystem.current.currentSelectedGameObject))
      {
        continue;
      }

      // 選択しているものがなくなった、または許可していない Selectable を選択した場合は前の選択に戻す
      EventSystem.current.SetSelectedGameObject(PreviousSelection);
    }
  }
}

我不会赘述太多细节,但是 我正在做的是保留当前的选择对象,如果“所选对象已消失”或“我选择了不想被选中的对象”,我将恢复到以前的选择。

此组件可以附加到任何存在的对象,因此 EventSystem 请将其附加到 。

由于可以指定您不想被选为属性的对象,因此让我们尝试放置一个您不想选择的按钮。

Not Selectables 按钮设置为 。

尝试运行它以查看它是如何工作的。 首先,单击一个对象以将其选中,然后单击空白区域,以免它失去焦点。

您还可以确保单击设置为未选中的按钮不会移动焦点。