D-पैड को कार्यान्वित करने के लिए ऑन-स्क्रीन नियंत्रण अनुकूलित करें

पेज अद्यतन :
पेज निर्माण की तारीख :

सत्यापन वातावरण

विंडोज़
  • विंडोज 11
एकता संपादक
  • 2020.3.25f1
इनपुट सिस्टम पैकेज
  • 1.2.0

इस टिप के लिए आवश्यक शर्तें

इस टिप के विवरण के लिए एक आधार के रूप में निम्नलिखित सेटिंग्स पहले से बनाई गई हैं।

आपको निम्नलिखित युक्तियों से भी परिचित होना चाहिए:

ऑन-स्क्रीन कंट्रोल स्टिक मानक अभिविन्यास ऑपरेशन है

ऑन-स्क्रीन स्टिक को एक वर्चुअल स्टिक के रूप में लागू किया जाता है, जो भौतिक नियंत्रक पर पाए जाने वाले स्टिक के संचालन की नकल करता है। उदाहरण के लिए, यदि आप इसे दाईं ओर ले जाना चाहते हैं, तो आप पहले स्टिक को छूने के लिए स्क्रीन को स्पर्श कर सकते हैं, और फिर स्टिक को नीचे खटखटाने के लिए इसे दाईं ओर स्लाइड कर सकते हैं।

स्टिक ऑपरेशन के रूप में कल्पना करना आसान है, लेकिन इसके विपरीत, जब आप इसे तुरंत दाईं ओर ले जाना चाहते हैं, तो (1) स्पर्श, (2) दाईं ओर स्लाइड, चूंकि इसके लिए दो-चरणीय ऑपरेशन की आवश्यकता होती है, इसलिए प्रतिक्रिया अनिवार्य रूप से थोड़ी देरी होगी। यह विशेष रूप से सच है जब आपको एक पंक्ति में दिशा का त्वरित परिवर्तन करने की आवश्यकता होती है।

डी-पैड जो स्पर्श के क्षण में दिशा निर्धारित करता है (गैर-मानक)

त्वरित दिशा इनपुट के लिए सबसे अच्छी इनपुट विधि वह है जो डी-पैड या तीर कुंजी जैसी किसी चीज को रखकर स्पर्श के समय दिशा निर्धारित कर सकती है। यह एकता द्वारा नहीं बनाया गया है, लेकिन मेरे गेम लिटिल सबर में, मैं तीर कुंजी रखता हूं ताकि आप जल्दी से उस दिशा में आगे बढ़ सकें जिसे आप छूते हैं। बेशक, आप स्विच आंदोलनों को छूने के दौरान ऊपर या बाएं स्लाइड भी कर सकते हैं।

हालांकि, ऑन-स्क्रीन नियंत्रण मानक को रखना और लागू करना आसान नहीं है क्योंकि केवल ऑन-स्क्रीन स्टिक और ऑन-स्क्रीन बटन हैं।

आप नीचे दिए गए आंकड़े में दिखाए गए अनुसार चार बटन व्यवस्थित करके एक छद्म-डी-पैड भी बना सकते हैं, लेकिन यह असुविधाजनक है क्योंकि आप विकर्ण रूप से इनपुट नहीं कर सकते हैं। यदि आप 8 बटन रखते हैं, तो विकर्ण ऑपरेशन संभव है, लेकिन प्रवाह दिशा ऑपरेशन जैसे "← ↙ " अभी भी संभव नहीं है।

ऑन-स्क्रीन नियंत्रण अनुकूलन के साथ एक दिशात्मक पैड बनाएँ

जैसा कि आप देख सकते हैं, ऑन-स्क्रीन नियंत्रण केवल स्टिक और बटन के साथ मानक आता है, लेकिन आप अपनी स्क्रिप्ट के साथ लापता सुविधाओं को अनुकूलित कर सकते हैं। तो यहां मैं ऑन-स्क्रीन कंट्रोल अनुकूलन के साथ एक दिशात्मक पैड बनाना चाहता हूं।

कार्रवाई मानचित्र

हम एक्शन मैप का उपयोग करेंगे, लेकिन हम पिछले सुझावों में बनाए गए एक का उपयोग करेंगे क्योंकि यह है, इसलिए प्रक्रिया को छोड़ दिया गया है।

आपने कुछ कोड भी लिखा है।

ऑब्जेक्ट्स की स्थिति

अपने इनपुट को प्रदर्शित करने के लिए एक टेक्स्ट क्षेत्र और डी-पैड को बदलने वाला बटन रखें। इस मामले में, बटन व्यवस्थित होते हैं, लेकिन आप उन्हें एक छवि के साथ बदल सकते हैं जो समझने में आसान है।

इनपुट प्रदर्शित करने के लिए स्क्रिप्ट

क्योंकि ऑन-स्क्रीन नियंत्रण भौतिक नियंत्रक इंटरैक्शन के साथ स्पर्श को प्रतिस्थापित करता है, एक स्क्रिप्ट बनाएं जो आपके इनपुट को टेक्स्ट में प्रदर्शित करती है क्योंकि एक्शन मैप काम करता है।

सामग्री पहले की तरह ही है, इसलिए मैं स्पष्टीकरण छोड़ दूंगा।

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

इसे सेट करने के बाद, पहले जांचें कि क्या यह आपके कीबोर्ड या गेमपैड के साथ काम करता है।

ऑन-स्क्रीन नियंत्रण अनुकूलित करना

यह हमें इन युक्तियों के मुख्य विषय पर लाता है। ऑन-स्क्रीन कंट्रोल को कस्टमाइज़ करना एक स्क्रिप्ट है, इसलिए पहले एक स्क्रिप्ट बनाएं। नाम मनमाना है, लेकिन इस मामले OnScreenDpad में यह है।

स्क्रिप्ट इस तरह दिखती है:

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

आप अनुकूलित वर्ग से विरासत में लेकर ऑन-स्क्रीन OnScreenControl नियंत्रण अनुकूलन वर्ग बनाएँ। इसके अलावा, विभिन्न स्पर्श घटनाओं को प्राप्त करने के लिए, लक्ष्य इंटरफ़ेस को विरासत में लें। यहां, "स्पर्श करते समय", "स्पर्श करते समय चलते समय", "जब स्पर्श जारी होता है", और "खींचने से पहले" संसाधित होते हैं, इसलिए निम्नलिखित इंटरफेस भी क्रमशः वर्णित हैं।

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
इंटरफ़ेस सामग्री
IPointerDownHandler छूने पर
IPointerUpHandler जब आप स्पर्श छोड़ते हैं
IDragHandler जब आप स्पर्श करते समय चलते हैं
InitilizPotentialDragHandler इससे पहले कि आप खींचना शुरू करें

निम्नलिखित फ़ील्ड घोषित किए गए हैं:

controlPathInternalOnScreenControl यदि आप एक कक्षा से विरासत में प्राप्त करते हैं तो इसकी आवश्यकता है। इसमें एक स्ट्रिंग होती है कि इनपुट डिवाइस के किस बटन को मैप करना है, लेकिन मूल रूप से आप इसे वैसे ही लिख सकते हैं जैसे यह है। InputControl हालांकि, विशेषता में बनाए रखने के लिए मान होना चाहिए (इस मामले में वेक्टर 2)।

_objectPosition_objectSizeHalf और बटन की आधी स्थिति और आकार को पहले से रखें और बाद में गणना के लिए इसका उपयोग करें।

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

ऑब्जेक्ट प्रारंभ होने के बाद कही जाने Start वाली विधि, कैनवास पर बटन की आधी स्थिति और आकार प्राप्त करती है। आकार पैमाने को भी ध्यान में रखता है। Start यह एक विधि द्वारा संसाधित किया जाता है, लेकिन अगर इसे अंत में गणना में सही ढंग से उपयोग किया जा सकता है, तो अधिग्रहण का समय कहीं भी हो सकता है।

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

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

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

OnInitializePotentialDrag इसे तब बुलाया जाता है जब आप स्पर्श के बाद खींचना शुरू करना चाहते हैं। खींचते समय, OnDrag विधि को कहा जाता है। ड्रैग जजमेंट को कुछ हद तक रोकने के लिए एक सीमा निर्धारित की जाती है ताकि उंगली थोड़ी भी न हिले और केवल छूने के ऑपरेशन के कारण ड्रैग जजमेंट न हो।

इस बार, चूंकि यह एक इनपुट है जो मानता है कि आप एक ठीक-ट्यूनिंग ऑपरेशन करना चाहते हैं, इस थ्रेशोल्ड सेटिंग को अक्षम करें और तुरंत ड्रैग निर्णय लें। eventData.useDragThreshold false आप इस पर सेट करके दहलीज को ओवरराइड कर सकते हैं.

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

नीचे क्रमशः छूने, खींचने और छोड़ने पर होने वाली घटनाएं हैं। OnPointerDown OnDrag चूंकि और, प्रत्येक एक ही इनपुट प्रोसेसिंग करता है Operate , इसलिए हम एक अलग विधि बनाते हैं और इसे कॉल करते हैं। OnPointerUp अब, इसे नियंत्रण को सूचित करने के लिए SendValueToControl Vector2.zero विधि पर पारित करें कि उसने लिखना बंद कर दिया है।

/// <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 विधि कोर डी-पैड इनपुट ऑपरेशन है।

सबसे पहले, स्पर्श की स्थिति के साथ प्राप्त किया जा सकता है, लेकिन चूंकि यह समन्वय गेम स्क्रीन का eventData.position समन्वय है, RectTransformUtility.ScreenPointToLocalPointInRectangle कैनवास निर्देशांक में कनवर्ट करने की विधि. पारित किए जाने वाले मान "कैनवास", "स्पर्श स्थिति", "कैमरा" और "चर प्राप्त करना" RectTransform हैं।

"कैनवास पर स्पर्श स्थिति" प्राप्त करने के बाद, इसे मूल के रूप में बटन की स्थिति में परिवर्तित करें। बस ऑब्जेक्ट की स्थिति () को_objectPosition घटाएं।

इसके बाद, चूंकि स्पर्श की स्थिति अभी भी कैनवास पर एक संख्या है, इसलिए इस मान को 0 ~ 1 के अनुपात में परिवर्तित करें। यदि आप बटन के आधे आकार से विभाजित करते हैं, तो बटन की सीमा के भीतर स्पर्श की स्थिति 0 ~ 1 होगी। यदि आप स्पर्श के साथ खींचते हैं, तो मान 1 या अधिक होगा क्योंकि यह बटन के बाहर भी संचालन स्वीकार करता है। Vector2.ClampMagnitude विधि में ऐसा करें ताकि यह 1 से अधिक न हो।

अंत में, इनपुट मान SendValueToControl को 0 ~ 1 में परिवर्तित विधि में पारित करें। ऑन-स्क्रीन कंट्रोल आपके लिए बाकी काम करता है।

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

आपके द्वारा बनाई गई स्क्रिप्ट को बटन में संलग्न करें। इस क्रिया को गेमपैड की बाईं छड़ी के संचालन के रूप में माना जाना निर्धारित है।

गेम को स्थानांतरित करने का प्रयास करें और बटन स्पर्श करें। मुझे लगता है कि आप देख सकते हैं कि स्पर्श की स्थिति के आधार पर प्राप्त मूल्य बदल जाता है। इसके अलावा, आप देख सकते हैं कि भले ही आप स्पर्श करते समय खींचते हैं, ड्रैग पोजिशन के आधार पर मूल्य बदल जाता है।