Personalizzare il controllo su schermo per implementare la croce direzionale

Pagina aggiornata :
Data di creazione della pagina :

Ambiente di verifica

Finestre
  • Windows 11
Unity Editor
  • 2020.3.25F1
Pacchetto del sistema di input
  • 1.2.0

Prerequisiti per questo suggerimento

Le seguenti impostazioni sono state effettuate in anticipo come premessa per la descrizione di questo suggerimento.

Dovresti anche avere familiarità con i seguenti suggerimenti:

On-Screen Control Stick è l'operazione di orientamento standard

L'On-Screen Stick è implementato come una chiavetta virtuale, replicando il funzionamento di una chiavetta simile a quella che si trova su un controller fisico. Ad esempio, se si desidera spostarlo verso destra, è possibile prima toccare lo schermo per toccare il bastone e quindi farlo scorrere verso destra per abbattere il bastone.

È facile immaginarlo come un'operazione di bastone, ma al contrario, quando si desidera spostarlo immediatamente a destra, (1) toccare, (2) scorrere verso destra, Poiché richiede un'operazione in due fasi, la risposta sarà inevitabilmente un po 'ritardata. Ciò è particolarmente vero quando è necessario effettuare un rapido cambio di direzione di seguito.

D-pad che determina la direzione al momento del tocco (non standard)

Il miglior metodo di input per l'input rapido della direzione è quello che può determinare la direzione al momento del tocco posizionando qualcosa come un D-pad o un tasto freccia. Non è fatto da Unity, ma nel mio gioco Little Saber, metto i tasti freccia in modo che tu possa muoverti rapidamente nella direzione che tocchi. Naturalmente, puoi anche scorrere verso l'alto o verso sinistra mentre tocchi per cambiare movimento.

Tuttavia, lo standard di controllo su schermo non è facile da posizionare e implementare perché ci sono solo stick su schermo e pulsanti su schermo.

Puoi anche creare uno pseudo-D-pad disponendo quattro pulsanti come mostrato nella figura seguente, ma è scomodo perché non puoi inserire diagonalmente. Se si posizionano 8 pulsanti, è possibile l'operazione diagonale, ma l'operazione di direzione del flusso come "← ↙ ↓" non è ancora possibile.

Creare un pad direzionale con la personalizzazione del controllo su schermo

Come puoi vedere, il controllo su schermo viene fornito solo di serie con Stick e Button, ma puoi personalizzare le funzionalità mancanti con i tuoi script. Quindi qui vorrei creare un pad direzionale con personalizzazione On-Screen Control.

Mappa d'azione

Useremo la mappa d'azione, ma useremo quella creata nei suggerimenti precedenti così com'è, quindi la procedura viene omessa.

Hai anche scritto del codice.

Posizionamento di oggetti

Posiziona un'area di testo per visualizzare l'input e un pulsante che sostituisce la croce direzionale. In questo caso, i pulsanti sono disposti, ma è possibile sostituirli con un'immagine più facile da capire.

Script per visualizzare l'input

Poiché Controllo su schermo sostituisce il tocco con l'interazione fisica del controller, Creare uno script che visualizzi l'input in testo durante il funzionamento della mappa delle azioni.

Il contenuto è lo stesso di prima, quindi ometterò la spiegazione.

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}";
  }
}

Dopo averlo configurato, controlla prima se funziona con la tastiera o il gamepad.

Personalizzazione dei controlli su schermo

Questo ci porta all'argomento principale di questi suggerimenti. La personalizzazione del controllo su schermo è uno script, quindi crea prima uno script. Il nome è arbitrario, ma in questo caso OnScreenDpad è .

Lo script è simile al seguente:

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);
  }
}

È possibile creare una OnScreenControl classe di personalizzazione Controllo su schermo ereditando dalla classe Customize. Inoltre, per ricevere vari eventi di tocco, ereditare l'interfaccia di destinazione. Qui, vengono elaborati "quando si tocca", "quando si muove mentre si tocca", "quando il tocco viene rilasciato" e "prima del trascinamento", quindi vengono descritte rispettivamente anche le seguenti interfacce.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Contenuto dell'interfaccia
IPointerDownHandler Quando toccato
IPointerUpHandler Quando rilasci il tocco
IDragHandler Quando ti muovi mentre tocchi
IInitializePotentialDragHandler Prima di iniziare il trascinamento

Vengono dichiarati i seguenti campi:

controlPathInternalOnScreenControl è obbligatorio se si eredita da una classe. Contiene una stringa di quale pulsante del dispositivo di input mappare, ma fondamentalmente puoi scriverlo così com'è. InputControl Tuttavia, l'attributo deve contenere il valore da mantenere (Vector2 in questo caso).

_objectPosition_objectSizeHalf e tenere metà della posizione e delle dimensioni del pulsante in anticipo e utilizzarlo in seguito per i calcoli.

[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;

Il metodo, chiamato Start dopo l'inizializzazione dell'oggetto, ottiene metà della posizione e delle dimensioni del pulsante nell'area di disegno. La dimensione tiene conto anche della scala. Start Viene elaborato con un metodo, ma se può essere utilizzato correttamente nel calcolo alla fine, i tempi di acquisizione possono essere ovunque.

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

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

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

OnInitializePotentialDrag viene chiamato quando si desidera iniziare a trascinare dopo il tocco. Durante il trascinamento, OnDrag viene chiamato il metodo. Una soglia è impostata per impedire il giudizio di trascinamento in una certa misura in modo che il dito non si muova un po 'e il giudizio di trascinamento non si verifichi a causa dell'operazione di semplice toccamento.

Questa volta, poiché si tratta di un input che presuppone che si desideri eseguire un'operazione di regolazione fine, disabilitare questa impostazione di soglia ed eseguire immediatamente un giudizio di trascinamento. eventData.useDragThreshold false È possibile ignorare la soglia impostando .

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

Di seguito sono riportati gli eventi chiamati rispettivamente quando si tocca, si trascina e si rilascia. OnPointerDown OnDrag Poiché e , ognuno esegue Operate la stessa elaborazione di input, quindi creiamo un metodo separato e lo chiamiamo. OnPointerUp A questo punto, passarlo al metodo per notificare SendValueToControl Vector2.zero al controllo che ha smesso di digitare.

/// <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 Il metodo è l'operazione di input D-pad principale.

Prima di tutto, la posizione del tocco può essere ottenuta con , ma poiché questa coordinata è la eventData.position coordinata della schermata di gioco, RectTransformUtility.ScreenPointToLocalPointInRectangle Metodo per convertire in coordinate canvas. I valori da passare sono "canvas", "touch position", "camera" e " RectTransformreceiving variable".

Dopo aver ottenuto la "posizione touch sulla tela", convertila nella posizione del pulsante come origine. Basta sottrarre la posizione () dell'oggetto_objectPosition.

Successivamente, poiché la posizione del tocco è ancora un numero sull'area di lavoro, converti questo valore in un rapporto di 0 ~ 1. Se dividi per metà la dimensione del pulsante, la posizione del tocco all'interno dell'intervallo del pulsante sarà 0 ~ 1. Se trascini con il tocco, il valore sarà 1 o più perché accetta operazioni anche al di fuori del pulsante. Vector2.ClampMagnitude Fallo nel metodo in modo che non superi 1.

Infine, passare il valore SendValueToControl di input convertito in 0 ~ 1 al metodo. Il controllo su schermo fa il resto per te.

/// <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);
}

Allegare lo script creato al pulsante. Questa azione è impostata per essere trattata come l'operazione della levetta sinistra del Gamepad.

Prova a spostare il gioco e tocca i pulsanti. Penso che tu possa vedere che il valore ottenuto cambia a seconda della posizione del tocco. Inoltre, puoi vedere che anche se trascini mentre tocchi, il valore cambia a seconda della posizione di trascinamento.