Personalizar el control en pantalla para implementar el cruceta

Actualización de la página :
Fecha de creación de la página :

Entorno de verificación

Windows
  • Ventanas 11
Unity Editor
  • 2020.3.25f1
Paquete del sistema de entrada
  • 1.2.0

Requisitos previos para esta sugerencia

Los siguientes ajustes se han realizado de antemano como premisa para la descripción de este consejo.

También debe estar familiarizado con los siguientes consejos:

La palanca de control en pantalla es la operación de orientación estándar

El On-Screen Stick se implementa como un stick virtual, replicando el funcionamiento de un stick similar al que se encuentra en un controlador físico. Por ejemplo, si desea moverlo hacia la derecha, primero puede tocar la pantalla para tocar el palo y luego deslizarlo hacia la derecha para derribar el palo.

Es fácil de imaginar como una operación de palo, pero por el contrario, cuando quieras moverlo hacia la derecha inmediatamente, (1) toque, (2) deslice hacia la derecha, Dado que requiere una operación de dos pasos, la respuesta inevitablemente se retrasará un poco. Esto es especialmente cierto cuando necesita hacer un cambio rápido de dirección en una fila.

D-pad que determina la dirección en el momento del tacto (no estándar)

El mejor método de entrada para la entrada de dirección rápida es aquel que puede determinar la dirección en el momento de tocar colocando algo como un D-pad o una tecla de flecha. No está hecho por Unity, pero en mi juego Little Saber, coloco las teclas de flecha para que puedas moverte rápidamente en la dirección que tocas. Por supuesto, también puede deslizarse hacia arriba o hacia la izquierda mientras toca para cambiar los movimientos.

Sin embargo, el estándar de control en pantalla no es fácil de colocar e implementar porque solo hay sticks en pantalla y botones en pantalla.

También puede crear un pseudo-D-pad organizando cuatro botones como se muestra en la figura siguiente, pero es inconveniente porque no puede ingresar diagonalmente. Si coloca 8 botones, la operación diagonal es posible, pero la operación de dirección de flujo como "← ↙ ↓" aún no es posible.

Crear un panel direccional con la personalización del control en pantalla

Como puede ver, el control en pantalla solo viene de serie con Stick y Button, pero puede personalizar las funciones que faltan con sus propios scripts. Así que aquí me gustaría crear un pad direccional con personalización de control en pantalla.

Mapa de acción

Usaremos el mapa de acción, pero usaremos el creado en los consejos anteriores tal cual, por lo que se omite el procedimiento.

También has escrito algo de código.

Posicionamiento de objetos

Coloque un área de texto para mostrar su entrada y un botón que reemplace el D-pad. En este caso, los botones están organizados, pero puede reemplazarlos con una imagen que sea más fácil de entender.

Script para mostrar la entrada

Debido a que el control en pantalla reemplaza el tacto con la interacción física del controlador, Cree un script que muestre su entrada en texto a medida que funciona el mapa de acción.

El contenido es el mismo que antes, así que omitiré la explicación.

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

Después de configurarlo, primero verifique si funciona con su teclado o gamepad.

Personalización de controles en pantalla

Esto nos lleva al tema principal de estos consejos. La personalización del control en pantalla es un script, así que primero cree un script. El nombre es arbitrario, pero en este caso OnScreenDpad es .

El script tiene este aspecto:

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

Crear una OnScreenControl clase de personalización de control en pantalla heredando de la clase Personalizar. Además, para recibir varios eventos táctiles, herede la interfaz de destino. Aquí, se procesan "al tocar", "al moverse mientras se toca", "cuando se suelta el toque" y "antes de arrastrar", por lo que también se describen las siguientes interfaces respectivamente.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Contenido de la interfaz
IPointerDownHandler Cuando se toca
IPointerUpHandler Cuando sueltas el toque
IDragHandler Cuando te mueves mientras tocas
IInitializePotentialDragHandler Antes de empezar a arrastrar

Se declaran los siguientes campos:

controlPathInternalOnScreenControl es necesario si hereda de una clase. Contiene una cadena de qué botón del dispositivo de entrada asignar, pero básicamente puede escribirlo tal como está. InputControl Sin embargo, el atributo debe contener el valor que se va a conservar (Vector2 en este caso).

_objectPosition_objectSizeHalf y mantenga la mitad de la posición y el tamaño del botón por adelantado y úselo más tarde para los cálculos.

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

El método, llamado Start después de inicializar el objeto, obtiene la mitad de la posición y el tamaño del botón en el lienzo. El tamaño también tiene en cuenta la escala. Start Se procesa mediante un método, pero si se puede usar correctamente en el cálculo al final, el tiempo de adquisición puede ser en cualquier lugar.

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

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

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

OnInitializePotentialDrag se llama cuando desea comenzar a arrastrar después del tacto. Al arrastrar, OnDrag se llama al método. Se establece un umbral para evitar el juicio de arrastre hasta cierto punto para que el dedo no se mueva un poco y el juicio de arrastre no ocurra debido a la operación de simplemente tocar.

Esta vez, dado que es una entrada que asume que desea realizar una operación de ajuste fino, deshabilite esta configuración de umbral e inmediatamente haga un juicio de arrastre. eventData.useDragThreshold false Puede invalidar el umbral estableciendo en .

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

A continuación se muestran los eventos llamados al tocar, arrastrar y soltar, respectivamente. OnPointerDown OnDrag Dado que y , cada uno realiza Operate el mismo procesamiento de entrada, por lo que creamos un método separado y lo llamamos. OnPointerUp Ahora, páselo al método para notificar al SendValueToControl Vector2.zero control que ha dejado de escribir.

/// <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 El método es la operación de entrada del D-pad principal.

En primer lugar, la posición táctil se puede obtener con , pero como esta coordenada es la coordenada de la eventData.position pantalla del juego, RectTransformUtility.ScreenPointToLocalPointInRectangle Método para convertir a coordenadas de lienzo. Los valores a pasar son "canvas", "touch position", "camera" y " RectTransformreceiving variable".

Después de obtener la "posición táctil en el lienzo", conviértala a la posición del botón como origen. Simplemente reste la posición () del_objectPosition objeto.

A continuación, dado que la posición táctil sigue siendo un número en el lienzo, convierta este valor en una proporción de 0 ~ 1. Si divide por la mitad el tamaño del botón, la posición táctil dentro del rango del botón será 0 ~ 1. Si arrastra con la función táctil, el valor será 1 o más porque acepta operaciones incluso fuera del botón. Vector2.ClampMagnitude Haga esto en el método para que no exceda 1.

Finalmente, pase el valor SendValueToControl de entrada convertido a 0 ~ 1 al método. El control en pantalla hace el resto por usted.

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

Adjunte el script que creó al botón. Esta acción está configurada para ser tratada como el funcionamiento del stick izquierdo del Gamepad.

Intenta mover el juego y toca los botones. Creo que se puede ver que el valor obtenido cambia dependiendo de la posición táctil. Además, puede ver que incluso si arrastra mientras toca, el valor cambia dependiendo de la posición de arrastre.