Sesuaikan Kontrol Di Layar untuk menerapkan D-pad

Halaman Diperbarui :
Tanggal pembuatan halaman :

Lingkungan verifikasi

Windows
  • jendela 11
Editor Kesatuan
  • 25f1/3/2020
Paket Sistem Input
  • 1.2.0

Prasyarat untuk tip ini

Pengaturan berikut telah dibuat sebelumnya sebagai premis untuk deskripsi tip ini.

Anda juga harus terbiasa dengan tips berikut:

On-Screen Control Stick adalah operasi orientasi standar

On-Screen Stick diimplementasikan sebagai tongkat virtual, mereplikasi pengoperasian tongkat yang mirip dengan yang ditemukan pada pengontrol fisik. Misalnya, jika Anda ingin memindahkannya ke kanan, pertama-tama Anda dapat menyentuh layar untuk menyentuh tongkat, lalu menggesernya ke kanan untuk menjatuhkan tongkat.

Mudah dibayangkan sebagai operasi tongkat, tetapi sebaliknya, ketika Anda ingin segera memindahkannya ke kanan, (1) sentuh, (2) geser ke kanan, Karena membutuhkan operasi dua langkah, responsnya pasti akan sedikit tertunda. Ini terutama benar ketika Anda perlu membuat perubahan arah yang cepat berturut-turut.

D-pad yang menentukan arah pada saat disentuh (tidak standar)

Metode input terbaik untuk input arah cepat adalah metode yang dapat menentukan arah pada saat menyentuh dengan menempatkan sesuatu seperti D-pad atau tombol panah. Itu tidak dibuat oleh Unity, tetapi dalam permainan saya Little Saber, saya menempatkan tombol panah sehingga Anda dapat dengan cepat bergerak ke arah yang Anda sentuh. Tentu saja, Anda juga dapat menggeser ke atas atau ke kiri sambil menyentuh untuk beralih gerakan.

Namun, standar On-Screen Control tidak mudah ditempatkan dan diterapkan karena hanya ada On-Screen Sticks dan On-Screen Buttons.

Anda juga dapat membuat pseudo-D-pad dengan mengatur empat tombol seperti yang ditunjukkan pada gambar di bawah ini, tetapi tidak nyaman karena Anda tidak dapat memasukkan secara diagonal. Jika Anda menempatkan 8 tombol, operasi diagonal dimungkinkan, tetapi operasi arah aliran seperti "← ↙ ↓" masih belum memungkinkan.

Membuat directional pad dengan kustomisasi Kontrol Di Layar

Seperti yang Anda lihat, On-Screen Control hanya hadir standar dengan Stick and Button, tetapi Anda dapat menyesuaikan fitur yang hilang dengan skrip Anda sendiri. Jadi di sini saya ingin membuat directional pad dengan kustomisasi On-Screen Control.

Peta Tindakan

Kami akan menggunakan peta tindakan, tetapi kami akan menggunakan yang dibuat di tips sebelumnya apa adanya, sehingga prosedurnya dihilangkan.

Anda juga telah menulis beberapa kode.

Objek Pemosisian

Tempatkan area teks untuk menampilkan input Anda dan tombol yang menggantikan D-pad. Dalam hal ini, tombol diatur, tetapi Anda dapat menggantinya dengan gambar yang lebih mudah dipahami.

Skrip untuk menampilkan input

Karena On-Screen Control menggantikan sentuhan dengan interaksi pengontrol fisik, Buat skrip yang menampilkan input Anda dalam teks saat peta tindakan berfungsi.

Isinya sama seperti sebelumnya, jadi saya akan menghilangkan penjelasannya.

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

Setelah mengaturnya, periksa terlebih dahulu apakah itu berfungsi dengan keyboard atau gamepad kamu.

Menyesuaikan Kontrol di Layar

Ini membawa kita ke topik utama tips ini. Menyesuaikan Kontrol Di Layar adalah skrip, jadi buat skrip terlebih dahulu. Namanya sewenang-wenang, tetapi dalam kasus OnScreenDpad ini adalah .

Skripnya terlihat seperti ini:

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

Anda membuat OnScreenControl kelas kustomisasi Kontrol Layar dengan mewarisi dari kelas Kustomisasi. Selain itu, untuk menerima berbagai peristiwa sentuh, mewarisi antarmuka target. Di sini, "saat menyentuh", "saat bergerak sambil menyentuh", "saat sentuhan dilepaskan", dan "sebelum menyeret" diproses, sehingga antarmuka berikut juga dijelaskan masing-masing.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Konten Antarmuka
IPointerDownHandler Saat disentuh
IPointerUpHandler Saat Anda melepaskan sentuhan
IDragHandler Saat Anda bergerak sambil menyentuh
IInitializePotentialDragHandler Sebelum Anda mulai menyeret

Bidang berikut dideklarasikan:

controlPathInternalOnScreenControl diperlukan jika Anda mewarisi dari kelas. Ini memegang string tombol mana dari perangkat input yang akan dipetakan, tetapi pada dasarnya Anda dapat menulisnya apa adanya. InputControl Namun, atribut harus berisi nilai yang akan dipertahankan (Vektor2 dalam hal ini).

_objectPosition_objectSizeHalf dan tahan setengah posisi dan ukuran tombol terlebih dahulu dan gunakan nanti untuk perhitungan.

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

Metode, dipanggil Start setelah objek diinisialisasi, mendapat setengah posisi dan ukuran tombol pada kanvas. Ukurannya juga memperhitungkan skala. Start Ini diproses dengan metode, tetapi jika dapat digunakan dengan benar dalam perhitungan pada akhirnya, waktu akuisisi bisa di mana saja.

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

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

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

OnInitializePotentialDrag dipanggil saat Anda ingin mulai menyeret setelah disentuh. Saat menyeret, OnDrag metode ini disebut. Ambang batas ditetapkan untuk mencegah penilaian seret sampai batas tertentu sehingga jari tidak bergerak sedikit dan penilaian seret tidak terjadi karena operasi hanya menyentuh.

Kali ini, karena ini adalah input yang mengasumsikan bahwa Anda ingin melakukan operasi fine-tuning, nonaktifkan pengaturan ambang batas ini dan segera buat penilaian drag. eventData.useDragThreshold false Anda dapat mengganti ambang batas dengan menyetel ke .

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

Di bawah ini adalah peristiwa yang disebut saat menyentuh, menyeret, dan melepaskan masing-masing. OnPointerDown OnDrag Karena dan , masing-masing melakukan Operate pemrosesan input yang sama, jadi kami membuat metode terpisah dan menyebutnya. OnPointerUp Sekarang, berikan ke metode untuk SendValueToControl Vector2.zero memberi tahu kontrol bahwa ia telah berhenti mengetik.

/// <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 Metode ini adalah operasi input D-pad inti.

Pertama-tama, posisi sentuh dapat diperoleh dengan , tetapi karena koordinat ini adalah koordinat eventData.position layar game, RectTransformUtility.ScreenPointToLocalPointInRectangle metode untuk mengonversi ke koordinat kanvas. Nilai yang akan diteruskan adalah "canvas", "touch position", "camera", dan "receiving variable" RectTransform.

Setelah mendapatkan "posisi sentuh pada kanvas", ubah ke posisi tombol sebagai asalnya. Cukup kurangi posisi ()_objectPosition objek.

Selanjutnya, karena posisi sentuh masih berupa angka di kanvas, ubah nilai ini menjadi rasio 0 ~ 1. Jika Anda membagi setengah ukuran tombol, posisi sentuh dalam rentang tombol akan menjadi 0 ~ 1. Jika Anda menyeret dengan sentuhan, nilainya akan menjadi 1 atau lebih karena menerima operasi bahkan di luar tombol. Vector2.ClampMagnitude Lakukan ini dalam metode agar tidak melebihi 1.

Terakhir, teruskan nilai SendValueToControl input yang dikonversi ke 0 ~ 1 ke metode. Kontrol Di Layar melakukan sisanya untuk Anda.

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

Lampirkan skrip yang Anda buat ke tombol. Tindakan ini diatur untuk diperlakukan sebagai pengoperasian tongkat kiri Gamepad.

Coba gerakkan game dan sentuh tombolnya. Saya pikir Anda dapat melihat bahwa nilai yang diperoleh berubah tergantung pada posisi sentuh. Selain itu, Anda dapat melihat bahwa meskipun Anda menyeret sambil menyentuh, nilainya berubah tergantung pada posisi seret.