Персонализиране на екранната контрола за внедряване на D-pad

Страницата се актуализира :
Дата на създаване на страница :

Среда за проверка

Уиндоус
  • Прозорци 11
Редактор на единство
  • 2020.3.25ф1
Пакет на входната система
  • 1.2.0

Предпоставки за този съвет

Следните настройки са направени предварително като предпоставка за описанието на този съвет.

Също така трябва да сте запознати със следните съвети:

Екранният контролен стик е стандартната операция за ориентация

Екранният стик се изпълнява като виртуална пръчка, възпроизвеждаща работата на пръчка, подобна на тази, която се намира на физически контролер. Например, ако искате да го преместите надясно, можете първо да докоснете екрана, за да докоснете пръчката, и след това да го плъзнете надясно, за да съборите пръчката.

Лесно е да си представите като операция с пръчка, а напротив, когато искате да я преместите надясно веднага, (1) докоснете, (2) плъзнете надясно, Тъй като изисква операция в две стъпки, отговорът неизбежно ще бъде малко забавен. Това е особено вярно, когато трябва да направите бърза промяна на посоката подред.

D-pad, който определя посоката в момента на докосване (нестандартен)

Най-добрият метод за въвеждане на бърза посока е този, който може да определи посоката в момента на докосване, като постави нещо като D-pad или клавиш със стрелка. Тя не е направена от Unity, но в моята игра Little Saber, поставям клавишите със стрелки, така че да можете бързо да се движите в посоката, която докосвате. Разбира се, можете също да се плъзгате нагоре или наляво, докато докосвате, за да превключвате движенията.

Въпреки това, стандартът за управление на екрана не е лесен за поставяне и внедряване, защото има само екранни пръчки и екранни бутони.

Можете също така да създадете псевдо-D-подложка, като подредите четири бутона, както е показано на фигурата по-долу, но това е неудобно, защото не можете да въвеждате диагонално. Ако поставите 8 бутона, диагоналната операция е възможна, но операцията по посока на потока като "← ↙ ↓" все още не е възможна.

Създаване на подложка за посока с персонализиране на екранното управление

Както можете да видите, On-Screen Control се предлага стандартно само с Stick and Button, но можете да персонализирате липсващите функции със собствени скриптове. Така че тук бих искал да създам подложка за посока с персонализиране на On-Screen Control.

Карта на действията

Ще използваме картата на действието, но ще използваме тази, създадена в предишните съвети, както е, така че процедурата е пропусната.

Вие също сте написали някакъв код.

Позициониране на обекти

Поставете текстова област, за да покажете въведените данни, и бутон, който замества 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);
  }
}

Можете да създадете клас за персонализиране на екранната контрола OnScreenControl , като наследите от класа Персонализиране. Освен това, за да получавате различни събития за докосване, наследете целевия интерфейс. Тук се обработват "при докосване", "при движение при докосване", "при освобождаване на докосването" и "преди плъзгане", така че съответно са описани и следните интерфейси.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Съдържание на интерфейса
IPointerDownHandler При докосване
IPointerUpHandler Когато отпуснете докосването
IDragHandler Когато се движите, докато докосвате
IInitializePotentialDragHandler Преди да започнете плъзгането

Декларират се следните полета:

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

Прикачете скрипта, който сте създали, към бутона. Това действие е настроено да се третира като операция на лявата пръчка на геймпада.

Опитайте се да преместите играта и докоснете бутоните. Мисля, че можете да видите, че получената стойност се променя в зависимост от позицията на докосване. Също така можете да видите, че дори ако плъзгате, докато докосвате, стойността се променя в зависимост от позицията на плъзгане.