自訂螢幕控制項以實現方向鍵

更新頁 :
頁面創建日期 :

驗證環境

窗戶
  • 視窗 11
統一編輯器
  • 2020.3.25f1
輸入系統包
  • 1.2.0

此提示的先決條件

作為此提示描述的前提,已預先進行了以下設置。

您還應該熟悉以下提示:

螢幕控制桿是標準的方向操作

屏幕搖桿作為虛擬搖桿實現,複製類似於物理控制器上的搖桿的操作。 例如,如果要將其向右移動,可以先觸摸螢幕觸摸搖桿,然後將其向右滑動以將搖桿擊倒。

很容易想像成一個搖桿操作,但相反,當你想立即向右移動它時,(1)觸摸,(2)向右滑動, 由於需要兩步操作,因此回應不可避免地會有點延遲。 當您需要連續快速改變方向時尤其如此。

在觸摸時確定方向的方向鍵(非標準)

快速方向輸入的最佳輸入方法是可以通過放置方向鍵或箭頭鍵之類的東西來確定觸摸時的方向。 它不是由Unity製作的,但是在我的遊戲Little Saber中,我放置了箭頭鍵,以便您可以快速向觸摸的方向移動。 當然,您也可以在觸摸時向上或向左滑動以切換動作。

但是,螢幕控制標準不容易放置和實施,因為只有屏幕搖桿和螢幕按鈕。

您也可以如下圖所示,通過排列四個按鈕來創建偽方向鍵,但這很不方便,因為您無法對角線輸入。 如果放置8個按鈕,則可以進行對角線操作,但是仍然無法進行諸如“ ←↙↓”之類的流向操作。

使用螢幕控制自定義創建方向鍵

如您所見,螢幕控制僅標配搖桿和按鈕,但您可以使用自己的腳本自定義缺少的功能。 所以在這裡我想創建一個帶有螢幕控制自定義的方向鍵。

行動地圖

我們將使用操作映射,但我們將按原樣使用在前面的提示中創建的操作映射,因此省略了該過程。

您還編寫了一些代碼。

定位物件

放置一個文本區域以顯示您的輸入和一個替換方向鍵的按鈕。 在這種情況下,按鈕是排列的,但您可以將它們替換為更易於理解的圖像。

用於顯示輸入的腳本

由於螢幕控制取代了物理控制器交互的觸摸, 創建一個腳本,在操作映射工作時以文本形式顯示輸入。

內容和以前一樣,所以我省略了解釋。

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class InputActionScript : MonoBehaviour
{
  /// <summary>情報を表示させるテキストオブジェクト。</summary>
  [SerializeField] private Text TextObject;

  /// <summary>アクションマップから自動生成されたクラス。</summary>
  private InputActionSample _actionMap;

  private void Awake()
  {
    // 各操作を行ったときに呼ばれるイベントを設定する
    _actionMap = new InputActionSample();
    _actionMap.Action2D.Move.performed += context => OnMove(context);
    _actionMap.Action2D.Attack.performed += context => OnAttack(context);
    _actionMap.Action2D.Move.canceled += context => OnMove(context);
    _actionMap.Action2D.Attack.canceled += context => OnAttack(context);
  }

  private void OnEnable()
  {
    // このオブジェクトが有効になったときにアクションマップを有効にする
    _actionMap.Enable();
  }

  private void OnDisable()
  {
    // このオブジェクトが無効になったときにアクションマップが余計な動作を起こさないように無効にする
    _actionMap.Disable();
  }

  /// <summary>
  /// Move 操作をした時に呼ばれるメソッドです。
  /// </summary>
  /// <param name="context">コールバックパラメータ。</param>
  public void OnMove(InputAction.CallbackContext context)
  {
    // Move の入力量を取得
    var vec = context.ReadValue<Vector2>();
    TextObject.text = $"Move:({vec.x:f2}, {vec.y:f2})\n{TextObject.text}";
  }

  /// <summary>
  /// Attack 操作をした時に呼ばれるメソッドです。
  /// </summary>
  /// <param name="context">コールバックパラメータ。</param>
  public void OnAttack(InputAction.CallbackContext context)
  {
    // Attack ボタンの状態を取得
    var value = context.ReadValueAsButton();
    TextObject.text = $"Attack:{value}\n{TextObject.text}";
  }
}

設置完成後,首先檢查它是否適用於您的鍵盤或遊戲手柄。

自訂螢幕控制件

這就把我們帶到了這些技巧的主要主題。 自定義螢幕控制項是一個腳本,因此請先創建一個腳本。 名稱是任意的,但在這種情況下 OnScreenDpad 是.

文稿如下所示:

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.OnScreen;

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
{
  [InputControl(layout = "Vector2")]
  [SerializeField]
  private string _controlPath;
  /// <summary><see cref="OnScreenControl"/> で定義された値。</summary>
  protected override string controlPathInternal { get => _controlPath; set => _controlPath = value; }

  /// <summary>オブジェクトの位置。</summary>
  private Vector2 _objectPosition;

  /// <summary>オブジェクトのサイズの半分 (スケールも含む)。</summary>
  private Vector2 _objectSizeHalf;


  /// <summary>
  /// オブジェクトが動作する最初のタイミングで1回だけ呼ばれます。
  /// </summary>
  private void Start()
  {
    var rectTransform = (RectTransform)base.transform;

    // オブジェクトの位置を取得
    _objectPosition = rectTransform.anchoredPosition;

    // オブジェクトのサイズの半分を取得 (スケールサイズも考慮)
    _objectSizeHalf = rectTransform.sizeDelta * rectTransform.localScale / 2f;
  }

  /// <summary>ドラッグの初期化処理として呼ばれます。</summary>
  /// <param name="eventData">タッチ情報。</param>
  public void OnInitializePotentialDrag(PointerEventData eventData)
  {
    // タッチのスライド操作を即座に発生させたいのでドラッグ開始までの閾値を無効にします
    eventData.useDragThreshold = false;
  }

  /// <summary>タッチしたタイミングで呼ばれます。</summary>
  /// <param name="eventData">タッチ情報。</param>
  public void OnPointerDown(PointerEventData eventData)
  {
    Operate(eventData);
  }

  /// <summary>タッチした後ドラッグするたびに呼ばれます。</summary>
  /// <param name="eventData">タッチ情報。</param>
  public void OnDrag(PointerEventData eventData)
  {
    Operate(eventData);
  }

  /// <summary>タッチを離したときに呼ばれます。</summary>
  /// <param name="eventData">タッチ情報。</param>
  public void OnPointerUp(PointerEventData eventData)
  {
    // 入力をやめた扱いにしたいので zero を渡します
    SendValueToControl(Vector2.zero);
  }

  /// <summary>
  /// 方向パッドの入力処理を行います。
  /// </summary>
  /// <param name="eventData">タッチ情報。</param>
  private void Operate(PointerEventData eventData)
  {
    // タッチ位置を Canvas 上の位置に変換します
    RectTransformUtility.ScreenPointToLocalPointInRectangle(
      transform.parent.GetComponentInParent<RectTransform>(),
      eventData.position,
      eventData.pressEventCamera,
      out Vector2 localPoint);

    // Dpad の中心を原点としたタッチ位置
    Vector2 positionInDpad = localPoint - _objectPosition;

    // タッチ位置をオブジェクトサイズの半分で割り 0~1 の範囲に収めます
    Vector2 positionRate = Vector2.ClampMagnitude(positionInDpad / _objectSizeHalf, 1);

    // 入力値を OnScreenControl に渡してその後の処理を任せます。
    SendValueToControl(positionRate);
  }
}

OnScreenControl通過繼承自定義類來創建螢幕控件自定義類。 此外,要接收各種觸摸事件,請繼承目標介面。 這裏處理的是“觸摸時”、“觸摸時移動時”、“鬆開觸摸時”、“拖動前”,所以下面也分別描述了以下介面。

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
介面 內容
IPointerDownHandler 觸摸時
IPointerUpHandler 當您鬆開觸摸時
IDragHandler 當您在觸摸時移動時
IInitializePotentialDragHandler 開始拖動之前

宣告以下欄位:

controlPathInternalOnScreenControl如果從類繼承,則必需。 它包含要映射到的輸入設備的哪個按鈕的字串,但基本上您可以按原樣編寫它。 InputControl但是,該屬性應包含要保留的值(在本例中為 Vector2)。

_objectPosition_objectSizeHalf並提前按住按鈕位置和大小的一半,稍後使用它進行計算。

[InputControl(layout = "Vector2")]
[SerializeField]
private string _controlPath;
/// <summary><see cref="OnScreenControl"/> で定義された値。</summary>
protected override string controlPathInternal { get => _controlPath; set => _controlPath = value; }

/// <summary>オブジェクトの位置。</summary>
private Vector2 _objectPosition;

/// <summary>オブジェクトのサイズの半分 (スケールも含む)。</summary>
private Vector2 _objectSizeHalf;

該方法在物件初始化后調用 Start ,獲取畫布上按鈕位置和大小的一半。 尺寸還考慮了規模。 Start 它通過一種方法進行處理,但如果最終可以在計算中正確使用,則採集時序可以在任何地方。

// <summary>
// オブジェクトが動作する最初のタイミングで1回だけ呼ばれます。
// </summary>
private void Start()
{
  var rectTransform = (RectTransform)base.transform;

  // オブジェクトの位置を取得
  _objectPosition = rectTransform.anchoredPosition;

  // オブジェクトのサイズの半分を取得 (スケールサイズも考慮)
  _objectSizeHalf = rectTransform.sizeDelta * rectTransform.localScale / 2f;
}

OnInitializePotentialDrag 當您想在觸摸後開始拖動時調用。 拖動時, OnDrag 將調用該方法。 設置閾值,在一定程度上防止拖動判斷,使手指不動一點,不會因為簡單的觸摸操作而發生拖動判斷。

這一次,由於它是假設您要執行微調操作的輸入,因此請禁用此閾值設置並立即做出拖動判斷。 eventData.useDragThreshold false 您可以通過設置為 來覆蓋閾值。

/// <summary>ドラッグの初期化処理として呼ばれます。</summary>
/// <param name="eventData">タッチ情報。</param>
public void OnInitializePotentialDrag(PointerEventData eventData)
{
  // タッチのスライド操作を即座に発生させたいのでドラッグ開始までの閾値を無効にします
  eventData.useDragThreshold = false;
}

以下是分別在觸摸、拖動和釋放時調用的事件。 OnPointerDown OnDrag由於 和,每個都執行Operate相同的輸入處理,因此我們創建一個單獨的方法並調用它。 OnPointerUp現在,將其傳遞給方法以SendValueToControlVector2.zero通知控件它已停止鍵入。

/// <summary>タッチしたタイミングで呼ばれます。</summary>
/// <param name="eventData">タッチ情報。</param>
public void OnPointerDown(PointerEventData eventData)
{
  Operate(eventData);
}

/// <summary>タッチした後ドラッグするたびに呼ばれます。</summary>
/// <param name="eventData">タッチ情報。</param>
public void OnDrag(PointerEventData eventData)
{
  Operate(eventData);
}

/// <summary>タッチを離したときに呼ばれます。</summary>
/// <param name="eventData">タッチ情報。</param>
public void OnPointerUp(PointerEventData eventData)
{
  // 入力をやめた扱いにしたいので zero を渡します
  SendValueToControl(Vector2.zero);
}

Operate 該方法是核心方向鍵輸入操作。

首先,觸摸位置可以用 獲得,但由於這個座標是遊戲畫面的 eventData.position 座標, RectTransformUtility.ScreenPointToLocalPointInRectangle 轉換為畫布座標的方法。 要傳遞的值是「畫布」、「觸摸位置」、「 RectTransform相機」和「接收變數」。。

獲取「畫布上的觸摸位置」 後,將其轉換為按鈕的位置作為原點。 只需減去物件的位置_objectPosition ()。

接下來,由於觸摸位置仍然是畫布上的數位,因此將此值轉換為0~1的比率。 如果除以按鈕大小的一半,則按鈕範圍內的觸摸位置將為0~1。 如果使用觸摸拖動,則該值將為1或更大,因為它即使在按鈕外部也能接受操作。 Vector2.ClampMagnitude 在方法中執行此操作,使其不超過 1。

最後,將轉換為 0~1 的輸入值 SendValueToControl 傳遞給方法。 螢幕控制件為您完成其餘工作。

/// <summary>
/// 方向パッドの入力処理を行います。
/// </summary>
/// <param name="eventData">タッチ情報。</param>
private void Operate(PointerEventData eventData)
{
  // タッチ位置を Canvas 上の位置に変換します
  RectTransformUtility.ScreenPointToLocalPointInRectangle(
    transform.parent.GetComponentInParent<RectTransform>(),
    eventData.position,
    eventData.pressEventCamera,
    out Vector2 localPoint);

  // Dpad の中心を原点としたタッチ位置
  Vector2 positionInDpad = localPoint - _objectPosition;

  // タッチ位置をオブジェクトサイズの半分で割り 0~1 の範囲に収めます
  Vector2 positionRate = Vector2.ClampMagnitude(positionInDpad / _objectSizeHalf, 1);

  // 入力値を OnScreenControl に渡してその後の処理を任せます。
  SendValueToControl(positionRate);
}

將創建的腳稿附加到按鈕。 這個操作設置為被視為遊戲手柄左搖桿的操作。

嘗試移動遊戲並觸摸按鈕。 我想您可以看到獲得的值會根據觸摸位置而變化。 此外,您可以看到,即使您在觸摸時拖動,該值也會根據拖動位置而變化。