Pas de besturing op het scherm aan om de D-pad te implementeren

Pagina bijgewerkt :
Aanmaakdatum van pagina :

Verificatieomgeving

Ramen
  • Voor Windows 11
Unity Editor
  • 2020.3.25f1
Input systeem pakket
  • 1.2.0

Vereisten voor deze tip

De volgende instellingen zijn vooraf gemaakt als uitgangspunt voor de beschrijving van deze tip.

U moet ook bekend zijn met de volgende tips:

On-Screen Control Stick is de standaard oriëntatiebediening

De On-Screen Stick is geïmplementeerd als een virtuele stick, die de werking van een stick repliceert die vergelijkbaar is met die op een fysieke controller. Als u het bijvoorbeeld naar rechts wilt verplaatsen, kunt u eerst het scherm aanraken om de stick aan te raken en vervolgens naar rechts schuiven om de stick naar beneden te slaan.

Het is gemakkelijk voor te stellen als een stokbediening, maar integendeel, wanneer u het onmiddellijk naar rechts wilt verplaatsen, (1) aanraken, (2) naar rechts schuiven, Omdat het een bewerking in twee stappen vereist, zal de reactie onvermijdelijk een beetje vertraagd zijn. Dit geldt vooral wanneer u snel achter elkaar van richting moet veranderen.

D-pad die de richting bepaalt op het moment van aanraking (niet-standaard)

De beste invoermethode voor snelle richtingsinvoer is er een die de richting op het moment van aanraken kan bepalen door iets als een D-pad of pijltoets te plaatsen. Het is niet gemaakt door Unity, maar in mijn spel Little Saber plaats ik de pijltjestoetsen zo dat je snel kunt bewegen in de richting die je aanraakt. Natuurlijk kun je ook omhoog of naar links schuiven tijdens het aanraken om van beweging te wisselen.

De On-Screen Control-standaard is echter niet eenvoudig te plaatsen en te implementeren omdat er alleen On-Screen Sticks en On-Screen Buttons zijn.

U kunt ook een pseudo-D-pad maken door vier knoppen te rangschikken zoals weergegeven in de onderstaande afbeelding, maar het is onhandig omdat u niet diagonaal kunt invoeren. Als u 8 knoppen plaatst, is diagonale bediening mogelijk, maar stroomrichtingsbediening zoals "← ↙ ↓" is nog steeds niet mogelijk.

Een richtingspad maken met aanpassing van Besturingselement op het scherm

Zoals u kunt zien, wordt On-Screen Control alleen standaard geleverd met Stick en Button, maar u kunt de ontbrekende functies aanpassen met uw eigen scripts. Dus hier wil ik een directioneel pad maken met aanpassing van On-Screen Control.

Actiekaart

We zullen de actiekaart gebruiken, maar we zullen degene gebruiken die in de vorige tips is gemaakt zoals deze is, dus de procedure wordt weggelaten.

Je hebt ook wat code geschreven.

Objecten positioneren

Plaats een tekstgebied om uw invoer weer te geven en een knop die de D-pad vervangt. In dit geval zijn de knoppen gerangschikt, maar u kunt ze vervangen door een afbeelding die gemakkelijker te begrijpen is.

Script om invoer weer te geven

Omdat On-Screen Control touch vervangt door fysieke controllerinteractie, Maak een script dat uw invoer in tekst weergeeft terwijl de actiekaart werkt.

De inhoud is hetzelfde als voorheen, dus ik zal de uitleg weglaten.

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

Controleer na het instellen eerst of het werkt met uw toetsenbord of gamepad.

Besturingselementen op het scherm aanpassen

Dit brengt ons bij het hoofdonderwerp van deze tips. Het aanpassen van het besturingselement op het scherm is een script, dus maak eerst een script. De naam is arbitrair, maar in dit geval OnScreenDpad is het .

Het script ziet er als volgt uit:

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

U maakt een OnScreenControl aanpassingsklasse Besturingselement op het scherm door over te nemen van de klasse Aanpassen. Als u bovendien verschillende aanraakgebeurtenissen wilt ontvangen, neemt u de doelinterface over. Hier worden "bij aanraken", "bij bewegen tijdens het aanraken", "wanneer de aanraking wordt losgelaten" en "voor het slepen" verwerkt, zodat respectievelijk ook de volgende interfaces worden beschreven.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Inhoud van de interface
IPointerDownHandler Bij aanraking
IPointerUpHandler Wanneer u de aanraking loslaat
IDragHandler Wanneer u beweegt tijdens het aanraken
IInitializePotentialDragHandler Voordat u begint met slepen

De volgende velden worden gedeclareerd:

controlPathInternalOnScreenControl is vereist als u erft van een klasse. Het bevat een reeks van welke knop van het invoerapparaat moet worden toegewezen, maar in principe kunt u het schrijven zoals het is. InputControl Het attribuut moet echter de te behouden waarde bevatten (vector2 in dit geval).

_objectPosition_objectSizeHalf en houd van tevoren de helft van de positie en grootte van de knop vast en gebruik deze later voor berekeningen.

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

De methode, die wordt aangeroepen Start nadat het object is geïnitialiseerd, krijgt de helft van de positie en grootte van de knop op het canvas. De grootte houdt ook rekening met de schaal. Start Het wordt verwerkt door een methode, maar als het uiteindelijk correct kan worden gebruikt in de berekening, kan de acquisitietiming overal zijn.

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

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

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

OnInitializePotentialDrag wordt aangeroepen wanneer u wilt beginnen met slepen na aanraking. Tijdens het slepen OnDrag wordt de methode aangeroepen. Er is een drempel ingesteld om het slepen tot op zekere hoogte te voorkomen, zodat de vinger niet een beetje beweegt en het sleepoordeel niet optreedt als gevolg van de werking van gewoon aanraken.

Deze keer, omdat het een invoer is die ervan uitgaat dat u een fijnafstemmingsbewerking wilt uitvoeren, schakelt u deze drempelinstelling uit en maakt u onmiddellijk een sleepoordeel. eventData.useDragThreshold false U kunt de drempel overschrijven door in te stellen op .

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

Hieronder staan de gebeurtenissen die worden genoemd bij respectievelijk aanraken, slepen en loslaten. OnPointerDown OnDrag Aangezien en , voert elk dezelfde invoerverwerking uit Operate , dus we maken een afzonderlijke methode en noemen deze aan. OnPointerUp Geef het nu door aan de methode om het besturingselement te SendValueToControl Vector2.zero laten weten dat het is gestopt met typen.

/// <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 De methode is de kern D-pad input operatie.

Allereerst kan de aanraakpositie worden verkregen met , maar aangezien deze coördinaat de coördinaat van het eventData.position spelscherm is, RectTransformUtility.ScreenPointToLocalPointInRectangle methode om te converteren naar canvascoördinaten. De door te geven waarden zijn "canvas", "touch position", "camera" en "receiving variable" RectTransform.

Nadat u de "aanraakpositie op het canvas" hebt gekregen, converteert u deze naar de positie van de knop als de oorsprong. Trek gewoon de positie () van het_objectPosition object af.

Aangezien de aanraakpositie nog steeds een getal op het canvas is, converteert u deze waarde vervolgens naar een verhouding van 0 ~ 1. Als u deelt door de helft van de grootte van de knop, is de aanraakpositie binnen het bereik van de knop 0 ~ 1. Als u sleept met aanraken, is de waarde 1 of meer omdat deze bewerkingen accepteert, zelfs buiten de knop. Vector2.ClampMagnitude Doe dit in de methode zodat deze niet hoger is dan 1.

Geef ten slotte de invoerwaarde SendValueToControl geconverteerd naar 0 ~ 1 door aan de methode. De On-Screen Control doet de rest voor je.

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

Voeg het script dat u hebt gemaakt toe aan de knop. Deze actie wordt behandeld als de bediening van de linkerstick van de Gamepad.

Probeer het spel te verplaatsen en raak de knoppen aan. Ik denk dat je kunt zien dat de verkregen waarde verandert afhankelijk van de aanraakpositie. U kunt ook zien dat zelfs als u sleept tijdens het aanraken, de waarde verandert afhankelijk van de sleeppositie.