D-pad'i uygulamak için Ekran Denetimi'ni özelleştirme

Sayfa güncel :
Sayfa oluşturma tarihi :

Doğrulama ortamı

Windows
  • Pencereler 11
Birlik Editörü
  • 2020.3.25f1
Giriş Sistemi Paketi
  • 1.2.0

Bu ipucu için önkoşullar

Aşağıdaki ayarlar, bu ipucunun açıklaması için bir öncül olarak önceden yapılmıştır.

Aşağıdaki ipuçlarına da aşina olmalısınız:

Ekran Kontrol Çubuğu standart yönlendirme işlemidir

Ekran Çubuğu, fiziksel bir denetleyicide bulunana benzer bir çubuğun çalışmasını çoğaltan sanal bir çubuk olarak uygulanır. Örneğin, sağa taşımak isterseniz, önce çubuğa dokunmak için ekrana dokunabilir ve ardından çubuğu aşağı indirmek için sağa kaydırabilirsiniz.

Bir çubuk işlemi olarak hayal etmek kolaydır, ancak tam tersine, hemen sağa hareket ettirmek istediğinizde, (1) dokunmak, (2) sağa kaydırmak, İki aşamalı bir işlem gerektirdiğinden, yanıt kaçınılmaz olarak biraz gecikecektir. Bu, özellikle arka arkaya hızlı bir yön değişikliği yapmanız gerektiğinde geçerlidir.

Dokunma anında yönü belirleyen yön düğmesi (standart dışı)

Hızlı yön girişi için en iyi giriş yöntemi, D-pad veya ok tuşu gibi bir şey yerleştirerek dokunma anındaki yönü belirleyebilen yöntemdir. Unity tarafından yapılmadı, ancak oyunum Little Saber'da, dokunduğunuz yönde hızlı bir şekilde hareket edebilmeniz için ok tuşlarını yerleştiriyorum. Tabii ki, hareketleri değiştirmek için dokunurken yukarı veya sola da kaydırabilirsiniz.

Bununla birlikte, Ekran Kontrolü standardının yerleştirilmesi ve uygulanması kolay değildir, çünkü yalnızca Ekran Çubukları ve Ekran Düğmeleri vardır.

Aşağıdaki şekilde gösterildiği gibi dört düğme düzenleyerek sahte bir D-pad de oluşturabilirsiniz, ancak çapraz olarak giremeyeceğiniz için sakıncalıdır. 8 düğme yerleştirirseniz, diyagonal işlem mümkündür, ancak "← ↙ ↓" gibi akış yönü işlemi hala mümkün değildir.

Ekran Kontrolü özelleştirmesiyle yön düğmesi oluşturma

Gördüğünüz gibi, Ekran Kontrolü yalnızca Çubuk ve Düğme ile standart olarak gelir, ancak eksik özellikleri kendi komut dosyalarınızla özelleştirebilirsiniz. Bu yüzden burada Ekran Kontrolü özelleştirmesi ile bir yön pedi oluşturmak istiyorum.

Eylem Haritası

Eylem haritasını kullanacağız, ancak önceki ipuçlarında oluşturulanı olduğu gibi kullanacağız, bu nedenle prosedür ihmal edildi.

Ayrıca bazı kodlar da yazdınız.

Nesneleri Konumlandırma

Girişinizi görüntülemek için bir metin alanı ve yön düğmesinin yerini alan bir düğme yerleştirin. Bu durumda, düğmeler düzenlenir, ancak bunları anlaşılması daha kolay bir görüntüyle değiştirebilirsiniz.

Girişi görüntülemek için komut dosyası

Ekran Kontrolü, dokunmayı fiziksel denetleyici etkileşimiyle değiştirdiğinden, Eylem haritası çalışırken girişinizi metin olarak görüntüleyen bir komut dosyası oluşturun.

İçerik öncekiyle aynı, bu yüzden açıklamayı atlayacağım.

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

Ayarladıktan sonra, önce klavyenizle veya gamepad'inizle çalışıp çalışmadığını kontrol edin.

Ekran Kontrollerini Özelleştirme

Bu bizi bu ipuçlarının ana konusuna getiriyor. Ekran Denetimi'ni özelleştirmek bir komut dosyasıdır, bu nedenle önce bir komut dosyası oluşturun. Adı keyfidir, ancak bu durumda OnScreenDpad .

Komut dosyası şöyle görünür:

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

Customize sınıfından devralarak bir OnScreenControl Ekran Denetimi özelleştirme sınıfı oluşturursunuz. Ek olarak, çeşitli dokunma olaylarını almak için hedef arayüzü devralın. Burada, "dokunurken", "dokunurken hareket ederken", "dokunma serbest bırakıldığında" ve "sürüklemeden önce" işlenir, bu nedenle sırasıyla aşağıdaki arayüzler de açıklanmaktadır.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Arayüz İçeriği
IPointerDownHandler Dokunulduğunda
IPointerUpHandler Dokunmayı bıraktığınızda
IDragHandler Dokunurken hareket ettiğinizde
IInitializePotentialDragHandler Sürüklemeye başlamadan önce

Aşağıdaki alanlar bildirilir:

controlPathInternalOnScreenControl bir sınıftan devralıyorsanız gereklidir. Giriş cihazının hangi düğmesiyle eşleneceğinin bir dizesini tutar, ancak temel olarak olduğu gibi yazabilirsiniz. InputControl Ancak, öznitelik korunacak değeri içermelidir (bu durumda Vektör2).

_objectPosition_objectSizeHalf ve düğmenin konumunu ve boyutunu önceden basılı tutun ve daha sonra hesaplamalar için kullanın.

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

Nesne başlatıldıktan sonra çağrılan Start yöntem, tuval üzerindeki düğmenin konumunun ve boyutunun yarısını alır. Boyut ayrıca ölçeği de dikkate alır. Start Bir yöntemle işlenir, ancak sonunda hesaplamada doğru şekilde kullanılabilirse, satın alma zamanlaması herhangi bir yerde olabilir.

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

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

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

OnInitializePotentialDrag dokunduktan sonra sürüklemeye başlamak istediğinizde çağrılır. Sürüklerken, OnDrag yöntem çağrılır. Sürükleme kararını bir dereceye kadar önlemek için bir eşik ayarlanır, böylece parmak biraz hareket etmez ve sürükleme yargısı sadece dokunma işlemi nedeniyle gerçekleşmez.

Bu kez, ince ayar işlemi gerçekleştirmek istediğinizi varsayan bir giriş olduğundan, bu eşik ayarını devre dışı bırakın ve hemen bir sürükleme kararı verin. eventData.useDragThreshold false olarak ayarlayarak eşiği geçersiz kılabilirsiniz.

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

Aşağıda, sırasıyla dokunma, sürükleme ve serbest bırakma sırasında çağrılan olaylar verilmiştir. OnPointerDown OnDrag Çünkü ve , her biri aynı giriş işlemesini gerçekleştirir Operate , bu yüzden ayrı bir yöntem oluşturur ve çağırırız. OnPointerUp Şimdi, denetime yazmayı bıraktığını bildirmek için SendValueToControl Vector2.zero yönteme geçirin.

/// <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 Yöntem, çekirdek D-pad giriş işlemidir.

Her şeyden önce, dokunma pozisyonu , ile elde edilebilir, ancak bu koordinat oyun ekranının eventData.position koordinatı olduğundan, RectTransformUtility.ScreenPointToLocalPointInRectangle tuval koordinatlarına dönüştürme yöntemi. İletilecek değerler "tuval", "dokunma konumu", "kamera" ve "alıcı değişken" RectTransformdir.

"Tuval üzerindeki dokunma konumunu" aldıktan sonra, düğmenin orijinal konumuna dönüştürün. Sadece nesnenin konumunu ()_objectPosition çıkarın.

Ardından, dokunma konumu hala tuval üzerinde bir sayı olduğundan, bu değeri 0 ~ 1 oranına dönüştürün. Düğmenin boyutunu yarıya bölerseniz, düğme aralığındaki dokunma konumu 0 ~ 1 olacaktır. Dokunarak sürüklerseniz, düğmenin dışında bile işlemleri kabul ettiği için değer 1 veya daha fazla olacaktır. Vector2.ClampMagnitude Bunu yöntemde, 1'i geçmeyecek şekilde yapın.

Son olarak, 0 ~ 1'e dönüştürülen giriş değerini SendValueToControl yönteme geçirin. Ekran Denetimi gerisini sizin yerinize halleder.

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

Oluşturduğunuz komut dosyasını düğmeye ekleyin. Bu eylem, Gamepad'in sol çubuğunun çalışması olarak kabul edilecek şekilde ayarlanmıştır.

Oyunu hareket ettirmeyi deneyin ve düğmelere dokunun. Sanırım elde edilen değerin dokunma pozisyonuna bağlı olarak değiştiğini görebilirsiniz. Ayrıca, dokunurken sürükleseniz bile, değerin sürükleme konumuna bağlı olarak değiştiğini görebilirsiniz.