Dostosuj sterowanie ekranowe, aby zaimplementować pad kierunkowy
Środowisko weryfikacji
- Windows
-
- Okna 11
- Edytor Unity
-
- 2020.3.25f1
- Pakiet systemu wejściowego
-
- 1.2.0
Wymagania wstępne dotyczące tej porady
Poniższe ustawienia zostały wcześniej wprowadzone jako przesłanka do opisu tej wskazówki.
Powinieneś również zapoznać się z następującymi wskazówkami:
- Używanie map akcji do przypisywania przycisków do zachowań w grze
- Używanie elementów sterujących wprowadzaniem zoptymalizowanych pod kątem dotyku dzięki funkcji sterowania ekranowego
Drążek sterujący na ekranie to standardowa operacja orientacji
On-Screen Stick jest zaimplementowany jako wirtualny drążek, replikując działanie kija podobnego do tego, które można znaleźć na kontrolerze fizycznym. Na przykład, jeśli chcesz przesunąć go w prawo, możesz najpierw dotknąć ekranu, aby dotknąć drążka, a następnie przesunąć go w prawo, aby powalić drążek.
Łatwo to sobie wyobrazić jako operację drążka, ale wręcz przeciwnie, gdy chcesz natychmiast przesunąć go w prawo, (1) dotknąć, (2) przesunąć w prawo, Ponieważ wymaga to dwuetapowej operacji, reakcja nieuchronnie będzie nieco opóźniona. Jest to szczególnie ważne, gdy musisz szybko zmienić kierunek z rzędu.
D-pad określający kierunek w momencie dotyku (niestandardowy)
Najlepsza metoda wprowadzania szybkiego kierunku to taka, która może określić kierunek w momencie dotknięcia, umieszczając coś w rodzaju pada kierunkowego lub strzałki. Nie jest to zrobione przez Unity, ale w mojej grze Little Saber umieszczam strzałek, abyś mógł szybko poruszać się w kierunku, którego dotykasz. Oczywiście możesz również przesuwać w górę lub w lewo podczas dotykania, aby przełączać ruchy.
Jednak standard sterowania ekranowego nie jest łatwy do umieszczenia i wdrożenia, ponieważ istnieją tylko drążki ekranowe i przyciski ekranowe.
Możesz także utworzyć pseudo-D-pad, układając cztery przyciski, jak pokazano na poniższym rysunku, ale jest to niewygodne, ponieważ nie można wprowadzać po przekątnej. Jeśli umieścisz 8 przycisków, możliwa jest operacja po przekątnej, ale operacja kierunku przepływu, taka jak "← ↙ ↓", nadal nie jest możliwa.
Tworzenie padu kierunkowego z dostosowywaniem sterowania ekranowego
Jak widać, sterowanie ekranowe jest standardowo dostarczane tylko z drążkiem i przyciskiem, ale możesz dostosować brakujące funkcje za pomocą własnych skryptów. Więc tutaj chciałbym stworzyć pad kierunkowy z dostosowaniem sterowania ekranowego.
Plan działania
Wykorzystamy mapę akcji, ale użyjemy tej stworzonej w poprzednich wskazówkach taką, jaka jest, więc procedura zostanie pominięta.
Napisałeś też trochę kodu.
Pozycjonowanie obiektów
Umieść obszar tekstowy, aby wyświetlić wprowadzone dane wejściowe, oraz przycisk, który zastępuje pad kierunkowy. W takim przypadku przyciski są rozmieszczone, ale można je zastąpić obrazem, który jest łatwiejszy do zrozumienia.
Skrypt do wyświetlania danych wejściowych
Ponieważ sterowanie ekranowe zastępuje dotyk fizyczną interakcją z kontrolerem, Utwórz skrypt, który wyświetla dane wejściowe w tekście podczas działania mapy działania.
Treść jest taka sama jak poprzednio, więc pominę wyjaśnienie.
- Używanie map akcji do przypisywania przycisków do zachowań w grze
- Używanie elementów sterujących wprowadzaniem zoptymalizowanych pod kątem dotyku dzięki funkcji sterowania ekranowego
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 skonfigurowaniu najpierw sprawdź, czy działa z klawiaturą lub gamepadem.
Dostosowywanie elementów sterujących na ekranie
To prowadzi nas do głównego tematu tych wskazówek.
Dostosowywanie formantu ekranowego jest skryptem, więc najpierw utwórz skrypt.
Nazwa jest arbitralna, ale w tym przypadku OnScreenDpad
jest .
Skrypt wygląda tak:
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);
}
}
Klasę OnScreenControl
dostosowywania Formant ekranowy tworzy się, dziedzicząc z klasy Dostosuj.
Ponadto, aby odbierać różne zdarzenia dotykowe, dziedzicz interfejs docelowy.
Tutaj przetwarzane są "podczas dotykania", "podczas poruszania się podczas dotykania", "po zwolnieniu dotyku" i "przed przeciąganiem", więc opisane są również następujące interfejsy.
public class OnScreenDpad
: OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Zawartość interfejsu | |
---|---|
IPointerDownHandler | Po dotknięciu |
IPointerUpHandler | Po zwolnieniu przycisku dotykowego |
IDragHandler | Gdy poruszasz się podczas dotykania |
IInitializePotentialDragHandler | Przed rozpoczęciem przeciągania |
Następujące pola są deklarowane:
controlPathInternal
OnScreenControl
jest wymagany, jeśli dziedziczysz po klasie.
Zawiera ciąg przycisków urządzenia wejściowego, na który należy zmapować, ale w zasadzie można go zapisać takim, jakim jest.
InputControl
Atrybut powinien jednak zawierać wartość, która ma zostać zachowana (w tym przypadku Vector2).
_objectPosition
_objectSizeHalf
i przytrzymaj wcześniej połowę pozycji i rozmiaru przycisku i użyj go później do obliczeń.
[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;
Metoda, wywoływana Start
po zainicjowaniu obiektu, uzyskuje połowę położenia i rozmiaru przycisku na obszarze roboczym.
Rozmiar uwzględnia również skalę.
Start
Jest przetwarzany przez metodę, ale jeśli można go poprawnie wykorzystać w obliczeniach na końcu, czas akwizycji może być w dowolnym miejscu.
// <summary>
// オブジェクトが動作する最初のタイミングで1回だけ呼ばれます。
// </summary>
private void Start()
{
var rectTransform = (RectTransform)base.transform;
// オブジェクトの位置を取得
_objectPosition = rectTransform.anchoredPosition;
// オブジェクトのサイズの半分を取得 (スケールサイズも考慮)
_objectSizeHalf = rectTransform.sizeDelta * rectTransform.localScale / 2f;
}
OnInitializePotentialDrag
jest wywoływany, gdy chcesz rozpocząć przeciąganie po dotknięciu.
Podczas przeciągania OnDrag
wywoływana jest metoda.
Próg jest ustawiony, aby w pewnym stopniu zapobiec osądowi przeciągania, tak aby palec nie poruszał się trochę, a osąd przeciągania nie występował z powodu operacji zwykłego dotykania.
Tym razem, ponieważ jest to dane wejściowe, które zakładają, że chcesz wykonać operację dostrajania, wyłącz to ustawienie progu i natychmiast dokonaj oceny przeciągania.
eventData.useDragThreshold
false
Możesz zastąpić próg, ustawiając na .
<summary>ドラッグの初期化処理として呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnInitializePotentialDrag(PointerEventData eventData)
{
// タッチのスライド操作を即座に発生させたいのでドラッグ開始までの閾値を無効にします
eventData.useDragThreshold = false;
}
Poniżej znajdują się zdarzenia wywoływane odpowiednio podczas dotykania, przeciągania i zwalniania.
OnPointerDown
OnDrag
Ponieważ i , każdy wykonuje Operate
to samo przetwarzanie danych wejściowych, więc tworzymy osobną metodę i wywołujemy ją.
OnPointerUp
Teraz przekaż go do metody, aby SendValueToControl
Vector2.zero
powiadomić kontrolkę, że przestała pisać.
<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
Metoda jest podstawową operacją wejściową D-pad.
Przede wszystkim pozycję dotyku można uzyskać za pomocą , ale ponieważ ta współrzędna jest współrzędną eventData.position
ekranu gry,
RectTransformUtility.ScreenPointToLocalPointInRectangle
Metoda konwersji na współrzędne kanwy.
Wartości, które mają zostać przekazane, to "canvas", "touch position", "camera" i " RectTransform
receiving variable".
Po uzyskaniu "pozycji dotykowej na płótnie" przekonwertuj ją na pozycję przycisku jako źródło.
Wystarczy odjąć pozycję ()_objectPosition
obiektu.
Następnie, ponieważ pozycja dotykowa jest nadal liczbą na kanwie, przekonwertuj tę wartość na stosunek 0 ~ 1.
Jeśli podzielisz przez połowę rozmiaru przycisku, pozycja dotyku w zakresie przycisku wyniesie 0 ~ 1.
Jeśli przeciągniesz dotykiem, wartość będzie wynosić 1 lub więcej, ponieważ akceptuje operacje nawet poza przyciskiem.
Vector2.ClampMagnitude
Zrób to w metodzie, aby nie przekraczała 1.
Na koniec przekaż wartość SendValueToControl
wejściową przekonwertowaną na 0 ~ 1 do metody.
Sterowanie ekranowe zrobi resztę za Ciebie.
<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);
}
Dołącz utworzony skrypt do przycisku. Ta akcja jest ustawiona jako działanie lewego drążka gamepada.
Spróbuj przenieść grę i dotknij przycisków. Myślę, że widać, że uzyskana wartość zmienia się w zależności od pozycji dotyku. Widać również, że nawet jeśli przeciągniesz podczas dotykania, wartość zmienia się w zależności od pozycji przeciągania.