Personalizați controlul de pe ecran pentru a implementa D-pad-ul

Pagina actualizată :
Data creării paginii :

Mediul de verificare

Windows
  • Ferestre 11
Unity Editor
  • 2020.3.25F1
Pachet sistem de intrare
  • 1.2.0

Cerințe preliminare pentru acest sfat

Următoarele setări au fost făcute în avans ca premisă pentru descrierea acestui sfat.

De asemenea, trebuie să cunoașteți următoarele sfaturi:

Control Stick-ul de pe ecran este operația standard de orientare

On-Screen Stick este implementat ca un stick virtual, replicând funcționarea unui stick similar cu cel găsit pe un controler fizic. De exemplu, dacă doriți să îl mutați spre dreapta, puteți atinge mai întâi ecranul pentru a atinge stickul, apoi glisați-l spre dreapta pentru a bate stick-ul în jos.

Este ușor de imaginat ca o operație de băț, dar dimpotrivă, atunci când doriți să o mutați imediat spre dreapta, (1) atingeți, (2) glisați spre dreapta, Deoarece necesită o operație în două etape, răspunsul va fi în mod inevitabil puțin întârziat. Acest lucru este valabil mai ales atunci când trebuie să faceți o schimbare rapidă de direcție la rând.

D-pad care determină direcția în momentul atingerii (non-standard)

Cea mai bună metodă de introducere pentru introducerea rapidă a direcției este una care poate determina direcția în momentul atingerii prin plasarea a ceva de genul unui D-pad sau a unei taste săgeată. Nu este făcut de Unity, dar în jocul meu Little Saber, plasez tastele săgeată astfel încât să vă puteți deplasa rapid în direcția pe care o atingeți. Desigur, puteți, de asemenea, să glisați în sus sau spre stânga în timp ce atingeți pentru a comuta mișcările.

Cu toate acestea, standardul de control pe ecran nu este ușor de plasat și implementat, deoarece există doar stick-uri pe ecran și butoane pe ecran.

De asemenea, puteți crea un pseudo-D-pad aranjând patru butoane așa cum se arată în figura de mai jos, dar este incomod deoarece nu puteți introduce în diagonală. Dacă plasați 8 butoane, este posibilă funcționarea diagonală, dar funcționarea direcției de curgere, cum ar fi "← ↙ ↓", nu este încă posibilă.

Crearea unui pad direcțional cu personalizarea Control pe ecran

După cum puteți vedea, controlul pe ecran vine standard numai cu Stick și Button, dar puteți personaliza caracteristicile lipsă cu propriile scripturi. Deci, aici aș dori să creez un pad direcțional cu personalizarea controlului pe ecran.

Harta de acțiune

Vom folosi harta acțiunii, dar o vom folosi pe cea creată în sfaturile anterioare așa cum este, astfel încât procedura este omisă.

Ai scris și un cod.

Poziționarea obiectelor

Plasați o zonă de text pentru a afișa intrarea și un buton care înlocuiește D-pad-ul. În acest caz, butoanele sunt aranjate, dar le puteți înlocui cu o imagine mai ușor de înțeles.

Script pentru afișarea intrărilor

Deoarece Controlul vizual înlocuiește atingerea cu interacțiunea fizică a controlerului, Creați un script care afișează intrarea în text pe măsură ce funcționează harta de acțiune.

Conținutul este același ca înainte, așa că voi omite explicația.

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

După configurare, verificați mai întâi dacă funcționează cu tastatura sau gamepadul.

Personalizarea comenzilor de pe ecran

Acest lucru ne aduce la subiectul principal al acestor sfaturi. Personalizarea controlului pe ecran este un script, deci mai întâi creați un script. Numele este arbitrar, dar în acest caz OnScreenDpad este .

Scenariul arată astfel:

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

Creați o OnScreenControl clasă de particularizare Control vizual moștenind de la clasa Particularizare. În plus, pentru a primi diverse evenimente tactile, moșteniți interfața țintă. Aici, sunt procesate "când atingeți", "când vă deplasați în timp ce atingeți", "când atingeți eliberarea" și "înainte de tragere", astfel încât sunt descrise și următoarele interfețe.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Conținutul interfeței
IPointerDownHandler Când este atins
IPointerUpHandler Când eliberați atingerea
IDragHandler Când vă mișcați în timp ce atingeți
IInitializePotentialDragHandler Înainte de a începe să trageți

Se declară următoarele câmpuri:

controlPathInternalOnScreenControl este necesar dacă moșteniți de la o clasă. Acesta deține un șir de buton al dispozitivului de intrare la care să mapați, dar practic îl puteți scrie așa cum este. InputControl Cu toate acestea, atributul trebuie să conțină valoarea care trebuie păstrată (Vector2 în acest caz).

_objectPosition_objectSizeHalf și țineți apăsată jumătate din poziția și dimensiunea butonului în avans și utilizați-l mai târziu pentru calcule.

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

Metoda, numită Start după inițializarea obiectului, obține jumătate din poziția și dimensiunea butonului de pe pânză. Dimensiunea ia în considerare și scara. Start Este procesat printr-o metodă, dar dacă poate fi utilizat corect în calcul în cele din urmă, momentul achiziției poate fi oriunde.

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

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

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

OnInitializePotentialDrag este apelat atunci când doriți să începeți să trageți după atingere. În timp ce trageți, OnDrag se numește metoda. Un prag este setat pentru a preveni judecata de tragere într-o oarecare măsură, astfel încât degetul să nu se miște puțin și judecata de tragere să nu apară din cauza operației de simplă atingere.

De data aceasta, deoarece este o intrare care presupune că doriți să efectuați o operație de reglare fină, dezactivați această setare de prag și faceți imediat o judecată de tragere. eventData.useDragThreshold false Puteți suprascrie pragul setând la .

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

Mai jos sunt evenimentele apelate la atingere, tragere și, respectiv, eliberare. OnPointerDown OnDrag Deoarece și , fiecare efectuează Operate aceeași procesare de intrare, așa că creăm o metodă separată și o numim. OnPointerUp Acum, treceți-l la metodă pentru a SendValueToControl Vector2.zero notifica controlul că a încetat să tasteze.

/// <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 Metoda este operația de intrare D-pad de bază.

În primul rând, poziția tactilă poate fi obținută cu , dar din moment ce această coordonată este coordonata ecranului de eventData.position joc, RectTransformUtility.ScreenPointToLocalPointInRectangle Metoda de conversie în coordonate de pânză. Valorile care trebuie transmise sunt "canvas", "touch position", "camera" și " RectTransformreceiving variable".

După obținerea "poziției tactile pe pânză", convertiți-o în poziția butonului ca origine. Doar scădeți poziția () obiectului_objectPosition.

Apoi, deoarece poziția tactilă este încă un număr pe pânză, convertiți această valoare într-un raport de 0 ~ 1. Dacă împărțiți la jumătate dimensiunea butonului, poziția tactilă în intervalul butonului va fi 0 ~ 1. Dacă trageți prin atingere, valoarea va fi 1 sau mai mult, deoarece acceptă operații chiar și în afara butonului. Vector2.ClampMagnitude Faceți acest lucru în metodă, astfel încât să nu depășească 1.

În cele din urmă, treceți valoarea SendValueToControl de intrare convertită la 0 ~ 1 la metodă. Controlul de pe ecran face restul pentru dvs.

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

Atașați scriptul pe care l-ați creat la buton. Această acțiune este setată să fie tratată ca funcționarea stick-ului stâng al gamepadului.

Încercați să mutați jocul și atingeți butoanele. Cred că puteți vedea că valoarea obținută se modifică în funcție de poziția tactilă. De asemenea, puteți vedea că, chiar dacă trageți în timp ce atingeți, valoarea se modifică în funcție de poziția de tragere.