Personnaliser le contrôle à l’écran pour implémenter le pavé directionnel

Page mise à jour :
Date de création de la page :

Environnement de vérification

Windows
  • Fenêtres 11
Éditeur Unity
  • 2020.3.25f1
Package système d’entrée
  • 1.2.0

Conditions préalables à cette astuce

Les paramètres suivants ont été définis à l’avance comme prémisse pour la description de cette astuce.

Vous devez également connaître les conseils suivants :

Le Control Stick à l’écran est l’opération d’orientation standard

Le bâton à l’écran est implémenté comme un bâton virtuel, reproduisant le fonctionnement d’un bâton similaire à celui trouvé sur un contrôleur physique. Par exemple, si vous souhaitez le déplacer vers la droite, vous pouvez d’abord toucher l’écran pour toucher le bâton, puis le faire glisser vers la droite pour faire tomber le bâton.

Il est facile à imaginer comme une opération de bâton, mais au contraire, lorsque vous voulez le déplacer vers la droite immédiatement, (1) toucher, (2) glisser vers la droite, Comme cela nécessite une opération en deux étapes, la réponse sera inévitablement un peu retardée. Cela est particulièrement vrai lorsque vous devez effectuer un changement de direction rapide à la suite.

D-pad qui détermine la direction au moment du toucher (non standard)

La meilleure méthode d’entrée pour une entrée de direction rapide est celle qui peut déterminer la direction au moment du toucher en plaçant quelque chose comme un pavé directionnel ou une touche fléchée. Ce n’est pas fait par Unity, mais dans mon jeu Little Saber, je place les touches fléchées pour que vous puissiez vous déplacer rapidement dans la direction que vous touchez. Bien sûr, vous pouvez également glisser vers le haut ou vers la gauche en touchant pour changer de mouvement.

Cependant, la norme de contrôle à l’écran n’est pas facile à placer et à mettre en œuvre car il n’y a que des bâtons à l’écran et des boutons à l’écran.

Vous pouvez également créer un pseudo-D-pad en organisant quatre boutons comme illustré dans la figure ci-dessous, mais cela n’est pas pratique car vous ne pouvez pas entrer en diagonale. Si vous placez 8 boutons, l’opération diagonale est possible, mais une opération de direction d’écoulement telle que « ← ↙ ↓ » n’est toujours pas possible.

Créer un pavé directionnel avec personnalisation du contrôle à l’écran

Comme vous pouvez le voir, le contrôle à l’écran n’est livré en standard qu’avec Stick et Button, mais vous pouvez personnaliser les fonctionnalités manquantes avec vos propres scripts. Donc, ici, je voudrais créer un pavé directionnel avec personnalisation du contrôle à l’écran.

Carte d’action

Nous utiliserons la carte d’action, mais nous utiliserons celle créée dans les conseils précédents telle quelle, de sorte que la procédure est omise.

Vous avez également écrit du code.

Positionnement des objets

Placez une zone de texte pour afficher votre entrée et un bouton qui remplace le pavé directionnel. Dans ce cas, les boutons sont disposés, mais vous pouvez les remplacer par une image plus facile à comprendre.

Script pour afficher l’entrée

Étant donné que le contrôle à l’écran remplace le tactile par l’interaction physique du contrôleur, Créez un script qui affiche votre entrée dans le texte pendant que la carte d’action fonctionne.

Le contenu est le même qu’avant, donc je vais omettre l’explication.

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

Après l’avoir configuré, vérifiez d’abord s’il fonctionne avec votre clavier ou votre manette de jeu.

Personnalisation des contrôles à l’écran

Cela nous amène au sujet principal de ces conseils. La personnalisation du contrôle visuel est un script, créez donc d’abord un script. Le nom est arbitraire, mais dans ce cas OnScreenDpad , il est .

Le script ressemble à ceci :

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

Vous créez une OnScreenControl classe de personnalisation Contrôle visuel en héritant de la classe Personnaliser. En outre, pour recevoir divers événements tactiles, héritez de l’interface cible. Ici, « lors du toucher », « en se déplaçant en touchant », « lorsque le toucher est relâché » et « avant de glisser » sont traités, de sorte que les interfaces suivantes sont également décrites respectivement.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Contenu de l’interface
IPointerDownHandler Lorsqu’on le touche
IPointerUpHandler Lorsque vous relâchez le toucher
IDragHandler Lorsque vous bougez en touchant
IInitializePotentialDragHandler Avant de commencer à faire glisser

Les champs suivants sont déclarés :

controlPathInternalOnScreenControl est obligatoire si vous héritez d’une classe. Il contient une chaîne de quel bouton du périphérique d’entrée mapper, mais fondamentalement, vous pouvez l’écrire tel quel. InputControl Toutefois, l’attribut doit contenir la valeur à conserver (Vector2 dans ce cas).

_objectPosition_objectSizeHalf et maintenez la moitié de la position et de la taille du bouton à l’avance et utilisez-le plus tard pour les calculs.

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

La méthode, appelée Start après l’initialisation de l’objet, obtient la moitié de la position et de la taille du bouton sur la zone de travail. La taille prend également en compte l’échelle. Start Il est traité par une méthode, mais s’il peut être utilisé correctement dans le calcul à la fin, le moment de l’acquisition peut être n’importe où.

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

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

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

OnInitializePotentialDrag est appelé lorsque vous souhaitez commencer à faire glisser après le toucher. Lors du glissement, OnDrag la méthode est appelée. Un seuil est défini pour empêcher le jugement de traînée dans une certaine mesure afin que le doigt ne bouge pas un peu et que le jugement de traînée ne se produise pas en raison de l’opération de simple toucher.

Cette fois, puisqu’il s’agit d’une entrée qui suppose que vous souhaitez effectuer une opération de réglage fin, désactivez ce paramètre de seuil et effectuez immédiatement un jugement de glissement. eventData.useDragThreshold false Vous pouvez remplacer le seuil en définissant la valeur .

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

Vous trouverez ci-dessous les événements appelés lors du toucher, du glissement et du relâchement respectivement. OnPointerDown OnDrag Puisque et , chacun effectue Operate le même traitement d’entrée, nous créons donc une méthode distincte et l’appelons. OnPointerUp Maintenant, passez-le à la méthode pour SendValueToControl Vector2.zero notifier au contrôle qu’il a cessé de taper.

/// <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 La méthode est l’opération d’entrée D-pad de base.

Tout d’abord, la position tactile peut être obtenue avec , mais comme cette coordonnée est la coordonnée de l’écran eventData.position de jeu, RectTransformUtility.ScreenPointToLocalPointInRectangle Méthode de conversion en coordonnées de canevas. Les valeurs à passer sont « canvas », « touch position », « camera » et «  RectTransformreceiver variable ».

Après avoir obtenu la « position tactile sur la toile », convertissez-la à la position du bouton comme origine. Il suffit de soustraire la position () de l’objet_objectPosition.

Ensuite, puisque la position tactile est toujours un nombre sur la zone de travail, convertissez cette valeur en un rapport de 0 ~ 1. Si vous divisez par la moitié de la taille du bouton, la position tactile dans la plage du bouton sera 0 ~ 1. Si vous faites glisser avec une interface tactile, la valeur sera égale ou supérieure à 1 car elle accepte les opérations même en dehors du bouton. Vector2.ClampMagnitude Faites-le dans la méthode afin qu’il ne dépasse pas 1.

Enfin, passez la valeur SendValueToControl d’entrée convertie en 0~1 à la méthode. Le contrôle à l’écran fait le reste pour vous.

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

Joignez le script que vous avez créé au bouton. Cette action est configurée pour être traitée comme le fonctionnement du stick gauche de la manette de jeu.

Essayez de déplacer le jeu et appuyez sur les boutons. Je pense que vous pouvez voir que la valeur obtenue change en fonction de la position tactile. En outre, vous pouvez voir que même si vous faites glisser en touchant, la valeur change en fonction de la position du glissement.