A képernyőn megjelenő vezérlő testreszabása az I-választó megvalósításához

Oldal frissítve :
Oldal létrehozásának dátuma :

Ellenőrzési környezet

Windows
  • Windows 11 esetén
Unity-szerkesztő
  • 2020.3.25f1
Bemeneti rendszercsomag
  • 1.2.0

A tipp előfeltételei

A következő beállításokat előre elvégeztük a tipp leírásának előfeltételeként.

Ismernie kell a következő tippeket is:

A képernyőn megjelenő vezérlőkar a szokásos tájolás

A képernyőn megjelenő botkormány virtuális botként van megvalósítva, amely a fizikai vezérlőhöz hasonló botkormány működését reprodukálja. Ha például jobbra szeretné mozgatni, először érintse meg a képernyőt a bot megérintéséhez, majd csúsztassa jobbra a bot leütéséhez.

Könnyen elképzelhető botos műveletként, de éppen ellenkezőleg, ha azonnal jobbra akarja mozgatni, (1) érintse meg, (2) csúsztassa jobbra, Mivel kétlépcsős műveletet igényel, a válasz elkerülhetetlenül kissé késik. Ez különösen igaz, ha gyorsan meg kell változtatnia az irányt egymás után.

I-választó, amely meghatározza az érintés pillanatában az irányt (nem szabványos)

A gyors iránybevitel legjobb beviteli módja az, amely az érintés pillanatában meg tudja határozni az irányt valami I-választó vagy nyílbillentyű elhelyezésével. Nem a Unity készítette, de a Little Saber játékomban úgy helyezem el a nyílbillentyűket, hogy gyorsan mozoghasson az érintett irányba. Természetesen érintés közben felfelé vagy balra is csúsztathatja a mozgások váltásához.

A képernyőn megjelenő vezérlési szabványt azonban nem könnyű elhelyezni és megvalósítani, mert csak a képernyőn megjelenő botok és a képernyőn megjelenő gombok vannak.

Pszeudo-I-választót is létrehozhat négy gomb elrendezésével az alábbi ábrán látható módon, de ez kényelmetlen, mert nem tud átlósan beírni. Ha 8 gombot helyez el, az átlós működés lehetséges, de az áramlási irányú működés, például a "← ↙ ↓" még mindig nem lehetséges.

Irányválasztó létrehozása a Képernyő-vezérlés testreszabásával

Mint láthatja, a képernyőn megjelenő vezérlés csak a Stick and Button funkcióval rendelkezik, de a hiányzó funkciókat saját szkriptekkel testreszabhatja. Tehát itt szeretnék létrehozni egy irányválasztót a képernyőn megjelenő vezérlés testreszabásával.

Műveleti térkép

A műveleti térképet fogjuk használni, de az előző tippekben létrehozottat fogjuk használni, így az eljárás kimarad.

Írtál néhány kódot is.

Objektumok pozicionálása

Helyezzen el egy szövegterületet a bevitel megjelenítéséhez, valamint egy gombot, amely helyettesíti az I-választót. Ebben az esetben a gombok el vannak rendezve, de helyettesítheti őket egy könnyebben érthető képpel.

Szkript a bemenet megjelenítéséhez

Mivel a Képernyő-vezérlés az érintést fizikai vezérlővel helyettesíti, Hozzon létre egy szkriptet, amely szövegesen jeleníti meg a bemenetet a művelettérkép működése közben.

A tartalom ugyanaz, mint korábban, ezért elhagyom a magyarázatot.

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

A beállítás után először ellenőrizze, hogy működik-e a billentyűzettel vagy a játékvezérlővel.

A képernyőn megjelenő vezérlők testreszabása

Ez elvezet minket a tippek fő témájához. A Képernyőn megjelenő vezérlő testreszabása egy parancsfájl, ezért először hozzon létre egy szkriptet. A név önkényes, de ebben az esetben OnScreenDpad .

A szkript így néz ki:

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

A Képernyő-vezérlő testreszabási osztályt OnScreenControl a Customize osztálytól örökölve hozhatja létre. Ezenkívül különféle érintési események fogadásához örökölje a célfelületet. Itt a "megérintéskor", "érintés közben mozogva", "amikor az érintést elengedik" és "húzás előtt" kerülnek feldolgozásra, így a következő interfészeket is leírjuk.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Interfész tartalma
IPointerDownHandler Amikor megérinti
IPointerUpHandler Amikor elengedi az érintést
IDragHandler Amikor érintés közben mozog
IInitializálPotenciálDragHandler A húzás megkezdése előtt

A következő mezők vannak deklarálva:

controlPathInternalOnScreenControl kötelező, ha egy osztálytól örököl. Tartalmaz egy karakterláncot, hogy a beviteli eszköz melyik gombjához kell leképezni, de alapvetően úgy írhatja, ahogy van. InputControl Az attribútumnak azonban tartalmaznia kell a megtartandó értéket (ebben az esetben Vector2).

_objectPosition_objectSizeHalf és tartsa előre a gomb helyzetének és méretének felét, és később használja a számításokhoz.

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

Az objektum inicializálása után meghívott Start metódus a képernyőn lévő gomb pozíciójának és méretének felét kapja. A méret figyelembe veszi a skálát is. Start Ezt egy módszerrel dolgozzák fel, de ha végül helyesen használható a számításban, akkor az akvizíció időzítése bárhol lehet.

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

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

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

OnInitializePotentialDrag akkor hívódik meg, amikor érintés után el szeretné kezdeni a húzást. Húzás OnDrag közben a metódust hívják. Egy küszöbérték van beállítva, hogy bizonyos mértékig megakadályozza a húzási ítéletet, hogy az ujj ne mozogjon egy kicsit, és a húzási ítélet ne forduljon elő az egyszerű érintés művelete miatt.

Ezúttal, mivel ez egy bemenet, amely feltételezi, hogy finomhangolási műveletet szeretne végrehajtani, tiltsa le ezt a küszöbérték-beállítást, és azonnal hozzon húzási ítéletet. eventData.useDragThreshold false A küszöbértéket felülbírálhatja a értékre állításával.

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

Az alábbiakban láthatók az érintés, húzás és elengedés során hívott események. OnPointerDown OnDrag Mivel és , mindegyik ugyanazt a bemeneti feldolgozást hajtja végre Operate , ezért létrehozunk egy külön metódust, és meghívjuk. OnPointerUp Most adja át a metódusnak, hogy SendValueToControl Vector2.zero értesítse a vezérlőt arról, hogy abbahagyta a gépelést.

/// <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 A módszer az alapvető I-választó bemeneti művelet.

Először is, az érintési pozíció a segítségével érhető el, de mivel ez a koordináta a eventData.position játékképernyő koordinátája, RectTransformUtility.ScreenPointToLocalPointInRectangle vászonkoordinátákká konvertálási módszer. Az átadandó értékek a következők: "vászon", "érintési pozíció", "kamera" és " RectTransformfogadó változó".

Miután megkapta az "érintési pozíciót a vásznon", alakítsa át a gomb helyzetére origóként. Csak vonja ki az_objectPosition objektum helyzetét ().

Ezután, mivel az érintési pozíció továbbra is szám a vásznon, alakítsa át ezt az értéket 0~1 arányra. Ha elosztja a gomb méretének felével, az érintési pozíció a gomb tartományán belül 0 ~ 1 lesz. Ha érintéssel húzza, az érték 1 vagy több lesz, mert a gombon kívül is fogad műveleteket. Vector2.ClampMagnitude Tegye ezt a módszerben úgy, hogy ne haladja meg az 1-et.

Végül adja át a 0~1-re konvertált bemeneti értéket SendValueToControl a metódusnak. A többit a képernyőn megjelenő vezérlő végzi el Ön helyett.

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

Csatolja a létrehozott szkriptet a gombhoz. Ez a művelet úgy van beállítva, hogy a játékvezérlő bal karjának műveleteként kezelje.

Próbálja meg mozgatni a játékot, és érintse meg a gombokat. Azt hiszem, láthatja, hogy a kapott érték az érintési helyzettől függően változik. Azt is láthatja, hogy még akkor is, ha érintés közben húzza, az érték a húzási pozíciótól függően változik.