Tinkinkite ekrano valdymą, kad įdiegtumėte D-pad

Puslapis atnaujintas :
Puslapio sukūrimo data :

Tikrinimo aplinka

Windows
  • Langai 11
"Unity" redaktorius
  • 2020.3.25f1
Įvesties sistemos paketas
  • 1.2.0

Būtinos šio patarimo sąlygos

Šie nustatymai buvo atlikti iš anksto kaip šio patarimo aprašymo prielaida.

Taip pat turėtumėte būti susipažinę su šiais patarimais:

"On-Screen Control Stick" yra standartinė orientacijos operacija

"On-Screen Stick" yra įdiegta kaip virtuali lazda, atkartojanti lazdos, panašios į esančią fiziniame valdiklyje, veikimą. Pavyzdžiui, jei norite perkelti jį į dešinę, pirmiausia galite paliesti ekraną, kad paliestumėte lazdą, tada pastumkite jį į dešinę, kad numuštumėte lazdą.

Tai lengva įsivaizduoti kaip lazdos operaciją, bet, priešingai, kai norite nedelsdami perkelti ją į dešinę, (1) palieskite, (2) slinkite į dešinę, Kadangi tam reikia dviejų pakopų operacijos, atsakas neišvengiamai bus šiek tiek atidėtas. Tai ypač aktualu, kai reikia greitai pakeisti kryptį iš eilės.

D-pad, kuris nustato kryptį prisilietimo momentu (nestandartinis)

Geriausias greito krypties įvesties metodas yra tas, kuris gali nustatyti kryptį prisilietimo momentu, įdėdamas kažką panašaus į D-pad arba rodyklės klavišą. Tai nėra sukurta "Unity", bet mano žaidime "Little Saber" aš įdedu rodyklių klavišus, kad galėtumėte greitai judėti ta kryptimi, kurią paliečiate. Žinoma, paliesdami taip pat galite slysti aukštyn arba kairėn, kad perjungtumėte judesius.

Tačiau ekrano valdymo standartą nėra lengva įdėti ir įdiegti, nes yra tik ekrano lazdelės ir ekrano mygtukai.

Taip pat galite sukurti pseudo-D-pad, išdėstydami keturis mygtukus, kaip parodyta paveikslėlyje žemiau, tačiau tai nepatogu, nes negalite įvesti įstrižai. Jei įdėsite 8 mygtukus, įstrižainės valdymas yra įmanomas, tačiau srauto krypties valdymas, pvz., "← ↙ ↓", vis tiek neįmanomas.

Sukurkite kryptinį bloknotą naudodami "On-Screen Control" tinkinimą

Kaip matote, "On-Screen Control" standartiškai yra tik su "Stick" ir "Button", tačiau trūkstamas funkcijas galite tinkinti naudodami savo scenarijus. Taigi čia norėčiau sukurti kryptinį kilimėlį su ekrano valdymo tinkinimu.

Veiksmų žemėlapis

Naudosime veiksmų žemėlapį, tačiau naudosime tą, kuris buvo sukurtas ankstesniuose patarimuose, koks jis yra, todėl procedūra praleidžiama.

Jūs taip pat parašėte tam tikrą kodą.

Objektų padėties nustatymas

Įdėkite teksto sritį, kad būtų rodoma įvestis, ir mygtuką, kuris pakeičia D-pad. Tokiu atveju mygtukai yra išdėstyti, tačiau galite juos pakeisti lengviau suprantamu vaizdu.

Scenarijus įvesčiai rodyti

Kadangi valdymas ekrane pakeičia prisilietimą fizine valdiklio sąveika, Sukurkite scenarijų, kuris rodys jūsų įvestį tekste, kai veiks veiksmų schema.

Turinys yra toks pat kaip ir anksčiau, todėl praleisiu paaiškinimą.

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

Nustatę pirmiausia patikrinkite, ar jis veikia su klaviatūra ar žaidimų pultu.

Ekrano valdiklių tinkinimas

Tai atveda mus prie pagrindinės šių patarimų temos. Ekrano valdiklio tinkinimas yra scenarijus, todėl pirmiausia sukurkite scenarijų. Pavadinimas yra savavališkas, tačiau šiuo atveju OnScreenDpad jis yra .

Scenarijus atrodo taip:

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

Galite sukurti OnScreenControl ekrano valdymo tinkinimo klasę paveldėdami iš tinkinimo klasės. Be to, norėdami gauti įvairius prisilietimo įvykius, paveldėkite tikslinę sąsają. Čia apdorojami "liečiant", "judant liečiant", "kai prisilietimas atleidžiamas" ir "prieš vilkdami", todėl atitinkamai aprašomos ir šios sąsajos.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Sąsajos turinys
"IPointerDownHandler" Palietus
"IPointerUpHandler" Kai atleidžiate prisilietimą
"IDragHandler" Kai judate liesdami
IInitializePotentialDragHandler Prieš pradėdami vilkti

Deklaruojami šie laukai:

controlPathInternalOnScreenControl būtina, jei paveldite iš klasės. Jame yra eilutė, su kuria įvesties įrenginio mygtuku susieti, bet iš esmės galite jį parašyti tokį, koks jis yra. InputControl Tačiau atribute turi būti nurodyta vertė, kurią reikia išlaikyti (šiuo atveju Vector2).

_objectPosition_objectSizeHalf ir iš anksto laikykite pusę mygtuko padėties ir dydžio ir vėliau naudokite jį skaičiavimams.

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

Metodas, vadinamas Start inicijavus objektą, gauna pusę mygtuko padėties ir dydžio ant drobės. Dydis taip pat atsižvelgia į skalę. Start Jis apdorojamas metodu, tačiau jei jis gali būti teisingai naudojamas skaičiuojant, įsigijimo laikas gali būti bet kur.

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

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

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

OnInitializePotentialDrag iškviečiamas, kai norite pradėti vilkti po prisilietimo. Vilkimo OnDrag metu metodas vadinamas. Slenkstis nustatomas tam, kad tam tikru mastu būtų užkirstas kelias vilkimo sprendimui, kad pirštas šiek tiek nejudėtų ir vilkimo sprendimas neįvyktų dėl to, kad veikia tiesiog liečiant.

Šį kartą, kadangi tai yra įvestis, daranti prielaidą, kad norite atlikti koregavimo operaciją, išjunkite šį slenksčio nustatymą ir nedelsdami priimkite sprendimą dėl vilkimo. eventData.useDragThreshold false Galite nepaisyti slenksčio nustatydami .

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

Žemiau pateikiami įvykiai, vadinami atitinkamai liečiant, vilkiant ir atleidžiant. OnPointerDown OnDrag Kadangi ir , kiekvienas atlieka Operate tą patį įvesties apdorojimą, todėl mes sukuriame atskirą metodą ir vadiname jį. OnPointerUp Dabar perduokite jį metodui, kad praneštumėte valdikliui, kad SendValueToControl Vector2.zero jis nustojo rašyti.

/// <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 Metodas yra pagrindinė D-pad įvesties operacija.

Visų pirma, prisilietimo padėtį galima gauti naudojant , bet kadangi ši koordinatė yra žaidimo ekrano koordinatė eventData.position , RectTransformUtility.ScreenPointToLocalPointInRectangle metodas konvertuoti į drobės koordinates. Perduodamos reikšmės yra "drobė", "lietimo padėtis", "kamera" ir " RectTransformpriimantis kintamasis".

Gavę "prisilietimo padėtį ant drobės", konvertuokite ją į mygtuko padėtį kaip kilmę. Tiesiog atimkite objekto padėtį ()._objectPosition

Kitas, kadangi jutiklinė padėtis vis dar yra skaičius drobėje, konvertuokite šią reikšmę į santykį 0 ~ 1. Jei padalinsite iš pusės mygtuko dydžio, jutiklinė padėtis mygtuko diapazone bus 0 ~ 1. Jei vilksite liečiant, reikšmė bus 1 ar daugiau, nes ji priima operacijas net už mygtuko ribų. Vector2.ClampMagnitude Atlikite tai metodu, kad jis neviršytų 1.

Galiausiai metodui perduokite įvesties vertę SendValueToControl , konvertuotą į 0 ~ 1. Visa kita už jus atlieka ekrano valdymas.

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

Prie mygtuko pridėkite sukurtą scenarijų. Šis veiksmas turi būti traktuojamas kaip kairiosios "Gamepad" lazdelės veikimas.

Pabandykite perkelti žaidimą ir palieskite mygtukus. Manau, matote, kad gauta vertė keičiasi priklausomai nuo prisilietimo padėties. Be to, matote, kad net jei velkate liesdami, vertė keičiasi priklausomai nuo vilkimo padėties.