使用操作映射为游戏行为分配按钮

更新页 :
页面创建日期 :

验证环境

窗户
  • 视窗 11
统一编辑器
  • 2020.3.25f1
输入系统包
  • 1.2.0

此提示的先决条件

作为此提示描述的前提,已预先进行了以下设置。

关于操作地图

键盘、鼠标和游戏手柄上的用户输入程序基本上表明,按下按钮时执行了某个操作。 例如,在操作映射中,您可以定义“跳跃”操作并为其分配控制器按钮和键盘键。 因此,程序只需要在执行特定操作时描述过程。 即使您事后在另一个控制器上分配了一个按钮,您也可以在不更改操作程序的情况下应用它。

创建操作映射

在这里,我想创建一个操作地图并在文本中显示用户的输入信息。 有几种方法可以使用操作映射获取用户输入。

右键单击工程中的任何文件夹以创建输入操作。 要创建的文件夹的位置是任意的,但请根据您的项目进行管理。 文件名也是任意的,但这里是 InputActionSample .

双击创建的文件时,将显示以下窗口。

首先,单击“操作映射”中的 + 按钮以创建操作映射。 作为创建单元,如果操作内容因场景而异,则会在该单元中定义。 例如,在横向卷轴动作游戏的情况下,操作的内容在操作期间和菜单中会发生变化,因此您将为每个操作创建一个操作地图。

在这里,作为一个例子,我们定义一个横向滚动操作并将其命名为“横向滚动动作映射”。

接下来,创建操作。 由于这是一个示例,我不会做很多,但在这里我们将为移动创建“移动”动作,为攻击创建“攻击”动作。 由于已经创建了一个,请更改名称或创建一个新名称,单击右上角的+按钮并输入它。

首先,配置移动配置。 控制器假定您使用摇杆、方向键和键盘光标键。 在横向滚动操作的情况下,有时只使用左键和右键,但在这里我们假设您使用四个方向,考虑使用上键跳跃并使用向下键蹲下。

选择“移动”时,右侧会有一个“操作类型”选项,因此请将其设置为“值”。

你将在下面看到控件类型,因此请选择 Vector2。 这是因为顶部和底部分配给 Y,左侧和右侧分配给 X。

然后选择要分配给此事件的密钥。 选择中间的“无绑定”,然后选择右侧的“路径”。 在这里,我们选择游戏手柄左摇杆。

这会将GamePad的LeftStick绑定到移动中。

要同时绑定其他控制器,请从“移动”右侧的 + 按钮中选择“添加绑定”。

添加无绑定后,我们为游戏手柄分配一个 Dpad。

通过这种方式,您可以添加要支持的控制器类型,以及按键和摇杆。 也可以专门为特定的游戏机设置它。

摇杆和 Dpad 是假定向上、向下、向左和向右的按钮,因此可以使用此按钮添加它们,但在键盘的情况下,它们都是单键,因此没有向上、向下、向左和向右的定义。 要设置键盘向上、向下、向左或向右,请从 + 按钮中选择向上向下添加左右复合。

然后将添加一个 2D 矢量,您可以将其分配给每个左下右上,如下图所示。

例如,如果您使用向上,请在键盘上设置“向上箭头”。 顺便说一句,如果您很难找到一个键,您可以通过在单击“收听”按钮的同时按目标键轻松选择它。

向上现在设置为向上箭头。

同样,设置向下、向左和向右,您就完成了。

当然,不仅可以设置光标键,还可以设置 WASD。

接下来,配置攻击。 攻击很容易分配,因为它是一个按钮。 首先,选择“攻击”并确保“操作类型”为按钮。

然后选择“无绑定”,并从“路径”中选择要分配的按钮。

如果要添加更多,请从 + 按钮中选择“添加绑定”。

根据需要添加任意数量。 由于它被视为按钮,因此可以像游戏控制器一样设置键盘。

完成所有设置后,单击“保存资产”进行保存。 您可以关闭此窗口。

最后,使用项目的输入操作文件(在本例中为之前创建的 InputActionSample 文件),在检查器中选中“生成 C# 类”。 将添加参数,但按原样单击“应用”按钮。

这将生成一个同名的脚本文件。 它包含用于使用程序中的操作映射的类。

如何接收输入信息

有几种方法可以接收基于操作映射的输入。 这个技巧解释了三种模式,但在实际制作游戏时最好专注于其中一种。 如果单独使用它们,管理起来会很麻烦。

此外,如果在单个场景中使用多种输入接收方法,则处理可能会在内部发生冲突,并且无法正常工作。

在“发送消息”中接收输入信息

这里的第一种方法是如何在“发送消息”中接收输入信息。

这次,我想在文本中显示输入的信息,所以我将放置一个文本对象。

此外,由于此提示将尝试获取多个输入信息,因此我们将创建一个空对象来单独设置组件。 名称可以是任何内容。

接下来,将玩家输入组件添加到空对象。 玩家输入是连接操作地图和脚本的重要组件。

在“添加组件”中,“输入”类别中有“玩家输入”,因此请添加它。

添加玩家输入组件后,设置您在“操作”中创建的操作映射。 将其从项目中删除,或从右侧的 + 图标中选择它。

验证默认映射是否是您在操作映射中创建的映射。

验证行为是否为“发送消息”。

接下来,创建一个脚本。 文件名可以是任何内容,但这里是 InputSendMessage .

脚本如下所示:

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}";
  }
}
  • 该对象附加了一个播放器输入,用于设置发送消息
  • 继承自 MonoBehavior

如果满足条件,则如果定义一个名为“OnXXXXXXXX”的方法, 执行指定的操作操作时,将调用目标方法。 “XXXXXXXX”是在操作映射中创建的操作的名称。 在这里,我们创建了“移动”和“攻击”动作,因此方法名称分别是“OnMove”和“OnAttack”。

OnMove 您可以从 的参数 InputValue 中获取输入的金额。 由于控件类型设置为“向量 2”,因此将在 中接收输入值 InputValue.Get<Vector2>

OnAttackInputValue.isPressed你也可以知道你是否在按。

保存脚本后,将其附加到具有播放器输入组件的对象。 同时设置要显示的文本对象。

运行游戏并查看。 这次,我包含了游戏手柄和键盘的定义,因此无论您操作哪一个,它都应该有效。

正如您在移动它时所看到的,您可以看到仅当值与先前状态发生更改时才调用该方法。 例如,在向左移动摇杆时,一心一意地调用它,但不向左移动(- OnMove 1,0)。 OnMove Attack 按钮也仅在按下时响应,如果按住它,则不会调用该方法。

因此,我认为理想的用法不是在调用 OnXXXXXXXX 时执行游戏处理,而是只保留输入内容并在游戏的更新处理中使用这些值。

顺便说一下,在当前状态下,释放按钮时不会调用它,因此无法确定何时 OnAttack 释放按钮。 要对此做出响应,请在操作映射设置中选择定义按钮的攻击操作,然后从“交互”中添加“按”。 之后,将添加的按的触发行为设置为“按下并释放”并保存。

执行时,您可以看到即使在释放按钮时也会 OnAttack 调用该按钮。 isPressed false 既然变成,也可以确定是否是发布的时间。

顺便说一下,请删除此交互,因为它将来不会使用。

使用调用 Unity 事件接收输入

接收输入的第二种方法是调用 Unity 事件,所以让我们试试这个。 如上所述,使用多个输入方法可能会导致处理冲突,因此如果启用了其他处理,请禁用它。

首先,放置文本对象,以便可以显示输入信息。

调用 Unity 事件会创建一个执行相关操作的空对象。

将输入>玩家输入添加到空对象。

设置为操作创建的操作映射文件(在本例中为 InputActionSample),并将您创建的操作映射(在本例中为 SideScrollActionMap)设置为“默认映射”。 设置行为以调用 Unity 事件。

创建脚本。 名称是任意的,但在这种情况下 InputInvokeUnityEvents 是.

脚本如下所示:

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

用户与之交互时调用的方法名称OnMoveOnAttack是 ,就像发送消息一样。 在调用 Unity 事件中,您可以根据需要设置此方法名称。

当调用每个时,它作为参数传递InputAction.CallbackContext,因此您可以从那里获取输入状态。 如果在操作中设置了“值”,则可以在方法中接收它,如果设置ReadValueAsButton了“按钮”,则可以ReadValue在方法中接收它。

保存脚本后,将播放器输入附加到要设置的对象并设置显示文本对象。

接下来,在玩家输入中展开“事件”和“操作映射名称(横向滚动操作映射)”,您应该会看到您创建的“移动”和“攻击”操作。

首先,单击“移动”上的 + 按钮进行添加。

左下角的对象是你自己的对象,函数设置为你刚刚创建 OnMove 的方法。

同时配置攻击事件。

运行游戏以查看其工作原理。

基本上,仅当与“发送消息”相同的值更改时才会调用它,但由于某种原因,该方法可能同时调用两次。 我不知道原因,但我认为可能是因为启动过程和连续过程同时运行。 但是,我认为如果您只保留输入的值,例如发送消息并在更新过程中单独执行实际的游戏处理,则没有问题。

使用自动生成的脚本接收输入信息

第三部分介绍如何使用从操作映射文件生成的脚本获取输入信息。

由于有可能与其他获取流程发生冲突,请禁用其他获取流程。

放置文本对象以显示输入信息。

此外,创建一个空对象来检索输入信息。 本文使用自动生成的脚本,因此无需添加玩家输入。

从操作映射自动生成的脚本只是一个库,因此请创建单独的控制脚本。 名称是任意的,但在这种情况下 InputScript 是.

脚本如下所示:

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

从字段中的操作映射定义自动生成的类 InputActionSample 。 此类定义操作映射中的每个操作集,您可以设置执行这些操作时调用的事件。

Awake 在该方法中,将创建 的实例, InputActionSample 并设置在操作时调用的事件。 执行这些操作时 OnMove,现在将调用 , OnAttack 方法。

但是,由于我们只在此处设置事件,因此我们需要 OnEnable 在调用时 Enable 调用该方法以启用操作映射。

此外,由于用户的输入行为被视为全局操作, 为了防止 OnDisable 操作映射在此对象失效后执行额外的操作,我们在方法中 Disable 调用该方法以禁用它。

保存脚本后,将其附加到您创建的空对象,并设置要显示的文本对象。

运行游戏以查看其工作原理。

如您所见,当 Move 操作变为 (0, 0) 时 OnMove ,不会调用该方法。 我不确定为什么,但似乎执行的事件实际上只采取了启用了击键的事件。

顺便说一下,如果您没有在操作地图中为攻击设置交互,则在释放按钮时 OnAttack 不会调用它。

canceled 处理此问题,您需要设置一个事件。 如果不想在 (0, 0) 处执行特殊处理,可以按原样 OnMove 调用该方法。 攻击也是如此。

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);   // 追加
}

运行并验证是否显示“移动:(0, 0) ”或“攻击:假”。