Personalitzeu el control en pantalla per implementar el D-pad
Entorn de verificació
- Windows
-
- Finestres 11
- Editor d'unitat
-
- 25.3.2020
- Paquet del sistema d'entrada
-
- 1.2.0
Requisits previs per a aquest consell
La configuració següent s'ha fet amb antelació com a premissa per a la descripció d'aquest consell.
També hauríeu de conèixer els consells següents:
- Utilitzar mapes d'accions per assignar botons als comportaments del joc
- Utilitzar controls d'entrada tàctils optimitzats amb el control en pantalla
On-Screen Control Stick és l'operació d'orientació estàndard
El On-Screen Stick s'implementa com un llapis virtual, replicant el funcionament d'un pal similar al que es troba en un controlador físic. Per exemple, si voleu moure'l cap a la dreta, primer podeu tocar la pantalla per tocar el pal i, a continuació, lliscar-lo cap a la dreta per tombar el pal.
És fàcil imaginar com una operació de pal, però al contrari, quan es vol moure cap a la dreta immediatament, (1) tocar, (2) lliscar cap a la dreta, Com que requereix una operació de dos passos, la resposta inevitablement es retardarà una mica. Això és especialment cert quan necessiteu fer un canvi ràpid de direcció seguit.
D-pad que determina la direcció en el moment del tacte (no estàndard)
El millor mètode d'entrada per a una entrada de direcció ràpida és aquell que pot determinar la direcció en el moment de tocar col·locant alguna cosa com un teclat D o una tecla de fletxa. No està fet per Unity, però en el meu joc Little Saber, col·loco les tecles de fletxa perquè puguis moure't ràpidament en la direcció que toques. Per descomptat, també podeu lliscar cap amunt o cap a l'esquerra mentre toqueu per canviar de moviment.
Tanmateix, l'estàndard de control en pantalla no és fàcil de col·locar i implementar perquè només hi ha pals a la pantalla i botons a la pantalla.
També podeu crear un pseudo-D-pad organitzant quatre botons com es mostra a la figura següent, però és inconvenient perquè no podeu introduir diagonalment. Si col·loqueu 8 botons, és possible operar en diagonal, però encara no és possible operar amb la direcció del flux com ara "← ↙ ↓".
Creeu un coixinet direccional amb la personalització del control en pantalla
Com podeu veure, el control en pantalla només ve de sèrie amb Stick and Button, però podeu personalitzar les funcions que falten amb els vostres propis scripts. Així que aquí m'agradaria crear un coixinet direccional amb personalització de control en pantalla.
Mapa d'accions
Utilitzarem el mapa d'accions, però utilitzarem tal com està el creat en els consells anteriors, de manera que s'omet el procediment.
També has escrit algun codi.
Col·locació d'objectes
Col·loqueu una àrea de text per mostrar l'entrada i un botó que substitueixi el coixinet D. En aquest cas, els botons estan organitzats, però podeu substituir-los per una imatge més fàcil d'entendre.
Script per mostrar l'entrada
Com que el control en pantalla substitueix el tacte per la interacció del controlador físic, Creeu un script que mostri l'entrada al text mentre funciona el mapa d'accions.
El contingut és el mateix que abans, així que ometreé l'explicació.
- Utilitzar mapes d'accions per assignar botons als comportaments del joc
- Utilitzar controls d'entrada tàctils optimitzats amb 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}";
}
}
Després de configurar-lo, comproveu primer si funciona amb el teclat o el gamepad.
Personalització dels controls en pantalla
Això ens porta al tema principal d'aquests consells.
La personalització del control en pantalla és un script, així que primer creeu un script.
El nom és arbitrari, però en aquest cas OnScreenDpad
és .
El guió té aquest aspecte:
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);
}
}
Creeu una OnScreenControl
classe de personalització de control en pantalla heretant de la classe Personalitza.
A més, per rebre diversos esdeveniments tàctils, hereteu la interfície de destinació.
Aquí, es processen "quan es toca", "quan es mou mentre es toca", "quan es deixa anar el tacte" i "abans d'arrossegar", de manera que també es descriuen respectivament les interfícies següents.
public class OnScreenDpad
: OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
de Continguts | la interfície |
---|---|
IPointerDownHandler | Quan es toca |
IPointerUpHandler | Quan deixeu anar el tacte |
IDragHandler | Quan et mous mentre toques |
IInitializePotentialDragHandler | Abans de començar a arrossegar |
Es declaren els següents camps:
controlPathInternal
OnScreenControl
és obligatori si heretes d'una classe.
Té una cadena de quin botó del dispositiu d'entrada assignar, però bàsicament podeu escriure-la tal com és.
InputControl
Tanmateix, l'atribut ha de contenir el valor que s'ha de conservar (Vector2 en aquest cas).
_objectPosition
_objectSizeHalf
i manteniu la meitat de la posició i la mida del botó per endavant i utilitzeu-lo més tard per als càlculs.
[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ètode, anomenat Start
després d'inicialitzar l'objecte, obté la meitat de la posició i la mida del botó del llenç.
La mida també té en compte l'escala.
Start
Es processa mitjançant un mètode, però si es pot utilitzar correctament en el càlcul al final, el temps d'adquisició pot estar en qualsevol lloc.
// <summary>
// オブジェクトが動作する最初のタイミングで1回だけ呼ばれます。
// </summary>
private void Start()
{
var rectTransform = (RectTransform)base.transform;
// オブジェクトの位置を取得
_objectPosition = rectTransform.anchoredPosition;
// オブジェクトのサイズの半分を取得 (スケールサイズも考慮)
_objectSizeHalf = rectTransform.sizeDelta * rectTransform.localScale / 2f;
}
OnInitializePotentialDrag
es diu quan es vol començar a arrossegar després de tocar-lo.
Mentre s'arrossega OnDrag
, s'anomena mètode.
S'estableix un llindar per evitar el judici d'arrossegament fins a cert punt perquè el dit no es mogui una mica i el judici d'arrossegament no es produeixi a causa de l'operació de simplement tocar.
Aquesta vegada, com que es tracta d'una entrada que assumeix que voleu realitzar una operació d'afinació, desactiveu aquesta configuració de llindar i feu immediatament un judici d'arrossegament.
eventData.useDragThreshold
false
Podeu substituir el llindar definint a .
<summary>ドラッグの初期化処理として呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnInitializePotentialDrag(PointerEventData eventData)
{
// タッチのスライド操作を即座に発生させたいのでドラッグ開始までの閾値を無効にします
eventData.useDragThreshold = false;
}
A continuació es mostren els esdeveniments anomenats en tocar, arrossegar i deixar anar, respectivament.
OnPointerDown
OnDrag
Com que i , cadascun realitza Operate
el mateix processament d'entrada, de manera que creem un mètode separat i l'anomenem.
OnPointerUp
Ara, passeu-lo al mètode per SendValueToControl
Vector2.zero
notificar al control que ha deixat d'escriure.
<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ètode és l'operació d'entrada D-pad del nucli.
En primer lloc, la posició tàctil es pot obtenir amb , però com que aquesta coordenada és la coordenada de la eventData.position
pantalla del joc,
RectTransformUtility.ScreenPointToLocalPointInRectangle
mètode per convertir en coordenades de llenç.
Els valors a passar són "llenç", "posició tàctil", "càmera" i " RectTransform
variable de recepció".
Després d'obtenir la "posició tàctil al llenç", convertiu-la a la posició del botó com a origen.
Només cal restar la posició () de l'objecte_objectPosition
.
A continuació, com que la posició tàctil encara és un número al llenç, convertiu aquest valor en una proporció de 0 ~ 1.
Si dividiu per la meitat la mida del botó, la posició tàctil dins del rang del botó serà 0 ~ 1.
Si arrossegueu amb el tacte, el valor serà 1 o més perquè accepta operacions fins i tot fora del botó.
Vector2.ClampMagnitude
Feu-ho en el mètode perquè no superi 1.
Finalment, passeu el valor SendValueToControl
d'entrada convertit a 0 ~ 1 al mètode.
El control en pantalla fa la resta per tu.
<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);
}
Adjunteu l'script que heu creat al botó. Aquesta acció està configurada per ser tractada com l'operació del pal esquerre del Gamepad.
Proveu de moure el joc i toqueu els botons. Crec que es pot veure que el valor obtingut canvia en funció de la posició tàctil. A més, podeu veure que, fins i tot si arrossegueu mentre toqueu, el valor canvia en funció de la posició d'arrossegament.