Använda åtgärdskartor för att tilldela knappar till spelbeteenden

Sidan uppdaterad :
Datum för skapande av sida :

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.

Om åtgärdskartor

Användarinmatningsprogram på tangentbord, möss och gamepads uppgav i princip att en viss åtgärd utfördes när en knapp trycktes in. I åtgärdskartan kan du till exempel definiera åtgärden att "hoppa" och tilldela kontrollknappar och tangentbordstangenter till den. Som ett resultat behöver programmet bara beskriva processen när en specifik åtgärd utförs. Även om du tilldelar en knapp på en annan handkontroll som en eftertanke kan du använda den utan att ändra operativsystemet.

Skapa en åtgärdskarta

Här skulle jag vilja skapa en åtgärdskarta och visa användarens inmatningsinformation i texten. Det finns flera sätt att få användarinput med hjälp av Action Maps.

Högerklicka på en mapp i projektet för att skapa en indataåtgärd. Platsen för mappen som ska skapas är godtycklig, men hantera den enligt ditt projekt. Filnamnet är också godtyckligt, men här InputActionSample är det .

När du dubbelklickar på den skapade filen visas följande fönster.

Klicka först på knappen + i Åtgärdskartor för att skapa en åtgärdskarta. Om innehållet i åtgärden ändras beroende på scenen definieras det som en skapande enhet i den enheten. Till exempel, när det gäller ett sidscrollande actionspel, ändras innehållet i operationen under åtgärden och i menyn, så du skapar en åtgärdskarta för varje.

Här, som ett exempel, definierar vi en sidscrollande åtgärd och namnger den "SideScrollActionMap".

Skapa sedan åtgärder. Eftersom det är ett prov kommer jag inte att göra många av dem, men här kommer vi att skapa "Move" -åtgärder för rörelse och "Attack" för attack. Eftersom en redan har skapats, ändra namnet eller skapa ett nytt, klicka på + -knappen i det övre högra hörnet och ange den.

Konfigurera först Move-konfigurationen. Handkontrollen förutsätter att du använder styrspaken, styrknappen och tangentbordets markörtangenter. När det gäller en sidrullningsåtgärd finns det fall där endast vänster och höger används, men här antar vi att du använder fyra riktningar med tanke på att hoppa med den övre tangenten och huka med nedåtknappen.

När du väljer Flytta finns det ett val av åtgärdstyp till höger, så ställ in detta på "Värde".

Du kommer att se kontrolltypen nedan, så välj Vector2. Detta beror på att toppen och botten tilldelas Y, och vänster och höger tilldelas X.

Välj sedan den nyckel som du vill tilldela händelsen. Välj Ingen bindning i mitten och välj Sökväg till höger. Här väljer vi GamePad LeftStick.

Detta binder GamePads LeftStick till Move.

För att binda andra kontroller också, välj "Lägg till bindning" från + -knappen till höger om Move.

Nu när Ingen bindning har lagts till tilldelar vi en Dpad för GamePad.

På detta sätt kan du lägga till den typ av styrenhet du vill stödja, liksom tangenter och pinnar. Det är också möjligt att ställa in det specifikt för en specifik spelkonsol.

Pinnar och Dpads är knappar som antar upp, ner, vänster och höger, så att de kan läggas till med detta, men när det gäller tangentbord är de alla enkla tangenter, så det finns ingen definition för upp, ner, vänster och höger. Om du vill ställa in tangentbordet uppåt, nedåt, åt vänster eller åt höger väljer du Lägg till uppåt vänster höger sammansatt från knappen +.

En 2D-vektor läggs sedan till och du kan tilldela den till varje upp ner vänster höger, som visas i figuren nedan.

Om du till exempel använder Up, ställ in "Up Arrow" på tangentbordet. Förresten, om du är besvärlig att hitta en nyckel kan du enkelt välja den genom att trycka på målknappen medan du klickar på "Lyssna" -knappen.

Upp är nu inställt på uppåtpil.

På samma sätt, ställ ner, vänster och höger så är du klar.

Naturligtvis kan inte bara markörtangenter utan även WASD ställas in.

Konfigurera sedan Attack. Attack är lätt att tilldela eftersom det är en enda knapp. Välj först Attack och se till att åtgärdstypen är en knapp.

Välj sedan Ingen bindning och välj den knapp du vill tilldela från sökvägen.

Om du vill lägga till mer väljer du "Lägg till bindning" från + -knappen.

Lägg till så många du behöver. Eftersom det behandlas som en knapp kan tangentbordet ställas in på samma sätt som en spelkontroll.

När alla inställningar är klara klickar du på "Spara tillgång" för att spara. Du kan stänga det här fönstret.

Slutligen, med projektets inputactions-fil (i det här fallet InputActionSample-filen som du skapade tidigare), markerar du "Generate C# Class" i granskaren. Parametern läggs till, men klicka på "Apply" -knappen som den är.

Detta genererar en skriptfil med samma namn. Den innehåller klasser som är användbara för att använda åtgärdskartor från program.

Så här tar du emot inmatningsinformation

Det finns flera sätt att få input baserat på en åtgärdskarta. Detta tips förklarar tre mönster, men det är bättre att fokusera på ett av dem när du faktiskt gör ett spel. Om du använder dem separat blir det besvärligt att hantera.

Om du använder flera indatamottagningsmetoder i en enda scen kan bearbetningen stå i konflikt internt och inte fungera korrekt.

Ta emot inmatningsinformation i Skicka meddelanden

Den första metoden här är hur man tar emot inmatningsinformation i "Skicka meddelanden".

Den här gången vill jag visa den angivna informationen i texten, så jag placerar ett textobjekt.

Eftersom detta tips kommer att försöka få flera indatainformation skapar vi också ett tomt objekt för att ställa in komponenten separat. Namnet kan vara vad som helst.

Lägg sedan till en Player Input-komponent i det tomma objektet. Spelarinmatning är en viktig komponent för att ansluta actionkartor och skript.

I "Lägg till komponent" finns det "Player Input" i kategorin "Input", så lägg till den.

När komponenten Player Input har lagts till ställer du in åtgärdskartan som du skapade i "Actions". Släpp den från projektet eller välj den från + -ikonen till höger.

Kontrollera att standardkartan är den du skapade i åtgärdskartan.

Kontrollera att beteendet är "Skicka meddelanden".

Skapa sedan ett skript. Filnamnet kan vara vad som helst, men här InputSendMessage är det .

Skriptet ser ut så här:

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class InputSendMessage : MonoBehaviour
{
  /// <summary>情報を表示させるテキストオブジェクト。</summary>
  [SerializeField] private Text TextObject;

  /// <summary>
  /// Move アクションが実行されたときに呼ばれる。
  /// </summary>
  /// <param name="inputValue">入力量。</param>
  public void OnMove(InputValue inputValue)
  {
    var vec = inputValue.Get<Vector2>();
    TextObject.text = $"Move:({vec.x:f2}, {vec.y:f2})\n{TextObject.text}";
  }

  /// <summary>
  /// Attack アクションが実行されたときに呼ばれる。
  /// </summary>
  public void OnAttack(InputValue inputValue)
  {
    TextObject.text = $"Attack:{inputValue.isPressed}\n{TextObject.text}";
  }
}
  • Objektet har en Player Input kopplad som anger Skicka meddelanden
  • Ärva från MonoBehaviour

Om villkoren är uppfyllda, om du definierar en metod som heter "OnXXXXXXXX", Målmetoden anropas när den angivna åtgärden utförs. "XXXXXXXX" är namnet på de åtgärder som skapats i åtgärdskartan. Här har vi skapat "Move" och "Attack" -åtgärder, så metodnamnen är "OnMove" respektive "OnAttack".

OnMove Du kan få beloppet angivet från argumentet InputValue för . Eftersom kontrolltypen är inställd på "Vector 2" kommer ingångsvärdet InputValue.Get<Vector2> att tas emot i .

OnAttackInputValue.isPressed Du kan få om du trycker in också.

När du har sparat skriptet kopplar du det till ett objekt som har en Player Input-komponent. Ställ in textobjektet för visning också.

Kör spelet och ta en titt. Den här gången har jag inkluderat definitionen av gamepad och tangentbord, så det borde fungera oavsett vilken du använder.

Som du kan se när du flyttar den kan du se att metoden endast anropas när det finns en värdeförändring från föregående tillstånd. Till exempel, när du flyttar pinnen till vänster, kallas den målmedvetet, men inte till vänster (- OnMove 1,0). OnMove Attackknappen svarar också bara när den trycks in, och om den trycks in och hålls intryckt anropas inte metoden.

Därför tror jag att den ideala användningen inte är att utföra spelbearbetning när OnXXXXXXXX anropas, utan att bara behålla inmatningsinnehållet och använda dessa värden i spelets uppdateringsbehandling.

Förresten, i nuvarande tillstånd kallas det inte när knappen släpps, så det är inte möjligt att bestämma när OnAttack knappen släpps. För att svara på detta, välj attackåtgärden som definierar knappen i inställningarna för åtgärdskartan och lägg till "Press" från "Interaktioner". Därefter ställer du in triggerbeteendet för den tillagda pressen till "Tryck och släpp" och spara den.

När den körs kan du se att den kallas även OnAttack när knappen släpps. isPressed false Eftersom det blir , är det också möjligt att avgöra om det är tidpunkten för frisläppandet.

Förresten, ta bort denna interaktion eftersom den inte kommer att användas i framtiden.

Ta emot indata med Anropa Unity-händelser

Ett andra sätt att ta emot indata är Anropa Unity-händelser, så låt oss prova det här. Som nämnts ovan kan användning av flera inmatningsmetoder orsaka motstridig bearbetning, så om annan bearbetning är aktiverad, inaktivera den.

Placera först textobjektet så att inmatningsinformationen kan visas.

Anropa Unity-händelser skapar ett tomt objekt som utför relaterade åtgärder.

Lägg till indata > spelarindata till det tomma objektet.

Ange åtgärdsmappningsfilen som du skapade för Actions (i det här fallet InputActionSample) och ange åtgärdskartan som du skapade (i det här fallet SideScrollActionMap) till Standardkarta. Ange beteende för att anropa Unity-händelser.

Skapa ett skript. Namnet är godtyckligt, men i det här fallet InputInvokeUnityEvents är det .

Skriptet ser ut så här:

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class InputInvokeUnityEvents : MonoBehaviour
{
  /// <summary>情報を表示させるテキストオブジェクト。</summary>
  [SerializeField] private Text TextObject;

  /// <summary>
  /// Move 操作を行ったときに呼ばれる。
  /// </summary>
  /// <param name="context">コールバック内容。</param>
  public void OnMove(InputAction.CallbackContext context)
  {
    var vec = context.ReadValue<Vector2>();
    TextObject.text = $"Move:({vec.x:f2}, {vec.y:f2})\n{TextObject.text}";
  }

  /// <summary>
  /// Move 操作を行ったときに呼ばれる。
  /// </summary>
  /// <param name="context">コールバック内容。</param>
  public void OnAttack(InputAction.CallbackContext context)
  {
    var value = context.ReadValueAsButton();
    TextObject.text = $"Attack:{value}\n{TextObject.text}";
  }
}

Metodnamnet OnMoveOnAttack som anropas när användaren interagerar med det är , som i fallet med Skicka meddelanden. I Anropa Unity-händelser kan du ange det här metodnamnet som du vill.

När var och en anropas skickas den som ett InputAction.CallbackContext argument, så att du kan få inmatningsstatus därifrån. Om du ställer in ett "värde" i åtgärden kan du ta emot det i metoden, och om du ställer in ReadValueAsButton en "knapp" kan du ReadValue ta emot den i metoden.

När du har sparat skriptet kopplar du spelarinmatningen till objektet du ställer in och anger visningstextobjektet.

Expandera sedan "Event" och "Action Map Name (SideScrollActionMap)" i Player Input och du bör se åtgärderna "Move" och "Attack" du skapade.

Klicka först på + -knappen på Flytta för att lägga till den.

Objektet i det nedre vänstra hörnet är ditt eget objekt och funktionen är inställd på den metod du just skapade OnMove .

Konfigurera även attackhändelsen.

Kör spelet för att se hur det fungerar.

I grund och botten kallas det bara när samma värde som Skicka meddelanden ändras, men av någon anledning kan metoden kallas två gånger samtidigt. Jag vet inte orsaken, men jag tror att det förmodligen beror på att startprocessen och den kontinuerliga processen körs samtidigt. Jag tror dock att det inte finns något problem om du bara behåller det angivna värdet som i fallet med Skicka meddelanden och utför den faktiska spelbehandlingen separat i uppdateringsprocessen.

Använda ett automatiskt genererat skript för att ta emot indatainformation

I det tredje avsnittet beskrivs hur du hämtar indatainformation med hjälp av ett skript som genereras från en åtgärdsmappningsfil.

Eftersom det finns en risk för konflikter med andra förvärvsprocesser, vänligen inaktivera andra förvärvsprocesser.

Placera ett textobjekt för att visa indatainformationen.

Skapa också ett tomt objekt för att hämta indatainformation. I den här artikeln används ett automatiskt genererat skript, så du behöver inte lägga till indata från spelaren.

Skriptet som genereras automatiskt från åtgärdskartan är bara ett bibliotek, så skapa ett separat kontrollskript. Namnet är godtyckligt, men i det här fallet InputScript är det .

Skriptet ser ut så här:

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class InputScript : MonoBehaviour
{
  /// <summary>情報を表示させるテキストオブジェクト。</summary>
  [SerializeField] private Text TextObject;

  /// <summary>アクションマップから自動生成されたクラス。</summary>
  private InputActionSample _actionMap;

  private void Awake()
  {
    // 各操作を行ったときに呼ばれるイベントを設定する
    _actionMap = new InputActionSample();
    _actionMap.SideScrollActionMap.Move.performed += context => OnMove(context);
    _actionMap.SideScrollActionMap.Attack.performed += 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}";
  }
}

Definiera en automatiskt genererad klass InputActionSample från åtgärdskartan i fältet. Den här klassen definierar varje åtgärdsuppsättning i åtgärdskartan och du kan ange de händelser som anropas när dessa åtgärder utförs.

AwakeI metoden InputActionSample skapas en instans av och händelsen som anropas vid tidpunkten för åtgärden anges. När OnMovedu utför dessa åtgärder anropas nu metoden . OnAttack

Men eftersom vi bara ställer in händelsen här måste vi OnEnable anropa metoden när Enable anropas för att aktivera åtgärdskartan.

Eftersom användarens indatabeteende behandlas som en global åtgärd För att förhindra OnDisable att åtgärdskartan gör extra efter att det här objektet har ogiltigförklarats anropar vi metoden i Disable metoden för att inaktivera den.

När du har sparat skriptet bifogar du det till det tomma objektet du skapade och anger textobjektet för visning.

Kör spelet för att se hur det fungerar.

Som du kan se kallas metoden inte när OnMove Move-operationen blir (0, 0). Jag är inte säker på varför, men det verkar som om den utförda händelsen faktiskt bara tar den med tangenttryckningar aktiverade.

Förresten, om du inte har ställt in interaktioner i åtgärdskartan för Attack, kommer det inte att kallas när OnAttack du släpper knappen.

För att canceled hantera detta måste du skapa en händelse. Om du inte vill utföra särskild bearbetning vid (0, 0) kan du ringa metoden som den är OnMove . Detsamma gäller för Attack.

private void Awake()
{
  // 各操作を行ったときに呼ばれるイベントを設定する
  _actionMap = new InputActionSample();
  _actionMap.SideScrollActionMap.Move.performed += context => OnMove(context);
  _actionMap.SideScrollActionMap.Attack.performed += context => OnAttack(context);
  _actionMap.SideScrollActionMap.Move.canceled += context => OnMove(context);       // 追加
  _actionMap.SideScrollActionMap.Attack.canceled += context => OnAttack(context);   // 追加
}

Kör och kontrollera att Move:(0, 0) eller Attack:False visas.