A képernyőn megjelenő vezérlő testreszabása az I-választó megvalósításához
Ellenőrzési környezet
- Windows
-
- Windows 11 esetén
- Unity-szerkesztő
-
- 2020.3.25f1
- Bemeneti rendszercsomag
-
- 1.2.0
A tipp előfeltételei
A következő beállításokat előre elvégeztük a tipp leírásának előfeltételeként.
Ismernie kell a következő tippeket is:
- Gombok hozzárendelése a játékviselkedéshez művelettérképek használatával
- Érintésre optimalizált beviteli vezérlők használata a Képernyő-vezérléssel
A képernyőn megjelenő vezérlőkar a szokásos tájolás
A képernyőn megjelenő botkormány virtuális botként van megvalósítva, amely a fizikai vezérlőhöz hasonló botkormány működését reprodukálja. Ha például jobbra szeretné mozgatni, először érintse meg a képernyőt a bot megérintéséhez, majd csúsztassa jobbra a bot leütéséhez.
Könnyen elképzelhető botos műveletként, de éppen ellenkezőleg, ha azonnal jobbra akarja mozgatni, (1) érintse meg, (2) csúsztassa jobbra, Mivel kétlépcsős műveletet igényel, a válasz elkerülhetetlenül kissé késik. Ez különösen igaz, ha gyorsan meg kell változtatnia az irányt egymás után.
I-választó, amely meghatározza az érintés pillanatában az irányt (nem szabványos)
A gyors iránybevitel legjobb beviteli módja az, amely az érintés pillanatában meg tudja határozni az irányt valami I-választó vagy nyílbillentyű elhelyezésével. Nem a Unity készítette, de a Little Saber játékomban úgy helyezem el a nyílbillentyűket, hogy gyorsan mozoghasson az érintett irányba. Természetesen érintés közben felfelé vagy balra is csúsztathatja a mozgások váltásához.
A képernyőn megjelenő vezérlési szabványt azonban nem könnyű elhelyezni és megvalósítani, mert csak a képernyőn megjelenő botok és a képernyőn megjelenő gombok vannak.
Pszeudo-I-választót is létrehozhat négy gomb elrendezésével az alábbi ábrán látható módon, de ez kényelmetlen, mert nem tud átlósan beírni. Ha 8 gombot helyez el, az átlós működés lehetséges, de az áramlási irányú működés, például a "← ↙ ↓" még mindig nem lehetséges.
Irányválasztó létrehozása a Képernyő-vezérlés testreszabásával
Mint láthatja, a képernyőn megjelenő vezérlés csak a Stick and Button funkcióval rendelkezik, de a hiányzó funkciókat saját szkriptekkel testreszabhatja. Tehát itt szeretnék létrehozni egy irányválasztót a képernyőn megjelenő vezérlés testreszabásával.
Műveleti térkép
A műveleti térképet fogjuk használni, de az előző tippekben létrehozottat fogjuk használni, így az eljárás kimarad.
Írtál néhány kódot is.
Objektumok pozicionálása
Helyezzen el egy szövegterületet a bevitel megjelenítéséhez, valamint egy gombot, amely helyettesíti az I-választót. Ebben az esetben a gombok el vannak rendezve, de helyettesítheti őket egy könnyebben érthető képpel.
Szkript a bemenet megjelenítéséhez
Mivel a Képernyő-vezérlés az érintést fizikai vezérlővel helyettesíti, Hozzon létre egy szkriptet, amely szövegesen jeleníti meg a bemenetet a művelettérkép működése közben.
A tartalom ugyanaz, mint korábban, ezért elhagyom a magyarázatot.
- Gombok hozzárendelése a játékviselkedéshez művelettérképek használatával
- Érintésre optimalizált beviteli vezérlők használata a Képernyő-vezérléssel
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}";
}
}
A beállítás után először ellenőrizze, hogy működik-e a billentyűzettel vagy a játékvezérlővel.
A képernyőn megjelenő vezérlők testreszabása
Ez elvezet minket a tippek fő témájához.
A Képernyőn megjelenő vezérlő testreszabása egy parancsfájl, ezért először hozzon létre egy szkriptet.
A név önkényes, de ebben az esetben OnScreenDpad
.
A szkript így néz ki:
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);
}
}
A Képernyő-vezérlő testreszabási osztályt OnScreenControl
a Customize osztálytól örökölve hozhatja létre.
Ezenkívül különféle érintési események fogadásához örökölje a célfelületet.
Itt a "megérintéskor", "érintés közben mozogva", "amikor az érintést elengedik" és "húzás előtt" kerülnek feldolgozásra, így a következő interfészeket is leírjuk.
public class OnScreenDpad
: OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Interfész | tartalma |
---|---|
IPointerDownHandler | Amikor megérinti |
IPointerUpHandler | Amikor elengedi az érintést |
IDragHandler | Amikor érintés közben mozog |
IInitializálPotenciálDragHandler | A húzás megkezdése előtt |
A következő mezők vannak deklarálva:
controlPathInternal
OnScreenControl
kötelező, ha egy osztálytól örököl.
Tartalmaz egy karakterláncot, hogy a beviteli eszköz melyik gombjához kell leképezni, de alapvetően úgy írhatja, ahogy van.
InputControl
Az attribútumnak azonban tartalmaznia kell a megtartandó értéket (ebben az esetben Vector2).
_objectPosition
_objectSizeHalf
és tartsa előre a gomb helyzetének és méretének felét, és később használja a számításokhoz.
[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;
Az objektum inicializálása után meghívott Start
metódus a képernyőn lévő gomb pozíciójának és méretének felét kapja.
A méret figyelembe veszi a skálát is.
Start
Ezt egy módszerrel dolgozzák fel, de ha végül helyesen használható a számításban, akkor az akvizíció időzítése bárhol lehet.
// <summary>
// オブジェクトが動作する最初のタイミングで1回だけ呼ばれます。
// </summary>
private void Start()
{
var rectTransform = (RectTransform)base.transform;
// オブジェクトの位置を取得
_objectPosition = rectTransform.anchoredPosition;
// オブジェクトのサイズの半分を取得 (スケールサイズも考慮)
_objectSizeHalf = rectTransform.sizeDelta * rectTransform.localScale / 2f;
}
OnInitializePotentialDrag
akkor hívódik meg, amikor érintés után el szeretné kezdeni a húzást.
Húzás OnDrag
közben a metódust hívják.
Egy küszöbérték van beállítva, hogy bizonyos mértékig megakadályozza a húzási ítéletet, hogy az ujj ne mozogjon egy kicsit, és a húzási ítélet ne forduljon elő az egyszerű érintés művelete miatt.
Ezúttal, mivel ez egy bemenet, amely feltételezi, hogy finomhangolási műveletet szeretne végrehajtani, tiltsa le ezt a küszöbérték-beállítást, és azonnal hozzon húzási ítéletet.
eventData.useDragThreshold
false
A küszöbértéket felülbírálhatja a értékre állításával.
<summary>ドラッグの初期化処理として呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnInitializePotentialDrag(PointerEventData eventData)
{
// タッチのスライド操作を即座に発生させたいのでドラッグ開始までの閾値を無効にします
eventData.useDragThreshold = false;
}
Az alábbiakban láthatók az érintés, húzás és elengedés során hívott események.
OnPointerDown
OnDrag
Mivel és , mindegyik ugyanazt a bemeneti feldolgozást hajtja végre Operate
, ezért létrehozunk egy külön metódust, és meghívjuk.
OnPointerUp
Most adja át a metódusnak, hogy SendValueToControl
Vector2.zero
értesítse a vezérlőt arról, hogy abbahagyta a gépelést.
<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
A módszer az alapvető I-választó bemeneti művelet.
Először is, az érintési pozíció a segítségével érhető el, de mivel ez a koordináta a eventData.position
játékképernyő koordinátája,
RectTransformUtility.ScreenPointToLocalPointInRectangle
vászonkoordinátákká konvertálási módszer.
Az átadandó értékek a következők: "vászon", "érintési pozíció", "kamera" és " RectTransform
fogadó változó".
Miután megkapta az "érintési pozíciót a vásznon", alakítsa át a gomb helyzetére origóként.
Csak vonja ki az_objectPosition
objektum helyzetét ().
Ezután, mivel az érintési pozíció továbbra is szám a vásznon, alakítsa át ezt az értéket 0~1 arányra.
Ha elosztja a gomb méretének felével, az érintési pozíció a gomb tartományán belül 0 ~ 1 lesz.
Ha érintéssel húzza, az érték 1 vagy több lesz, mert a gombon kívül is fogad műveleteket.
Vector2.ClampMagnitude
Tegye ezt a módszerben úgy, hogy ne haladja meg az 1-et.
Végül adja át a 0~1-re konvertált bemeneti értéket SendValueToControl
a metódusnak.
A többit a képernyőn megjelenő vezérlő végzi el Ön helyett.
<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);
}
Csatolja a létrehozott szkriptet a gombhoz. Ez a művelet úgy van beállítva, hogy a játékvezérlő bal karjának műveleteként kezelje.
Próbálja meg mozgatni a játékot, és érintse meg a gombokat. Azt hiszem, láthatja, hogy a kapott érték az érintési helyzettől függően változik. Azt is láthatja, hogy még akkor is, ha érintés közben húzza, az érték a húzási pozíciótól függően változik.