在一次想做鼠标拖拽摄像机旋转的时候,了解到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
窗口属性说明
为了大致地说明窗口的各个位置分别用来做什么,此处就用默认的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();
}
}
到这里右键点击旋转相机,松开右键相机吸附的效果就完成了