关键字:
Enhanced Input 、 输入、映射、事件、鼠标、键盘、键鼠、动作、Trigger、触发器、
疑问:
- 新输入系统怎么做一个基础的案例?
- Trigger修改器中每个项都是什么功能?
- 功能边界问题:如时刻、时段、单次事件、持续事件、可变频率事件,延迟事件、前置事件、后置事件?
- InputAction和InputMappingContext中都有修改器,那么听谁的?
- 如何获得鼠标平移? 如何将WSADQE的三维XYZ的事件进行输出?
看完希望你会找到答案。
增强输入系统 Enhanced Input
最主要的两个组件
- Input Mapping Context 输入映射管理器 建立映射关系,是一套映射方案 简称 IMC
- Input Action 输入动作 单个键盘鼠标动作事件 简称 IA
二者关系:
Input Mapping Context
|——Input Action
|——|——映射对应键鼠
|——|——映射对应键鼠
|——|——… …
|——Input Action
|——|——映射对应键鼠
|——|——映射对应键鼠
|——|——… …
|——Input Action
|——|——映射对应键鼠
|——|——映射对应键鼠
|——|——… …
|—— … …
简单的例子:制作一个鼠标左键事件,单击、抬起并print信息
一、制作Input Action
创建一个IA,在空白区域右键,弹出菜单Input / Input Action ,起名IA_LMB,用它表示单击左键
- 这个IA起名字,根据项目而定。可以是键鼠对应的按钮名称,也可以是角色行走、开枪的动作名称。 命名方式很重要,它是蓝图中的事件的名称。
- 类似载具类操作,如汽车,大多将水平和垂直轴分开处理,即WS一组、AD一组、有的还会有QE一组。这样分开输出比起合在一个事件里的三维坐标更容易控制,因为车辆前后是油门,在没有油门的时候,左右是不动的。
- 我的项目以 键盘操作 为主,所以用 <键鼠按钮名字_事件名称> 命名。如下图,展示了正式项目中,围绕鼠标左键的双击、按住、按下、抬起事件。之所以分的这么细致的产品需求是:项目有大量键鼠组合操作。
注意: 时刻、时段,有区别。
名称 事件描述 IA_LMB_DubbleClick 鼠标左键双击事件 IA_LMB_Hold 鼠标左键持续按住,并持续输出事件 IA_LMB_Pressed 鼠标左键按下时刻事件 IA_LMB_Released 鼠标左键抬起时刻事件 这么做对于后续的键鼠事件配置,也将更为复杂 。
回到简单案例,当前的案例没有那么复杂的命名方式,双击打开IA_LMB,将Value Type 修改为Digital(bool)类型
如上图,在value Type中对于:
- 鼠标按下、抬起的瞬时动作, 使用bool 输出范围 true、false;
- 鼠标滚轮缩放, 使用 Axis1D(float) 输出范围0.0-1.0f;
- 鼠标水平移动,键盘WSAD, 使用 Axis2D(Vector2D) ;
二、制作Input Mapping Context,设置对映按键
创建一个IMC_起名FL_IMC
双击打开,添加一个mappings,指向IA_LMB,映射按钮设置成鼠标左键,如下图
三、做蓝图
在Playercontroller蓝图中添加 AddMappingContext ,如果不添加无法获取事件。
再添加事件 IA_LMB ,并打印
测试如下:
完成。
扩展与详解
细节1:Triggered触发是什么频率?帧率可以自定义吗?
答:频率可以调节
打开案例中的 IA_LMB
下方Triggers里,添加一个 Pulse脉冲修改器
Interval:频率,每秒钟的次数,输入0是按Tick频率
TriggerLimit:触发限制,一次按下的累计次数,到达次数返回Completed。输入0是不限制次数。
这个效果类似于枪械射击,但真正的枪械还有换弹夹、射速不同的复杂动作不会使用从此方式制作射击的。 狙击枪右键开镜,可以设置为1。
此外:Trigger on Start : 勾选按下就触发事件;不勾选,延迟间隔频率时间后再触发事件。
Triggers的设置
Trigger修改器详解(重点)
默认的顺序在Pressed之前,所以如果想得到纯洁的hold按住事件,最好用Hold延迟0.05秒的办法。
通常正式项目中只关注蓝图中的Trigger事件,少数特殊需求才会用到Completed事件。
● Pressed:按下时刻,先后各触发一次Trigger和Completed事件;在抬起时刻,无任何事件;Pressed是非持续事件。
● Pulse:脉冲事件,可设置为单次、多次、不同频率或持续Trigger事件。自由度最高可以模拟其他各类事件。
● Released,按下时段,无任何事件;抬起时刻,先后触发各一次Trigger和Completed事件;非持续事件。
● Hold:按下时长N以后,开始持续发出Trigger事件,在抬起时,触发一次Completed事件;勾选Is one Shot则Trigger和Completed一起被先后触发各一次。在N时间内无事件。
● HoldAndRelease:按下多久后,抬起时刻,先后触发各一次Trigger和Completed事件,按下时长不足时无事件。
● Down:按下时段,持续Trigger事件,频率不可调;在抬起时刻,Completed事件。
● Tap:按下后,在TapReleaseTimeThreshold时间内抬起,则先后各触发一次Trigger和Completed事件,超时抬起,无任何事件。
● Combo:按键组合,如Ctrl+T。在Time to Press Key时长内的按键有效,超过时长无任何事件。还可以设置两个按钮,一个开启事件和另一个关闭事件。
此外在IMC里调节也是一样的
那么IA和IMC两个地方都调了,谁最终有效?
经测试:最终使用了IMC参数,IMC参数覆盖了IA的Modifiers。
细节2:我想要鼠标移动的XY参数怎么传递和接收?
制作一个Axis2D的IA_MouseMove
IMC里,映射给Mouse XY 2D-Axis
PC里蓝图
给蓝图Pring的key添加名字,mm是我随便起的,可以避免被持续打印的信息刷屏
效果:
此时我们看到Triggered并不完全是Pressed。所以翻译为“触发”更加准确,意为当事件发生了。
完成
细节3:WSADQE对应的3D如何区分横纵轴向?
创建IA_WSADQE
IMC添加映射
WS的映射如下图, Modifiers在不添加任何修改器的时候,默认是输出X轴向正方向,长度1.0f
S后退,为负,所以给S增加一个Negate修改器,意为反向将取值范围从1.0f改为-1.0f
AD的映射,其中给AD都添加了Swizzle Input Axis Values 混合输入轴向值
这个下面有一个order,给AD选择YXZ,但是为啥不是YZX呢?我也不知道这个原理是怎么看的,难道是右手定则?
此外给A一个Negate反向修改器,让A与D值相反
这样通过Swizzle Input Axis Values 确定了AD共轴向并与WS垂直。轴值范围是0.0-1.0f,反向就是-1.0f。
QE的映射,Q下降,E上升,还是要确定轴向,继续使用Swizzle Input Axis Values ,order选择ZYX。
给Q增加Negate反向修改器。
在PC中连接ActionValue三维向量,打印
打印如下:
测试Swizzle Input Axis Values 在QE上的设置
正确——ZYX 打印输出时 Z值变化
错误——ZXY 打印输出时 Y值变化并不是Z值变化
测试Swizzle Input Axis Values 在AD上的设置
正确——YXZ 打印输出时 Y变化
错误——YZX 打印输出是 Z变化
也就是说
键盘AD采用ZYX 并且 QE采用YXZ 方案 AD是左右 QE是下上
键盘AD采用ZXY 并且 QE采用YZX 方案 AD是下上 QE是左右
我试着查询数学原理,但我也没看懂,此处整理碎片知识点:
- 东北天,北东地才是地理信息行业通用的,一般用于导航。
- UE是北东天坐标系,这并不是一个广泛通用的坐标系,只是UE自己用。
- UE是X,Y,Z左手坐标系。
- 欧拉角阐述了三维空间中的任意角度都可以使用三个轴向的旋转而得到,即使用Yaw、Pitch、Roll可以得到任意角度。这也引出了三个轴向的排列顺序,有XYZ、XZY、YXZ、YZX、ZXY、ZYX六种情况。
备注:我对这个轴向旋转也是一知半解。目前的仅凭经验整理了规则,并非数学上吃透。
下面分享一下基于经验性的规律总结
如上图ZYX中,第一位是输出位,想输出Z轴值,就把Z放在第一位,即对应的是世界坐标的X位为输出位。
此时后两位有两种排列情况XY和YX,该选哪个呢?
假定推测是必须要有一个轴是不变的,与世界坐标系XYZ位置对应,才能保证轴变换有支撑。如下框图。
Z已经变了,而且占用了世界X的位置,即X也对不上了。那么Y就不能变了,所以第二位是Y,第三位自然就是X了,也就有了ZYX。经测试,ZYX正确。假定成立。
复杂设置的案例
将鼠标每种动作独立出来
鼠标左键按下
鼠标左键按住
鼠标左键抬起
鼠标左键双击
此处使用了一个Combo组合修改器,将两个release组合在一起
蓝图
### 测试效果如下
载具类轴向,WS/AD/QE 各个轴向分开。
原因:车辆的转向在没有油门值时,车辆原地不动。
创建三个IA,IA_GoForward前后WS,IA_GoRight左右AD,IA_GoUp上下EQ
三个IA都是Axis1D Float类型,如下图以IA_GoForward 为示例
IA_GoForward前后添加WS按键,其中S添加Negate反向修改器
IA_GoRight左右添加AD按键,其中A添加Negate反向修改器
IA_GoUp上下添加EQ按键,其中Q添加Negate反向修改器
在PC里面的蓝图
这里我将输入对应到了自定义事件中。方便以后使用。
对应三个自定义事件E_GoForward / E_GoRight / E_GoUp
题外话: 养成良好的代码习惯,以下方案供抛砖引玉:
- 自定义事件命名使用 “E_” 前缀
- 自定义函数命名使用 “Fn_” 前缀
- 事件 Event 在框架上用于应用层,主要职责是业务流程的搭建,如流程判断、分支
- 函数 Function 在框架上用于数据层,为业务流程搭建做方法支撑
“框架”与“架构”有区别。架构范围更大。单专业做模块或子系统叫框架,跨专业做总体叫架构。- 事件中可以包含函数、函数中不可以包含事件
- 将事件与函数有效解耦的过程,可以参考《设计模式》、《设计范式》
举一个错误的例子,问题如下图
错误效果如下图,缺少抬起按键,输出0复位。
上图问题在于:Triggered只能发出按下的事件。抬起按键时刻 Action Value = 0 复位,但这个事件 E Go Forward 接收不到。
正确的如下图,一定要有Completed,否则失败。
测试效果
按下WSADQE键,有 0 ,功能正常,测试通过