سفارشی کردن کنترل روی صفحه نمایش برای پیاده سازی D-pad

صفحه به روز شده :
تاریخ ایجاد صفحه :

محیط تایید

ویندوز
  • ویندوز 11
ویرایشگر یونیتی
  • دانلود: 2020.3.25f1
بسته سیستم ورودی
  • 1.2.0

پیشنیازهای این نکته

تنظیمات زیر از قبل به عنوان یک فرض برای توصیف این نکته ساخته شده است.

شما همچنین باید با نکات زیر اشنا باشید:

روی صفحه نمایش کنترل استیک عملیات جهت گیری استاندارد است

On-Screen Stick به عنوان یک چوب مجازی اجرا می شود و عملکرد یک چوب را شبیه به انچه که در یک کنترل کننده فیزیکی یافت می شود، تکرار می کند. به عنوان مثال، اگر می خواهید ان را به سمت راست حرکت دهید، ابتدا می توانید صفحه نمایش را لمس کنید تا چوب را لمس کنید و سپس ان را به سمت راست بکشید تا چوب را پایین بکشید.

تصور ان به عنوان یک عملیات چوب اسان است، اما برعکس، هنگامی که می خواهید ان را بلافاصله به سمت راست حرکت دهید، (1) لمس، (2) اسلاید به سمت راست، از انجا که نیاز به یک عملیات دو مرحله ای دارد، پاسخ ناگزیر کمی به تاخیر خواهد رسید. این امر به ویژه هنگامی صادق است که شما نیاز به ایجاد یک تغییر سریع جهت در یک ردیف دارید.

D-pad که جهت را در لحظه لمس تعیین می کند (غیر استاندارد)

بهترین روش ورودی برای ورودی سریع جهت، روشی است که می تواند جهت را در لحظه لمس کردن با قرار دادن چیزی مانند D-pad یا کلید فلش تعیین کند. این توسط Unity ساخته نشده است، اما در بازی من Little Saber، کلید های arrow را قرار می دهم تا بتوانید به سرعت در جهتی که لمس می کنید حرکت کنید. البته، شما همچنین می توانید اسلاید بالا و یا چپ در حالی که لمس برای تغییر حرکات.

با این حال، استاندارد کنترل روی صفحه نمایش برای قرار دادن و پیاده سازی اسان نیست زیرا فقط دکمه های روی صفحه نمایش و دکمه های روی صفحه وجود دارد.

شما همچنین می توانید یک شبه D-پد با تنظیم چهار دکمه همانطور که در شکل زیر نشان داده شده است ایجاد کنید، اما ناخوشایند است زیرا شما نمی توانید مورب وارد کنید. اگر 8 دکمه قرار دهید، عملیات مورب امکان پذیر است، اما عملیات جهت جریان مانند "← ↙ ↓" هنوز امکان پذیر نیست.

ایجاد یک پد جهت با سفارشی سازی کنترل بر روی صفحه نمایش

همانطور که می بینید، کنترل روی صفحه نمایش تنها با Stick و Button استاندارد است، اما شما می توانید ویژگی های از دست رفته را با اسکریپت های خود سفارشی کنید. بنابراین در اینجا من می خواهم برای ایجاد یک پد جهت با روی صفحه نمایش سفارشی سازی کنترل.

نقشه عمل

ما از نقشه عمل استفاده خواهیم کرد، اما از انچه که در راهنمایی های قبلی ایجاد شده است استفاده خواهیم کرد، بنابراین روش حذف می شود.

شما همچنین برخی از کد نوشته شده است.

اشیاء موقعیت یابی

یک منطقه متن را برای نمایش ورودی خود و یک دکمه که جایگزین D-pad می شود قرار دهید. در این مورد، دکمه ها مرتب شده اند، اما می توانید انها را با یک تصویر جایگزین کنید که درک ان اسان تر است.

دستنوشته برای نمایش ورودی

از انجا که کنترل روی صفحه نمایش جایگزین لمس با تعامل کنترل کننده فیزیکی، ایجاد یک اسکریپت که ورودی خود را در متن نمایش می دهد به عنوان نقشه عمل کار می کند.

محتوای ان مثل قبل است، بنابراین توضیح را حذف خواهم کرد.

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

شما با به ارث بردن از کلاس Customize یک کلاس سفارشی سازی On-Screen Control ایجاد OnScreenControl می کنید. علاوه بر این، برای دریافت رویدادهای مختلف لمسی، رابط هدف را به ارث ببرید. در اینجا، "هنگام لمس"، "هنگام حرکت در هنگام لمس"، "هنگامی که لمس ازاد می شود" و "قبل از کشیدن" پردازش می شوند، بنابراین رابط های زیر نیز به ترتیب توصیف می شوند.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
محتویات واسط
IPointerدان هندلر وقتی لمس می شود
ایپوینتراپ هندلر هنگامی که لمس را ازاد می کنید
ایدراگ هندلر هنگامی که شما حرکت می کنید در حالی که لمس می کنید
IInitialize تواناییDragHandler قبل از اینکه شروع به کشیدن

فیلدهای زیر اعلام می شوند:

controlPathInternalOnScreenControl اگر شما از یک کلاس به ارث می برد مورد نیاز است. این یک رشته از دکمه دستگاه ورودی را برای نقشه برداری نگه می دارد، اما اساسا شما می توانید ان را همانطور که هست بنویسید. InputControl با این حال، ویژگی باید حاوی مقدار حفظ شود (Vector2 در این مورد).

_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 این روش عملیات ورودی هسته D-pad است.

اول از همه، موقعیت لمسی را می توان با به دست اورد، اما از انجا که این مختصات مختصات 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);
}

اسکریپتی را که ایجاد کرده اید به دکمه پیوست کنید. این عمل قرار است به عنوان عمل چوب چپ از Gamepad درمان می شود.

سعی کنید بازی را حرکت دهید و دکمه ها را لمس کنید. من فکر می کنم شما می توانید ببینید که ارزش به دست امده بسته به موقعیت لمسی تغییر می کند. همچنین، شما می توانید ببینید که حتی اگر شما در هنگام لمس کشیدن، ارزش بسته به موقعیت کشیدن تغییر می کند.