Anpassa skärmkontrollen för att implementera styrknappen
Verifiering miljö
- Windows
-
- Fönster 11
- Unity-redaktör
-
- 2020.3.25F1
- Paket för inmatningssystem
-
- 1.2.0
Förutsättningar för det här tipset
Följande inställningar har gjorts i förväg som en förutsättning för beskrivningen av detta tips.
Du bör också känna till följande tips:
- Använda åtgärdskartor för att tilldela knappar till spelbeteenden
- Använda pekoptimerade inmatningskontroller med skärmkontroll
Kontrollsticka på skärmen är standardorienteringsoperationen
On-Screen Stick implementeras som en virtuell pinne som replikerar driften av en pinne som liknar den som finns på en fysisk styrenhet. Om du till exempel vill flytta den åt höger kan du först trycka på skärmen för att röra vid pinnen och sedan skjuta den åt höger för att slå ner pinnen.
Det är lätt att föreställa sig som en stickoperation, men tvärtom, när du vill flytta den till höger omedelbart, (1) peka, (2) glida åt höger, Eftersom det kräver en tvåstegsoperation kommer svaret oundvikligen att bli lite försenat. Detta gäller särskilt när du behöver göra en snabb riktningsändring i rad.
Styrknapp som bestämmer riktningen vid beröringstillfället (icke-standard)
Den bästa inmatningsmetoden för snabb riktningsinmatning är en som kan bestämma riktningen vid beröringstillfället genom att placera något som en D-pad eller piltangent. Det är inte gjort av Unity, men i mitt spel Little Saber placerar jag piltangenterna så att du snabbt kan röra dig i den riktning du berör. Naturligtvis kan du också glida uppåt eller åt vänster medan du trycker för att byta rörelser.
Standarden för skärmkontroll är dock inte lätt att placera och implementera eftersom det bara finns On-Screen Sticks och On-Screen Buttons.
Du kan också skapa en pseudo-D-pad genom att ordna fyra knappar som visas i bilden nedan, men det är obekvämt eftersom du inte kan mata diagonalt. Om du placerar 8 knappar är diagonal drift möjlig, men flödesriktningsoperation som "← ↙ ↓" är fortfarande inte möjlig.
Skapa en riktningsplatta med anpassning av skärmkontroll
Som du kan se kommer On-Screen Control bara som standard med Stick and Button, men du kan anpassa de saknade funktionerna med dina egna skript. Så här skulle jag vilja skapa en riktningsplatta med anpassning av skärmkontroll.
Åtgärd karta
Vi kommer att använda åtgärdskartan, men vi kommer att använda den som skapades i föregående tips som den är, så proceduren utelämnas.
Du har också skrivit lite kod.
Placera objekt
Placera ett textområde för att visa dina inmatningar och en knapp som ersätter styrknappen. I det här fallet är knapparna ordnade, men du kan ersätta dem med en bild som är lättare att förstå.
Skript för att visa indata
Eftersom Skärmkontroll ersätter touch med interaktion med fysisk handkontroll Skapa ett skript som visar dina indata i text när åtgärdskartan fungerar.
Innehållet är detsamma som tidigare, så jag kommer att utelämna förklaringen.
- Använda åtgärdskartor för att tilldela knappar till spelbeteenden
- Använda pekoptimerade inmatningskontroller med skärmkontroll
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}";
}
}
När du har ställt in det, kontrollera först om det fungerar med tangentbordet eller gamepad.
Anpassa kontroller på skärmen
Detta leder oss till huvudämnet för dessa tips.
Att anpassa skärmkontrollen är ett skript, så skapa först ett skript.
Namnet är godtyckligt, men i det här fallet OnScreenDpad
är det .
Skriptet ser ut så här:
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);
}
}
Du skapar en OnScreenControl
anpassningsklass för Skärmkontroll genom att ärva från klassen Anpassa.
Dessutom, för att ta emot olika beröringshändelser, ärva målgränssnittet.
Här bearbetas "vid beröring", "när du rör dig medan du rör", "när beröringen släpps" och "innan du drar", så följande gränssnitt beskrivs också.
public class OnScreenDpad
: OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Gränssnittets | innehåll |
---|---|
IPointerDownHandler | Vid beröring |
IPointerUpHandler | När du släpper touchen |
IDragHandler | När du rör dig medan du rör vid |
IInitializePotentialDragHandler | Innan du börjar dra |
Följande fält deklareras:
controlPathInternal
OnScreenControl
krävs om du ärver från en klass.
Den innehåller en sträng av vilken knapp på inmatningsenheten som ska mappas till, men i princip kan du skriva den som den är.
InputControl
Attributet bör dock innehålla det värde som ska behållas (Vector2 i det här fallet).
_objectPosition
_objectSizeHalf
och håll halva knappens position och storlek i förväg och använd den senare för beräkningar.
[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;
Metoden, som anropas Start
efter att objektet har initierats, får halva positionen och storleken på knappen på duken.
Storleken tar också hänsyn till skalan.
Start
Den bearbetas med en metod, men om den kan användas korrekt i beräkningen i slutändan kan förvärvstiden vara var som helst.
// <summary>
// オブジェクトが動作する最初のタイミングで1回だけ呼ばれます。
// </summary>
private void Start()
{
var rectTransform = (RectTransform)base.transform;
// オブジェクトの位置を取得
_objectPosition = rectTransform.anchoredPosition;
// オブジェクトのサイズの半分を取得 (スケールサイズも考慮)
_objectSizeHalf = rectTransform.sizeDelta * rectTransform.localScale / 2f;
}
OnInitializePotentialDrag
anropas när du vill börja dra efter tryckgester.
När du drar OnDrag
anropas metoden.
En tröskel är inställd för att förhindra dragbedömning i viss utsträckning så att fingret inte rör sig lite och dragdomen inte uppstår på grund av att man helt enkelt rör.
Den här gången, eftersom det är en ingång som förutsätter att du vill utföra en finjusteringsåtgärd, inaktiverar du den här tröskelinställningen och gör omedelbart en dragbedömning.
eventData.useDragThreshold
false
Du kan åsidosätta tröskelvärdet genom att ange till .
<summary>ドラッグの初期化処理として呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnInitializePotentialDrag(PointerEventData eventData)
{
// タッチのスライド操作を即座に発生させたいのでドラッグ開始までの閾値を無効にします
eventData.useDragThreshold = false;
}
Nedan visas händelserna som kallas när du rör, drar respektive släpper.
OnPointerDown
OnDrag
Eftersom och , var och en utför Operate
samma ingångsbehandling, så vi skapar en separat metod och kallar den.
OnPointerUp
Skicka den nu till metoden för att meddela kontrollen att SendValueToControl
Vector2.zero
den har slutat skriva.
<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
Metoden är kärnan D-pad ingångsoperation.
Först och främst kan beröringspositionen erhållas med , men eftersom denna koordinat är koordinaten för eventData.position
spelskärmen,
RectTransformUtility.ScreenPointToLocalPointInRectangle
metod för att konvertera till dukkoordinater.
Värdena som ska skickas är "duk", "beröringsposition", "kamera" och " RectTransform
mottagningsvariabel".
Efter att ha fått "beröringspositionen på duken", konvertera den till knappens position som ursprung.
Subtrahera bara objektets_objectPosition
position ().
Därefter, eftersom beröringspositionen fortfarande är ett nummer på duken, konvertera detta värde till ett förhållande på 0 ~ 1.
Om du delar med halva knappens storlek blir beröringspositionen inom knappens intervall 0 ~ 1.
Om du drar med touch blir värdet 1 eller mer eftersom det accepterar åtgärder även utanför knappen.
Vector2.ClampMagnitude
Gör detta i metoden så att den inte överstiger 1.
Slutligen skickar du ingångsvärdet SendValueToControl
konverterat till 0 ~ 1 till metoden.
Kontrollen på skärmen gör resten åt dig.
<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);
}
Koppla skriptet du skapade till knappen. Den här åtgärden är inställd på att behandlas som funktionen av den vänstra styrspaken på Gamepad.
Prova att flytta spelet och tryck på knapparna. Jag tror att du kan se att det erhållna värdet ändras beroende på beröringspositionen. Du kan också se att även om du drar medan du trycker på ändras värdet beroende på dragpositionen.