Přizpůsobte si ovládací prvek na obrazovce a implementujte směrový ovladač

Stránky aktualizovány :
Datum vytvoření stránky :

Ověřovací prostředí

Windows
  • Systém Windows 11
Editor jednoty
  • 2020.3.25f1
Vstupní systémový balíček
  • 1.2.0

Předpoklady pro tento tip

Následující nastavení byla provedena předem jako předpoklad pro popis tohoto tipu.

Měli byste se také seznámit s následujícími tipy:

Ovládací páčka na obrazovce je standardní operace orientace

Karta na obrazovce je implementována jako virtuální páčka, která replikuje činnost páčky podobnou té, která se nachází na fyzickém ovladači. Chcete-li ji například posunout doprava, můžete se nejprve dotknout obrazovky a poté ji posunout doprava, abyste ji srazili dolů.

Je snadné si to představit jako operaci páčky, ale naopak, když ji chcete okamžitě posunout doprava, (1) dotkněte se, (2) posuňte doprava, Vzhledem k tomu, že vyžaduje dvoustupňovou operaci, reakce bude nevyhnutelně trochu zpožděna. To platí zejména tehdy, když potřebujete provést rychlou změnu směru v řadě.

směrový ovladač, který určuje směr v okamžiku dotyku (nestandardní)

Nejlepší vstupní metoda pro rychlé zadávání směru je taková, která dokáže určit směr v okamžiku dotyku umístěním něčeho jako směrový ovladač nebo šipka. Není to vyrobeno Unity, ale v mé hře Little Saber jsem umístil klávesy se šipkami tak, abyste se mohli rychle pohybovat ve směru, kterého se dotknete. Samozřejmě můžete také posunout nahoru nebo doleva při dotyku a přepínat pohyby.

Standard ovládání na obrazovce však není snadné umístit a implementovat, protože existují pouze páčky na obrazovce a tlačítka na obrazovce.

Můžete také vytvořit pseudo-D-pad uspořádáním čtyř tlačítek, jak je znázorněno na obrázku níže, ale je to nepohodlné, protože nemůžete zadávat diagonálně. Pokud umístíte 8 tlačítek, je možná diagonální operace, ale operace směru toku, jako je "← ↙ ↓", stále není možná.

Vytvoření směrového panelu s přizpůsobením ovládání na obrazovce

Jak můžete vidět, ovládání na obrazovce je standardně dodáváno pouze s hůlkou a tlačítkem, ale chybějící funkce můžete přizpůsobit pomocí vlastních skriptů. Takže tady bych chtěl vytvořit směrovou podložku s přizpůsobením ovládání na obrazovce.

Mapa akcí

Použijeme akční mapu, ale použijeme tu, která byla vytvořena v předchozích tipech, jak je, takže postup je vynechán.

Také jste napsal nějaký kód.

Umístění objektů

Umístěte textovou oblast pro zobrazení vašeho vstupu a tlačítka, které nahrazuje směrový ovladač. V tomto případě jsou tlačítka uspořádána, ale můžete je nahradit obrázkem, který je srozumitelnější.

Skript pro zobrazení vstupu

Vzhledem k tomu, že ovládání na obrazovce nahrazuje dotykové ovládání fyzickou interakcí ovladače, Vytvořte skript, který zobrazuje váš vstup v textu při fungování mapy akcí.

Obsah je stejný jako předtím, takže vysvětlení vynechám.

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

Po nastavení nejprve zkontrolujte, zda funguje s klávesnicí nebo gamepadem.

Přizpůsobení ovládacích prvků na obrazovce

To nás přivádí k hlavnímu tématu těchto tipů. Přizpůsobení ovládacího prvku na obrazovce je skript, takže nejprve vytvořte skript. Název je libovolný, ale v tomto případě OnScreenDpad je .

Skript vypadá takto:

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

Třídu OnScreenControl přizpůsobení ovládacího prvku na obrazovce vytvoříte zděděním ze třídy Přizpůsobit. Kromě toho, pro příjem různých dotykových událostí, zdědit cílové rozhraní. Zde jsou zpracovány "při dotyku", "při pohybu při dotyku", "při uvolnění dotyku" a "před přetažením", takže jsou také popsána následující rozhraní.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Obsah rozhraní
IPointerDownHandler Při dotyku
IPointerUpHandler Když uvolníte dotyk
IDragHandler Když se při dotyku pohybujete
IInitializePotentialDragHandler Než začnete přetahovat

Jsou deklarována následující pole:

controlPathInternalOnScreenControl je povinný, pokud dědíte ze třídy. Obsahuje řetězec, na které tlačítko vstupního zařízení se má mapovat, ale v podstatě jej můžete zapsat tak, jak je. InputControl Atribut by však měl obsahovat hodnotu, která má být zachována (v tomto případě Vector2).

_objectPosition_objectSizeHalf a předem podržte polovinu pozice a velikosti tlačítka a použijte jej později pro výpočty.

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

Metoda, volaná Start po inicializaci objektu, získá polovinu polohy a velikosti tlačítka na plátně. Velikost také zohledňuje měřítko. Start Zpracovává se metodou, ale pokud ji lze ve výpočtu nakonec správně použít, může být načasování akvizice kdekoliv.

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

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

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

OnInitializePotentialDrag se volá, když chcete začít přetahovat po dotyku. Při přetahování OnDrag je metoda volána. Prahová hodnota je nastavena tak, aby do určité míry zabránila úsudku o odporu, takže prst se trochu nepohybuje a úsudek o tažení nedochází v důsledku operace prostého dotyku.

Tentokrát, protože se jedná o vstup, který předpokládá, že chcete provést operaci jemného doladění, deaktivujte toto nastavení prahové hodnoty a okamžitě proveďte úsudek o přetažení. eventData.useDragThreshold false Prahovou hodnotu můžete přepsat nastavením na .

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

Níže jsou uvedeny události volané při dotyku, přetažení a uvolnění. OnPointerDown OnDrag Protože a , každý provádí Operate stejné vstupní zpracování, takže vytvoříme samostatnou metodu a zavoláme ji. OnPointerUp Nyní jej předejte metodě a oznámit ovládacímu SendValueToControl Vector2.zero prvku, že přestal psát.

/// <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 Metoda je základní vstupní operace D-padu.

Za prvé, dotyková poloha může být získána pomocí , ale protože tato souřadnice je souřadnice eventData.position herní obrazovky, RectTransformUtility.ScreenPointToLocalPointInRectangle metoda převodu na souřadnice plátna. Hodnoty, které mají být předány, jsou "canvas", "touch position", "camera" a " RectTransformreceiving variable".

Po získání "dotykové pozice na plátně" jej převeďte na pozici tlačítka jako počátku. Stačí odečíst polohu ()_objectPosition objektu.

Dále, protože dotyková pozice je stále číslo na plátně, převeďte tuto hodnotu na poměr 0 ~ 1. Pokud vydělíte polovinou velikosti tlačítka, dotyková pozice v rozsahu tlačítka bude 0 ~ 1. Pokud táhnete dotykem, hodnota bude 1 nebo více, protože přijímá operace i mimo tlačítko. Vector2.ClampMagnitude Udělejte to v metodě tak, aby nepřekročila 1.

Nakonec předejte vstupní hodnotu SendValueToControl převedenou na 0 ~ 1 metodě. Ovládání na obrazovce udělá zbytek za vás.

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

Připojte skript, který jste vytvořili, k tlačítku. Tato akce je nastavena tak, aby byla považována za činnost levé páčky gamepadu.

Zkuste hru posunout a dotknout se tlačítek. Myslím, že můžete vidět, že získaná hodnota se mění v závislosti na dotykové poloze. Také můžete vidět, že i když přetáhnete při dotyku, hodnota se mění v závislosti na poloze přetažení.