抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

我看了看手上的牌,明白自己无牌可出

在一次想做鼠标拖拽摄像机旋转的时候,了解到Unity Input System,就大致介绍一下Unity Input System的基本使用以及如何实现一个简单的右键拖拽控制相机旋转

官方文档

Quick start guide | Input System | 1.3.0 (unity.cn)

为什么用Input System

不同于主机平台,游戏在PC平台上可能需要同时处理不同设备的输出,同时一个游戏也可能会迁移到移动端,为多个平台做映射是一件非常令人头疼的事情。Input System将多个设备或UI上的操作都映射到了一个行为(Action)上,通过这种方法就可以使得多个设备之间的操作不会产生冲突,同时Input System还提供了很多内部实现(比如监听鼠标的移动,手柄操纵杆返回二维向量等),一定程度上简化了需要写的代码。

Input System还有一套Player的移动系统以及UI的系统,需要在Scene的GameObject中新建一个Player Input,然后Create Action就可以快速创建默认的InputActions。

什么时候不用Input System

如果只是需要做一个普通的原型或者不考虑多平台,可能就还是直接写代码会比较方便。

如果需要在移动方式等有所创新,那么原本Unity的那套输入系统能够让你更快更自由地实现你的想法。

如果游戏有较多UI需要交互的时候可以不用Input System,虽然Input System是支持UI的,但是UI本身的控件等方法Unity都已经写好了,还使用Input System反而会使得写起来有点麻烦。

除开以上场景外,它还有诸多限制,具体请自己查看官方文档:Known Limitations | Input System | 1.3.0 (unity.cn)

使用Input System

打开Package Manager,在Unity Registry分类下找到Input System导入,它会替换你当前使用的输入系统。

新建Player Input与Input Actions

在场景中创建空物体,并添加脚本Player Input,如果要创建默认Input Actions(默认包含上下左右移动以及视角移动开火等方法,以及UI的监听事件),如果要创建自己的,也可以直接右键Create->Input Actions

PlayerInput窗口
PlayerInput窗口

窗口属性说明

为了大致地说明窗口的各个位置分别用来做什么,此处就用默认的Input Actions来说明

Actions

如果你没有多设备,也没有多个Action Maps,那么只看Actions和Bindings就够了。

Action的使用方法

Action的创建在该栏的右上角的加号,一般地对于一个Action,在名字前加上On就是它的调用方法,例如名字为Move,它在脚本中的调用方法为(其实起名可以不这样起,但是传入的参数都是context)

public void OnMove(InputAction.CallbackContext context)
{
    // 根据Action Type的不同,context的属性也会
    Vector2 dir = context.ReadValue<Vector2>();
}

当你在这个窗口选择其中一个Action时,右边的窗口就会出现Action Properties

Action Properties属性说明
  • Action Type有三个选项

    • Value: 一个值

      代码用法(以二维向量为例)

      Vector2 vec2 = context.ReadValue<Vector2>();
    • Button: 一个按钮,按下一次按钮方法会被调用三次并更改三个bool值属性

      public void OnTouch(InputAction.CallbackContext context)
      {
          bool started = context.started; // 按钮开始被按下
          bool performed = context.performed; // 按钮正在被按下
          bool canceled = context.canceled; // 按钮被释放
      }
    • Pass Through: 跟Value相似,但是同时接受多个设备的输入

  • Interactions: 控制按钮按下释放的检测时间,摇杆死区等,一般不修改都使用默认值

  • Processors: 对于输入的处理,比如归一化方向的输入

Binding

在Action下创建或选择Binding可以看到右边出现Binding Properties,点击Path后点击Listen可以绑定到你输入的键位或搜索具体的键位进行绑定。

在下方的Use in Control Scheme可以选择当前绑定使用在哪个控制方案下(如果有更多控制方案)。

Control Schemes

Control Schemes(控制方案)位于窗口的左上角,打开下拉菜单,在这里你可以选择或新建设备的控制方案,并在之后的binding(绑定)中勾选对应的控制方案(例如左操纵杆就只勾选Gamepad),当使用该设备时,Actions将自动选择对应的binding

Action Maps

在最左边的是Action Maps,这里存放的是输入与操作对应的映射图,主要用在例如角色开启菜单就可以将原本控制角色的输入设备用于UI的点击,这时就需要转换映射关系,例如我们这里将映射从Player转换到UI,代码如下:

public PlayerInput playerInput;
playerInput.SwitchCurrentActionMap("UI");

在场景中调用方法

在场景中的PlayerInput选择使用的Actions,Behavior选择Invoke Unity Events,在Events下我们可以找到我们创建的映射图(Action Map),展开后就能看到我们的各个Action了,在对应的Action中选择当前控制的对象以及对象上要相应的方法(先选择控制的对象,再选择对象脚本内的方法)。例如:

相机旋转的实现

这里说的还是有点混乱,这里就来说一下如何实现鼠标旋转相机的效果,这里附上我看的教程地址:油管教程

鼠标偏移量的获取

新建Action命名为Look,Action Type为Value(或Pass Through),Control Type为Vector2,Action下新建binding,Path选择为Delta[Mouse]

在相机控制脚本中编写OnLook方法

private Vector2 _delta; // 鼠标偏移量

public void OnLook(InputSystem.CallbackContext context)
{
  _delta = context.ReadValue<Vector2>();
}

获取鼠标右键是否按下

新建Action命名为Rotate,Action Type选择为Button,Action下新建binding,Path选择为Right Button[Mouse]

在相机控制脚本中编写OnRotate方法

private bool _isRotating;

public void OnRotate(InputSystem.CallbackContext context)
{
  // 监听右键是否按下
  _isRotating = context.started || context.performed;
}

记得在PlayerInput中调用方法

相机旋转实现

我们获得了鼠标相对上一个位置的偏移量,以及目前是否在按下右键,那么就像当于我们或去了是否在按下右键拖拽鼠标的信息,接下来只需要实现相机的旋转(这里实现的是相机的横向旋转,即只改变相机旋转的y值)即可。

private float _xRotation;
[SerializeField] private float rotationSpeed = .5f;

private void Awake()
{
    _xRotation = transform.rotation.eulerAngles.x;
}

private void LateUpdate()
{
    if (_isRotating)
    {
      // 只修改Rotation的y
      transform.rotation = Quaternion.Euler(_xRotation, transform.rotation.eulerAngles.y + _delta.x * rotationSpeed, 0.0f);
    }
}

到了这一步就已经可以实现相机的旋转了,不过如果需要吸附相机的y值到45、135、225、315度,那么就看一下下面的步骤。

相机吸附

判断当前角度在哪个区间,要吸附到哪个角度

private Vector3 SnappedVector()
{
    var endValue = 0.0f;
    var currentY = Mathf.Ceil(transform.rotation.eulerAngles.y);


    endValue = currentY switch
    {
        >= 0 and <= 90 => 45.0f,
        >= 91 and <= 180 => 135.0f,
        >= 181 and <= 270 => 225.0f,
        _ => 315.0f
    };

    return new Vector3(_xRotation, endValue, 0f);
}

使用DoTween将为相机吸附做一个流畅的动画(DoTween调用OnComplete方法记得O是大写,isBusy用于判断是否完成动画了)

private void SnapRotation()
{
    transform.DORotate(SnappedVector(), 0.5f)
        .SetEase(Ease.OutBounce)
        .OnComplete(() => { isBusy = false; });
}

最后再在OnRotate方法中,当右键释放时执行这两个方法

public void OnRotate(InputAction.CallbackContext context)
{
    _isRotating = context.started || context.performed;

    if (context.canceled)
    {
        isBusy = true;
        SnapRotation();
    }
}

到这里右键点击旋转相机,松开右键相机吸附的效果就完成了

评论