Use action maps to assign buttons to game behaviors

Page update date :
Page creation date :

Verification environment

Windows
  • Windows 11
Unity Editor
  • 2020.3.25f1
Input System Package
  • 1.2.0

Prerequisites for this tip

The following settings have been made in advance as a premise for the description of this tip.

About Action Maps

User input programs on keyboards, mice, and gamepads basically stated that a certain action was performed when a button was pressed. In the action map, for example, you can define the action of "jumping" and assign controller buttons and keyboard keys to it. As a result, the program only needs to describe the process when a specific action is performed. Even if you assign a button on another controller as an afterthought, you can apply it without changing the operating program.

Creating an Action Map

Here, I would like to create an action map and display the user's input information in the text. There are several ways to get user input using Action Maps.

Right-click any folder in the project to create an Input Action. The location of the folder to be created is arbitrary, but please manage it according to your project. The file name is also arbitrary, but here InputActionSample it is .

When you double-click the created file, the following window will be displayed.

First, click the + button in Action Maps to create an action map. As a creation unit, if the content of the operation changes depending on the scene, it will be defined in that unit. For example, in the case of a side-scrolling action game, the contents of the operation change during the action and in the menu, so you will create an action map for each.

Here, as an example, we define a side-scrolling action and name it "SideScrollActionMap".

Next, create Actions. Since it is a sample, I will not make many of them, but here we will create "Move" actions for movement and "Attack" for attack. Since one has already been created, please change the name or create a new one, click the + button in the upper right corner and enter it.

First, configure the Move configuration. The controller assumes that you use the stick, D-pad, and keyboard cursor keys. In the case of a side-scrolling action, there are cases where only left and right are used, but here we assume that you use four directions considering jumping with the upper key and crouching with the down key.

When you select Move, there is an Action Type selection to the right, so set this to "Value".

You will see the Control Type below, so select Vector2. This is because the top and bottom are assigned to Y, and the left and right are assigned to X.

Then select the key you want to assign to this event. Select No Binding in the middle and select Path on the right. Here we select the GamePad LeftStick.

This binds the GamePad's LeftStick to the Move.

To bind other controllers as well, select "Add Binding" from the + button to the right of Move.

Now that the No Binding is added, we assign a Dpad for the GamePad.

In this way, you can add the type of controller you want to support, as well as the keys and sticks. It is also possible to set it specifically for a specific game console.

Sticks and Dpads are buttons that assume up, down, left, and right, so they can be added with this, but in the case of keyboards, they are all single keys, so there is no definition for up, down, left, and right. To set the keyboard up, down, left, or right, select Add Up Down Left Right Composite from the + button.

A 2D Vector will then be added and you can assign it to each Up Down Left Right, as shown in the figure below.

For example, if you use Up, set "Up Arrow" on your keyboard. By the way, if you are troublesome to find a key, you can easily select it by pressing the target key while clicking the "Listen" button.

Up is now set to Up Arrow.

Similarly, set Down, Left, and Right and you're done.

Of course, not only cursor keys but also WASD can be set.

Next, configure Attack. Attack is easy to assign because it's a single button. First, select Attack and make sure the Action Type is a button.

Then select No Binding and select the button you want to assign from the Path.

If you want to add more, select "Add Binding" from the + button.

Add as many as you need. Since it is treated as a button, the keyboard can be set in the same way as a game controller.

When all settings are complete, click "Save Asset" to save. You can close this window.

Finally, with the project's inputactions file (in this case, the InputActionSample file you created earlier), check "Generate C# Class" in the inspector. The parameter will be added, but click the "Apply" button as it is.

This will generate a script file with the same name. It contains classes that are useful for using action maps from programs.

How to receive input information

There are several ways to receive input based on an action map. This tip explains three patterns, but it's better to focus on one of them when actually making a game. If you use them separately, it will be troublesome to manage.

Also, if you use multiple input receiving methods in a single scene, the processing may conflict internally and not work correctly.

Receive input information in Send Messages

The first method here is how to receive input information in "Send Messages".

This time, I want to display the entered information in the text, so I will place a text object.

Also, since this tip will try to get multiple input information, we will create an empty object to set the component separately. The name can be anything.

Next, add a Player Input component to the empty object. Player Input is an important component for connecting action maps and scripts.

In "Add Component", there is "Player Input" in the "Input" category, so add it.

Once the Player Input component is added, set the action map you created in "Actions". Drop it from the project or select it from the + icon to the right.

Verify that the Default Map is the one you created in the action map.

Verify that the Behavior is "Send Messages".

Next, create a script. The file name can be anything, but here InputSendMessage it is .

The script looks like this:

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}";
  }
}
  • The object has a Player Input attached that sets Send Messages
  • Inheriting from MonoBehaviour

If the conditions are met, then if you define a method called "OnXXXXXXXX", The target method will be called when the specified action operation is performed. "XXXXXXXX" is the name of the Actions created in the action map. Here, we have created "Move" and "Attack" actions, so the method names are "OnMove" and "OnAttack", respectively.

OnMove You can get the amount entered from the argument InputValue of . Since the Control Type is set to "Vector 2", the input value InputValue.Get<Vector2> will be received in .

OnAttackInputValue.isPressed You can get whether you are pressing in as well.

After you save the script, attach it to an object that has a Player Input component. Set the text object for display as well.

Run the game and take a look. This time, I have included the definition of the gamepad and keyboard, so it should work no matter which one you operate.

As you can see when you move it, you can see that the method is called only when there is a change in value from the previous state. For example, while moving the stick to the left, it is called single-mindedly, but not to the left (- OnMove 1,0). OnMove The Attack button also responds only the moment it is pressed, and if it is pressed and held, the method is not called.

Therefore, I think that the ideal usage is not to perform game processing when OnXXXXXXXX is called, but to keep only the input contents and use those values in the game's update processing.

By the way, in the current state, it is not called when the button is released, so it is not possible to determine when OnAttack the button is released. To respond to this, select the Attack action that defines the button in the action map settings and add "Press" from "Interactions". After that, set the Trigger Behavior of the added Press to "Press And Release" and save it.

When executed, you can see that is called even OnAttack when the button is released. isPressed false Since it becomes , it is also possible to determine whether it is the timing of release.

By the way, please delete this interaction because it will not be used in the future.

Receive input with Invoke Unity Events

A second way to receive input is Invoke Unity Events, so let's try this. As mentioned above, using multiple input methods can cause conflicting processing, so if other processing is enabled, disable it.

First, place the text object so that the input information can be displayed.

Invoke Unity Events creates an empty object that performs related operations.

Add Input > Player Input to the empty object.

Set the action map file you created for Actions (in this case, InputActionSample) and set the Action Map you created (in this case, SideScrollActionMap) to Default Map. Set Behavior to Invoke Unity Events.

Create a script. The name is arbitrary, but in this case InputInvokeUnityEvents it is .

The script looks like this:

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}";
  }
}

The method name OnMoveOnAttack that is called when the user interacts with it is , as in the case of Send Messages. In Invoke Unity Events, you can set this method name as you like.

When each is called, it is passed as an InputAction.CallbackContext argument, so you can get the input status from there. If you set a "value" in the action, you can receive it in the method, and if you ReadValue set ReadValueAsButton a "button", you can receive it in the method.

After saving the script, attach the Player Input to the object you are setting and set the display text object.

Next, expand "Event" and "Action Map Name (SideScrollActionMap)" in Player Input and you should see the actions "Move" and "Attack" you created.

First, click the + button on Move to add it.

The object in the lower left corner is your own object, and the Function is set to the method you just created OnMove .

Configure the Attack event as well.

Run the game to see how it works.

Basically, it is called only when the same value as Send Messages changes, but for some reason the method may be called twice at the same time. I don't know the cause, but I think it's probably because the start process and the continuous process are running at the same time. However, I think that there is no problem if you keep only the entered value as in the case of Send Messages and perform the actual game processing separately in the Update process.

Use an auto-generated script to receive input information

The third section describes how to obtain input information using a script generated from an action map file.

Since there is a possibility of conflicts with other acquisition processes, please disable other acquisition processes.

Place a text object to display the input information.

Also, create an empty object for retrieving input information. This article uses an auto-generated script, so you don't need to add Player Input.

The script automatically generated from the action map is just a library, so create a separate control script. The name is arbitrary, but in this case InputScript it is .

The script looks like this:

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}";
  }
}

Define an auto-generated class InputActionSample from the action map in the field. This class defines each action set in the action map, and you can set the events that are called when those actions are performed.

Awake In the method, InputActionSample an instance of is created and the event called at the time of the action is set. When OnMoveyou perform these operations, the , OnAttack method is now called.

However, since we only set the event here, we OnEnable need to call the method when Enable is called to enable the action map.

Also, since the user's input behavior is treated as a global operation, To prevent OnDisable the action map from doing extra after this object is invalidated, we call the method in Disable the method to disable it.

After saving the script, attach it to the empty object you created and set the text object for display.

Run the game to see how it works.

As you can see, the method is not called when OnMove the Move operation becomes (0, 0). I'm not sure why, but it seems that the performed event only actually takes the one with keystrokes enabled.

By the way, if you have not set Interactions in the action map for Attack, it will not be called when OnAttack you release the button.

To canceled handle this, you need to set up an event. If you do not want to perform special processing at (0, 0), you can call the method as it is OnMove . The same is true for 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);   // 追加
}

Run and verify that Move:(0, 0) or Attack:False appears.