Prispôsobenie ovládacieho prvku na obrazovke na implementáciu smerového ovládača
Prostredie overovania
- Windows
-
- Windows 11
- Editor jednoty
-
- 2020.3.25f1
- Vstupný systémový balík
-
- 1.2.0
Predpoklady pre tento tip
Nasledujúce nastavenia boli vykonané vopred ako predpoklad pre popis tohto tipu.
Mali by ste byť tiež oboznámení s nasledujúcimi tipmi:
- Použitie akčných máp na priradenie tlačidiel k hernému správaniu
- Používanie ovládacích prvkov vstupu optimalizovaných na dotyk s ovládaním na obrazovke
Ovládacia páčka na obrazovke je štandardná orientačná operácia
On-Screen Stick je implementovaný ako virtuálna palica, ktorá replikuje činnosť palice podobnej tej, ktorá sa nachádza na fyzickom ovládači. Ak ju napríklad chcete presunúť doprava, môžete sa najprv dotknúť obrazovky, aby ste sa dotkli páčky, a potom ju posunutím doprava zraziť.
Je ľahké si to predstaviť ako operáciu palice, ale naopak, keď ju chcete okamžite presunúť doprava, (1) dotknite sa, (2) posuňte doprava, Keďže vyžaduje dvojstupňovú operáciu, reakcia bude nevyhnutne trochu oneskorená. To platí najmä vtedy, keď potrebujete urobiť rýchlu zmenu smeru v rade.
Smerový ovládač, ktorý určuje smer dotyku (neštandardný)
Najlepšia metóda vstupu pre rýchle zadávanie smeru je taká, ktorá dokáže určiť smer v okamihu dotyku umiestnením niečoho ako smerový ovládač alebo kláves so šípkou. Nie je vyrobený spoločnosťou Unity, ale v mojej hre Little Saber umiestňujem klávesy so šípkami, aby ste sa mohli rýchlo pohybovať smerom, ktorého sa dotknete. Samozrejme, môžete sa tiež posunúť nahor alebo doľava pri dotyku a prepínať pohyby.
Štandard ovládania na obrazovke však nie je ľahké umiestniť a implementovať, pretože existujú iba páčky na obrazovke a tlačidlá na obrazovke.
Môžete tiež vytvoriť pseudo-D-pad usporiadaním štyroch tlačidiel, ako je znázornené na obrázku nižšie, ale je to nepohodlné, pretože nemôžete zadávať diagonálne. Ak umiestnite 8 tlačidiel, diagonálne ovládanie je možné, ale operácia smeru prúdenia, napríklad "← ↙ ↓", stále nie je možná.
Vytvorenie smerového ovládača s prispôsobením ovládania na obrazovke
Ako vidíte, ovládanie na obrazovke je štandardne dodávané iba s Stick and Button, ale chýbajúce funkcie môžete prispôsobiť vlastnými skriptami. Takže tu by som chcel vytvoriť smerový ovládač s prispôsobením ovládania na obrazovke.
Akčný plán
Použijeme akčnú mapu, ale použijeme mapu vytvorenú v predchádzajúcich tipoch tak, ako je, takže postup je vynechaný.
Napísali ste aj nejaký kód.
Umiestňovanie objektov
Umiestnite textovú oblasť na zobrazenie vstupu a tlačidlo, ktoré nahradí smerový ovládač. V takom prípade sú tlačidlá usporiadané, ale môžete ich nahradiť obrázkom, ktorý je zrozumiteľnejší.
Skript na zobrazenie vstupu
Keďže ovládanie na obrazovke nahrádza dotyk fyzickou interakciou ovládača, Vytvorte skript, ktorý zobrazuje váš vstup v texte počas práce mapy akcií.
Obsah je rovnaký ako predtým, takže vysvetlenie vynechám.
- Použitie akčných máp na priradenie tlačidiel k hernému správaniu
- Používanie ovládacích prvkov vstupu optimalizovaných na dotyk s ovládaním na obrazovke
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}";
}
}
Po nastavení najskôr skontrolujte, či funguje s klávesnicou alebo gamepadom.
Prispôsobenie ovládacích prvkov na obrazovke
To nás privádza k hlavnej téme týchto tipov.
Prispôsobenie ovládacieho prvku na obrazovke je skript, preto najprv vytvorte skript.
Názov je ľubovoľný, ale v tomto prípade OnScreenDpad
je to .
Skript vyzerá takto:
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);
}
}
Triedu OnScreenControl
prispôsobenia ovládania na obrazovke vytvoríte dedením z triedy prispôsobenia.
Okrem toho, ak chcete prijímať rôzne dotykové udalosti, zdediť cieľové rozhranie.
Tu sa spracovávajú "pri dotyku", "pri pohybe pri dotyku", "pri uvoľnení dotyku" a "pred ťahaním", takže sú popísané aj nasledujúce rozhrania.
public class OnScreenDpad
: OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Obsah rozhrania | |
---|---|
IPointerDownHandler | Pri dotyku |
IPointerUpHandler | Keď dotyk uvoľníte |
IDragHandler | Keď sa pohybujete pri dotyku |
IInitializePotentialDragHandler | Skôr než začnete presúvať |
Deklarujú sa tieto polia:
controlPathInternal
OnScreenControl
sa vyžaduje, ak dedíte z triedy.
Obsahuje reťazec, na ktoré tlačidlo vstupného zariadenia sa má mapovať, ale v podstate ho môžete napísať tak, ako je.
InputControl
Tento atribút by však mal obsahovať hodnotu, ktorá sa má zachovať (v tomto prípade Vector2).
_objectPosition
_objectSizeHalf
a vopred podržte polovicu polohy a veľkosti tlačidla a neskôr ho použite na výpočty.
[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;
Metóda, nazývaná Start
po inicializácii objektu, získa polovicu polohy a veľkosti tlačidla na plátne.
Veľkosť zohľadňuje aj stupnicu.
Start
Spracováva sa metódou, ale ak sa dá správne použiť pri výpočte na konci, načasovanie akvizície môže byť kdekoľvek.
// <summary>
// オブジェクトが動作する最初のタイミングで1回だけ呼ばれます。
// </summary>
private void Start()
{
var rectTransform = (RectTransform)base.transform;
// オブジェクトの位置を取得
_objectPosition = rectTransform.anchoredPosition;
// オブジェクトのサイズの半分を取得 (スケールサイズも考慮)
_objectSizeHalf = rectTransform.sizeDelta * rectTransform.localScale / 2f;
}
OnInitializePotentialDrag
sa volá, keď chcete začať ťahať po dotyku.
Počas ťahania OnDrag
sa metóda nazýva.
Prahová hodnota je nastavená tak, aby do určitej miery zabránila úsudku ťahu, aby sa prst trochu nepohyboval a úsudok ťahu nenastal v dôsledku operácie jednoduchého dotyku.
Tentokrát, keďže ide o vstup, ktorý predpokladá, že chcete vykonať operáciu jemného doladenia, vypnite toto nastavenie prahovej hodnoty a okamžite vykonajte úsudok ťahania.
eventData.useDragThreshold
false
Prahovú hodnotu môžete prepísať nastavením na .
<summary>ドラッグの初期化処理として呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnInitializePotentialDrag(PointerEventData eventData)
{
// タッチのスライド操作を即座に発生させたいのでドラッグ開始までの閾値を無効にします
eventData.useDragThreshold = false;
}
Nižšie sú uvedené udalosti, ktoré sa nazývajú pri dotyku, pretiahnutí a uvoľnení.
OnPointerDown
OnDrag
Keďže a , každý vykonáva Operate
rovnaké spracovanie vstupov, preto vytvoríme samostatnú metódu a nazveme ju.
OnPointerUp
Teraz ho odovzdajte metóde, aby ste SendValueToControl
Vector2.zero
upozornili ovládací prvok, že prestal písať.
<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
Metóda je vstupná operácia jadra smerového ovládača.
Po prvé, dotykovú polohu je možné získať pomocou , ale keďže táto súradnica je súradnicou hernej eventData.position
obrazovky,
RectTransformUtility.ScreenPointToLocalPointInRectangle
metóda prevodu na súradnice plátna.
Hodnoty, ktoré sa majú odovzdať, sú "plátno", "dotyková pozícia", "fotoaparát" a " RectTransform
prijímacia premenná".
Po získaní "dotykovej polohy na plátne" ju preveďte na polohu tlačidla ako počiatok.
Stačí odpočítať polohu () objektu_objectPosition
.
Ďalej, keďže dotyková pozícia je stále číslom na plátne, skonvertujte túto hodnotu na pomer 0 ~ 1.
Ak vydelíte polovicou veľkosti tlačidla, dotyková poloha v dosahu tlačidla bude 0~1.
Ak ťaháte dotykom, hodnota bude 1 alebo viac, pretože prijíma operácie aj mimo tlačidla.
Vector2.ClampMagnitude
Urobte to v metóde tak, aby neprekročila 1.
Nakoniec odovzdajte vstupnú hodnotu SendValueToControl
prevedenú na 0 ~ 1 metóde.
Ovládanie na obrazovke sa postará o zvyšok za vás.
<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);
}
Pripojte vytvorený skript k tlačidlu. Táto akcia sa má považovať za činnosť ľavej páčky gamepadu.
Skúste hru presunúť a dotknúť sa tlačidiel. Myslím, že môžete vidieť, že získaná hodnota sa mení v závislosti od dotykovej polohy. Môžete tiež vidieť, že aj keď ťaháte počas dotyku, hodnota sa mení v závislosti od polohy ťahania.