Personalizar el control en pantalla para implementar el cruceta
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:
- Usar mapas de acción para asignar botones a los comportamientos del juego
- Usar controles de entrada táctiles optimizados con el control en pantalla
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.
- Usar mapas de acción para asignar botones a los comportamientos del juego
- Usar controles de entrada táctiles optimizados con el control en pantalla
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:
controlPathInternal
OnScreenControl
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 " RectTransform
receiving 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.