Sesuaikan Kawalan Pada Skrin untuk melaksanakan pad D

Laman dikemaskini :
Tarikh penciptaan halaman :

Persekitaran pengesahan

Windows
  • Windows 11
Penyunting Perpaduan
  • 2020.3.25f1
Pakej Sistem Input
  • 1.2.0

Prasyarat untuk petua ini

Tetapan berikut telah dibuat terlebih dahulu sebagai premis untuk penerangan petua ini.

Anda juga harus biasa dengan petua berikut:

On-Screen Control Stick ialah operasi orientasi standard

Tongkat Pada Skrin dilaksanakan sebagai tongkat maya, meniru operasi tongkat yang serupa dengan yang terdapat pada pengawal fizikal. Sebagai contoh, jika anda ingin mengalihkannya ke kanan, anda boleh menyentuh skrin terlebih dahulu untuk menyentuh tongkat, dan kemudian luncurkannya ke kanan untuk mengetuk tongkat ke bawah.

Ia mudah dibayangkan sebagai operasi kayu, tetapi sebaliknya, apabila anda ingin memindahkannya ke kanan dengan serta-merta, (1) sentuh, (2) slaid ke kanan, Oleh kerana ia memerlukan operasi dua langkah, tindak balas pasti akan sedikit tertangguh. Ini benar terutamanya apabila anda perlu membuat perubahan arah yang cepat berturut-turut.

D-pad yang menentukan arah pada masa sentuhan (tidak standard)

Kaedah input terbaik untuk input arah cepat ialah kaedah yang boleh menentukan arah pada saat menyentuh dengan meletakkan sesuatu seperti pad D atau kekunci anak panah. Ia bukan dibuat oleh Unity, tetapi dalam permainan saya Little Saber, saya meletakkan kekunci anak panah supaya anda boleh bergerak dengan cepat ke arah yang anda sentuh. Sudah tentu, anda juga boleh meluncur ke atas atau ke kiri semasa menyentuh untuk menukar pergerakan.

Walau bagaimanapun, standard Kawalan Pada Skrin tidak mudah diletakkan dan dilaksanakan kerana hanya terdapat Tongkat Pada Skrin dan Butang Pada Skrin.

Anda juga boleh membuat pseudo-D-pad dengan mengatur empat butang seperti yang ditunjukkan dalam gambar di bawah, tetapi ia menyusahkan kerana anda tidak boleh memasukkan secara menyerong. Sekiranya anda meletakkan 8 butang, operasi pepenjuru mungkin, tetapi operasi arah aliran seperti "← ↙ ↓" masih tidak mungkin.

Cipta pad arah dengan penyesuaian Kawalan Pada Skrin

Seperti yang anda lihat, Kawalan Pada Skrin hanya datang standard dengan Stick and Button, tetapi anda boleh menyesuaikan ciri yang hilang dengan skrip anda sendiri. Jadi di sini saya ingin mencipta pad arah dengan penyesuaian Kawalan Pada Skrin.

Peta Tindakan

Kami akan menggunakan peta tindakan, tetapi kami akan menggunakan yang dibuat dalam petua sebelumnya seperti itu, jadi prosedur itu ditinggalkan.

Anda juga telah menulis beberapa kod.

Meletakkan objek

Letakkan kawasan teks untuk memaparkan input anda dan butang yang menggantikan pad D. Dalam kes ini, butang diatur, tetapi anda boleh menggantikannya dengan imej yang lebih mudah difahami.

Skrip untuk memaparkan input

Kerana Kawalan Pada Skrin menggantikan sentuhan dengan interaksi pengawal fizikal, Cipta skrip yang memaparkan input anda dalam teks apabila peta tindakan berfungsi.

Kandungannya sama seperti sebelumnya, jadi saya akan meninggalkan 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}";
  }
}

Selepas menyediakannya, mula-mula semak sama ada ia berfungsi dengan papan kekunci atau pad permainan anda.

Menyesuaikan Kawalan Pada Skrin

Ini membawa kita ke topik utama petua ini. Menyesuaikan Kawalan Pada Skrin ialah skrip, jadi mula-mula buat skrip. Nama itu sewenang-wenangnya, tetapi dalam kes OnScreenDpad ini ia adalah .

Skrip kelihatan 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 mencipta OnScreenControl kelas penyesuaian Kawalan Pada Skrin dengan mewarisi daripada kelas Sesuaikan. Di samping itu, untuk menerima pelbagai acara sentuhan, mewarisi antara muka sasaran. Di sini, "apabila menyentuh", "apabila bergerak semasa menyentuh", "apabila sentuhan dikeluarkan", dan "sebelum menyeret" diproses, jadi antara muka berikut juga diterangkan masing-masing.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Kandungan Antara Muka
IPointerDownHandler Apabila disentuh
IPointerUpHandler Apabila anda melepaskan sentuhan
IDragHandler Apabila anda bergerak sambil menyentuh
IInitializePotentialDragHandler Sebelum anda mula menyeret

Medan berikut diisytiharkan:

controlPathInternalOnScreenControl diperlukan jika anda mewarisi dari kelas. Ia memegang rentetan butang mana peranti input untuk dipetakan, tetapi pada dasarnya anda boleh menulisnya seperti sedia ada. InputControl Walau bagaimanapun, atribut harus mengandungi nilai yang akan dikekalkan (Vector2 dalam kes ini).

_objectPosition_objectSizeHalf dan tahan separuh kedudukan dan saiz butang terlebih dahulu dan gunakannya kemudian untuk pengiraan.

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

Kaedah yang dipanggil Start selepas objek dimulakan, mendapat separuh kedudukan dan saiz butang pada kanvas. Saiz juga mengambil kira skala. Start Ia diproses dengan kaedah, tetapi jika ia boleh digunakan dengan betul dalam pengiraan pada akhirnya, masa pemerolehan boleh berada di mana-mana sahaja.

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

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

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

OnInitializePotentialDrag dipanggil apabila anda ingin mula menyeret selepas sentuhan. Semasa menyeret, OnDrag kaedah dipanggil. Ambang ditetapkan untuk mengelakkan penghakiman seretan sedikit sebanyak supaya jari tidak bergerak sedikit dan penghakiman seretan tidak berlaku kerana operasi hanya menyentuh.

Kali ini, kerana ia adalah input yang menganggap bahawa anda ingin melakukan operasi penalaan halus, lumpuhkan tetapan ambang ini dan segera membuat penghakiman seretan. eventData.useDragThreshold false Anda boleh mengatasi ambang dengan menetapkan kepada .

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

Berikut ialah peristiwa yang dipanggil apabila menyentuh, menyeret dan melepaskan masing-masing. OnPointerDown OnDrag Sejak dan , masing-masing Operate melakukan pemprosesan input yang sama, jadi kami membuat kaedah yang berasingan dan memanggilnya. OnPointerUp Sekarang, serahkan kepada kaedah SendValueToControl Vector2.zero untuk memberitahu kawalan bahawa ia telah berhenti menaip.

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

Pertama sekali, kedudukan sentuhan boleh didapati dengan , tetapi kerana koordinat ini adalah koordinat eventData.position skrin permainan, RectTransformUtility.ScreenPointToLocalPointInRectangle kaedah untuk menukar kepada koordinat kanvas. Nilai yang akan diluluskan ialah "kanvas", "kedudukan sentuh", "kamera", dan "menerima pembolehubah" RectTransform.

Selepas mendapat "kedudukan sentuh pada kanvas", tukarkannya ke kedudukan butang sebagai asal. Hanya tolak kedudukan ()_objectPosition objek.

Seterusnya, kerana kedudukan sentuhan masih merupakan nombor pada kanvas, tukar nilai ini kepada nisbah 0 ~ 1. Jika anda membahagikan dengan separuh saiz butang, kedudukan sentuh dalam julat butang akan menjadi 0 ~ 1. Jika anda menyeret dengan sentuhan, nilainya akan menjadi 1 atau lebih kerana ia menerima operasi walaupun di luar butang. Vector2.ClampMagnitude Lakukan ini dalam kaedah supaya ia tidak melebihi 1.

Akhirnya, lulus nilai SendValueToControl input yang ditukar kepada 0 ~ 1 kepada kaedah. Kawalan Pada Skrin melakukan selebihnya 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 pada butang. Tindakan ini ditetapkan untuk dianggap sebagai operasi tongkat kiri Gamepad.

Cuba gerakkan permainan dan sentuh butang. Saya fikir anda dapat melihat bahawa nilai yang diperoleh berubah bergantung pada kedudukan sentuhan. Selain itu, anda dapat melihat bahawa walaupun anda menyeret semasa menyentuh, nilai berubah bergantung pada kedudukan seret.