UE3输入输出开发笔记
很多游戏引擎因为只专注于图形渲染,所以很少为开发者提供输入事件处理方案。但UE3还是自带了一整套输入事件绑定系统,非常的自由灵活,可满足开发者各种开发需求。在UE3的官方文档中虽然有对UE3输入系统做一些基础介绍,但在一些细节上并未介绍清楚,初学者容易走弯路,而本篇文章是在官方文档基础上更为详细的整理了一些UE3输入系统的使用介绍,一起来看看吧。
1,一个简单的按键绑定例子。
A,*Input.ini中为按键添加绑定事件。
打开$UDKRoot$UDKGameConfigUDKInput.ini,在 [Engine.PlayerInput]模块下 输入一条映射条目如下:
Bindings=(Name="E", Command="TestE")
注意,该条目应该放在最靠后位置,如果前面已经存在Name="E"的条目,该条目 将被后面的冲掉。如下:
Bindings=(Name="E", Command="TestE1")
Bindings=(Name="E", Command="TestE2")
当按下"E"键时,"TestE1"将不会响应,"TestE2"对其进行响应。
另外,必须放置在[Engine.PlayerInput]模块下,它是游戏中响应输入的模 块,其他 模块如[GameFramework.DebugCameraInput]等有其他用途。
B,在UE3脚本中添加事件函数。
UDK中可以添加事件函数的类很多,一般会在PlayerInput 类下添加,如下:
Exec function TestE()
{
`Log('TestE');
}
注意,该函数一定要有Exec修饰符,否则按键绑定将没有效果。
C,保存ini文件,编译脚本并重启游戏,按下按键"E"时,便能输出日志"TestE"。
D,此例中只说明了一个按键"E"的绑定脚本方式,其他的输入事件绑定同理,文 档最后会把可绑定的所有输入事件列一个表。
2,*Input.ini中绑定条目Bindings的语法。
上例中我们添加了一个条目:
Bindings=(Name="E", Command="TestE")
这是绑定事件最简单的情况,意思是按键”E”按下时触发”TestE”命令,其中:
Name- 按键的绑定名称,也可以是别名或者映射的按键,后面会解释
Command- 当Name对应的按键或者事件被触发时,要执行的命令,它一般是UE3脚 本中的Exec修饰的可执行函数或者input类型变量。
再列一些后面要用到的修饰符定义:
Control- 激活这个按键时,需要按下Ctrl键
Shift- 激活这个按键时,需要按下Shift键
Alt - 激活这个按键时,需要按下Alt键
bIgnoreCtrl- 如果按下了Ctrl键,则忽略这个按键绑定
bIgnoreShift - 如果按下了Shift键,则忽略这个按键绑定
bIgnoreAlt- 如果按下了Alt键,则忽略这个按键绑定
Onrelease- 按键在释放时激活对应命令
现在,我们来扩展这个条目的各个功能。
A,组合按键。
我们想响应"Ctrl + E"按键,可以用如下语法:
Bindings=(Name="E",Command="TestE", Control=true)
同理,响应"Alt + E", "Shift +E"可以如下:
Bindings=(Name="E", Command="TestE", Alt=true)
Bindings=(Name="E", Command="TestE", Shift=true)
或者响应多组合键"Alt + Shift + Ctrl + E":
Bindings=(Name="E", Command="TestE", Control=true, Shift=true, Alt=true)
注意:默认情况下,Control,Alt,Shift都是为false的。
另外还有一个忽略按键选项,比如当按下Ctrl时,我不想响应"E"键,可以如下:
Bindings=(Name="E", Command="TestE", bIgnoreCtrl=true)
注意:在同时有Control=true和bIgnoreCtrl=true的情况下,该按键将在任何情况下 都无法响应,一般不会这样应用。
同理于Alt,Shift按键,有对应的bIgnoreAlt,bIgnoreShift,并且他们默认都为false。
B,按键按下或者释放响应,以及多命令响应。
我们想让"E"按下时响应"PressE",释放时响应"ReleaseE",语法如下:
Bindings=(Name="E",Command="PressE | onrelease ReleaseE")
Onrelease修饰符是加在Command下的,Command右边值可以有多个命令用“|“分 隔符分开,再比如:
Bindings=(Name="E",Command="PressE1 | PressE2 | onrelease ReleaseE")
该条目使"E"按下时同时响应"PressE1"和"PressE2"命令,释放时响应"ReleaseE"命 令。
C,按键的别名。
按键别名是什么,有什么好处。其实按键别名,就是游戏开发者为具体应用取的一 个虚拟的按键名,比如射击类游戏有开火动作,而键盘上又不存在专用的开火这个
按钮怎么办,那么我就在Bingdings下取一个Name="Fire"的条目,如下:
Bindings=(Name="Fire",Command="ExecFire")
这样做有什么好处呢?因为UE3是跨平台的引擎,它除了面向PC,还面向手机、XBOX等,这些不同平台的硬件输入是很不一样的,比如我想在PC中开火用"F" 按键,XBOX中用它的"Y"按钮,我们就可以用如下方式来绑定:
Bindings=(Name="F",Command="Fire")
Bindings=(Name="XboxTypeS_Y",Command="Fire")
这样无论PC中按下"F"按键或者XBOX中按下"Y"键,都能最终执行到ExecFire 命令。
D,按键绑定链。
按键绑定语法还可以动态修改按键绑定的命令,具体原理是通过UnrealScript的 SetBind 函数,它在Bindings语法内,可以通过Command来执行,并设置参数, 它在UnrealScript中的声明如下:
exec function SetBind(const out name BindName, string Command)
可以注意到它是一个exec修饰的函数,所以可以通过命令绑定,我们可以通过如 下 Bingdings语法来传参数并执行它:
Bindings=(Name="F",Command="SetBind F PressF")
第一次按下"F"时,它便将"F"绑定到"PressF"命令上,第二次按"F"则直接执行"PressF" 命令。
什么是按键绑定链,它有什么作用?按键绑定链是一种按键触发技巧,它的作用是 可以灵活的根据需要动态设置按键绑定的命令,它的实现方式用到了"SetBind" 命令和一些自定义别名。我们举个一个按键绑定开灯关灯的例子,我们可以这样做:
Bindings=(Name="L"Command="Light")
Bindings=(Name="Light",Command="Light | SetBind L LightOff")
Bindings=(Name="LightOff"",Command="ExecLightOff | SetBind L Light")
第一条Bindings:按键"L"绑定到别名"Light"上
第二条Bindings:别名"Light"对应命令是执行"ExecLight"开灯,同时通过"SetBind" 将"L" 绑定到别名"LightOff"上
第三条Bindings:别名"LightOff"对应命令是执行"ExecLightOff"关灯,同时通过 "SetBind" 将"L"绑定回别名"Light"上
这样,"L"按键将不断切换开灯关灯命令。
E,按键绑定变量。
官方文档中说道:“应该在 Controller、Input 或任何它们的子类(例如 PlayerController 或 PlayerInput)中定义这个变量”,也就是说按键绑定的变量应该
定义在上述两个类子类中,定义在其他类中将绑定不了。
先介绍一下按键绑定的语法:
1,Axis修饰符(轴修饰符)。
在Controller、Input 或任何它们的子类中声明一个自定义input类型变量如下:
var input float aMyAxis;
注意:Axis修饰符的变量必须是float类型,否则绑定将无效。
在UDKInput.ini中添加一条鼠标横轴方向(X轴)的响应:
Bindings=(Name="MouseX",Command="Axis aMyAxis")
这个时候,aMyAxis的值将会和鼠标横轴运动幅度相关,如鼠标往左会有一定的负 值,往右会有一定的正值,而在鼠标横轴方向不运动时则清零。
Axis类型变量也可以绑定到键盘按键上而非只有鼠标、操纵杆类型的模拟输入设 备。语法如下:
Bindings=(Name="E",Command="Axis aMyAxis")
这个时候如果按键E按下,则aMyAxis值将为1,释放状态则aMyAxis值为0。
也可以这样:
Bindings=(Name="E",Command="Axis aMyAxis Speed=-1.0")
这个时候如果按键E按下,则aMyAxis值将为-1,释放状态则aMyAxis值为0。
其中Speed是Axis的附加修饰符,另外几个Axis的附加修饰符,官方说明如下, 大家有兴趣可以一一在UDKInput.ini中去试试:
· AbsoluteAxis- 它是通过使用这个乘法器在增量时间过程中缩放输入值的预处理器。将其定义为一个整数。
· DeadZone- 如果这个值一点也没有改变输入数据上的绝对差,那么忽略输入数据。
· Invert- 它是使用这个数量倒置输入值的预处理器。
· Speed- 它是这个轴的变化的速度。
2,Button修饰符(按钮修饰符)
同上,脚本内声明一个input类型变量如下:
var input byte bMyButton;
注意:Button修饰符的变量必须是Byte类型,否则绑定将无效。
由于Button修饰符变量检测的是输入按键按下状态,所以,对于鼠标、操纵杆等 模拟设备的输入绑定无效,只能绑定按键类型输入,语法如下:
Bindings=(Name="E",Command="Button bMyButton")
当按键E按下时,bMyButton值为1,释放时,bMyButton值为0,可用于检 测 长按按键事件。
3,Toggle修饰符(触发器修饰符)
同样地,脚本内声明一个input类型变量如下:
var input byte bMyToggle;
注意:Toggle修饰符的变量同Button修饰符变量一样,必须是Byte类型,否则绑 定将无效。
同样地,Toggle修饰符也是检测输入按键按下状态,所以对于鼠标、操纵杆等无效。
语法如下:
Bindings=(Name="E",Command="Toggle bMyToggle")
当按键E按下第一次时,bMyToggle值为1即所谓开关打开,当按键E按下第二 次时,bMyToggle值为0即所谓开关关闭,如此往返。
4,Count修饰符(计数器修饰符)
同上,声明一个input类型变量:
var input byte bMyCount;
同Toggle、Button修饰符一样,Count修饰变量也必须为Byte类型,否则绑定将 无效。
同Toggle、Button不一样的是,Count修饰符即检测按键按下时间,也检测鼠标、 操纵杆等模拟设备的激活时间,只要激活状态存在,如鼠标在滑动,则游戏中每个 Tick,Count都会增1。
语法如下:
Bindings=(Name="MouseX",Command="Count bMyCount")
Bindings=(Name="E",Command="Count bMyCount")
则按键E在按下时或者鼠标沿X轴移动时,,游戏中每个Tick都会给bMyCount增 1,bMyCount的上限值是255,超过时则重新从0开始。
3,按键原理说明
A,输入来源及处理
Unreal底层有一个Input类,是游戏输入处理的基类,它有以下几个方法:
virtual UBOOL InputKey(...);
virtual UBOOL InputAxis(...);
virtual UBOOL InputTouch(...);
virtual UBOOL InputMotion(...);
它们分别是各游戏平台中输入消息的接收函数,例如PC中按下、释放或者按住按键E 会调用InputKey,将按键状(按下、释放或者重复)传入输入类进行处理,而鼠标滑 动 (或者操纵杆),则会调用InputAxis 将鼠标滑动的Delta值、Tick时间等传入 输入类 进行处理,总之,这几个函数就是对各个平台各种类型的输入进行各种处理,最终会将 输入数据统一包装,然后交给函数ExecInputCommands进行统一处理。
注意:处理UI的Scaleform另外也有一个输入处理基类以及一系列Input*()函数,只有Scaleform输入未捕获的输入,才能传入到UInput类中。另外Kismet中也可以捕获事 件,Kismet未捕获的输入才会传入到ExecInputCommands中,捕获优先级是 Scalefrom -> Kismet -> ExecInputCommands。
B,UDKInput.ini中Bindings条目与代码的联系
在Input的Input*()系列函数中,有如下代码片段(伪码):
StringCommands = GetBind( Key ) ;
If(Command.IsValid())
{
ExecInputCommands( Commands, GlobalLog )
}
Else
{
Super.Handle(...);
}
而在Input类中有一个数组:
var config array<KeyBind> Bindings;
数组元素类型KeyBind 的声明如下:
struct native KeyBind
{
var config name Name;
var config string Command;
var config bool Control, Shift, Alt;
var config bool bIgnoreCtrl, bIgnoreShift, bIgnoreAlt;
};
可见,这个数组保存了UDKInput.ini中的每一条Bindings所指内容,每个Bindings所 指内容都可以用KeyBind来保存。所以,每当有某个输入事件产生时,假设这个输入 事件名称为Key,则通过Key到Bindings数组里查找有没有对应要处理的Command,
如果有则执行ExecInputCommands(Commands, GlobalLog ) 。
注意:Bindings数组元素不止来自.ini文件,还可能在UE3脚本中通过函数SetBings动 态设置,或者在UE3编辑器中通过Kismet的方式添加,.ini文件只是比较普遍的一种
方式。
C,ExecInputCommands处理流程
在找到输入事件Key对应的Commands之后,则要在ExecInputCommands函数里执行
Commands里的内容,因为上文有说过,bindings里的Name可以绑定多个命令,所以,
这里的Commands字符串可能包含多个命令。ExecInputCommands的伪代码如下:
VoidExecInputCommands(string Commands)
{
While( ParseCommands(Commands,OneCommands) )
{
// 处理Kismet事件
If ( ProcessKismetEvents(OneCommand) ) continue;
// 处理脚本Exec函数
If( ProcessExecScriptFun(Onecommand)) continue;
// 处理绑定变量,Axis,Button, Count, Toggle ...
If(ProcessBindVar_Axis(Onecommand))continue; If( ProcessBindVar_Button(Onecommand) ) continue;
If(ProcessBindVar_Count,(Onecommand) ) continue;
If(ProcessBindVar_Toggle(Onecommand) ) continue;
...
// 处理按键绑定链
// 如果OneCommand本身是一个KeyBind的Name,则继续执行这个Name // 对应的Commands
For (int i=0;i< span="">
{
If ( Bingds[i].Name == OneCommand)
{
ExecInputCommands(Bingds[i].Commands);
continue;
}
}
}
}
D,按键绑定变量的处理细节,即上述伪码中ProcessBindVar_* 的处理细节
可以注意到Input类有如下声明:
var native const Map{FName,void*} NameToPtr;
var native const init array<pointer> AxisArray{FLOAT};
其中NameToPtr是一张Input类型变量的映射表,每次有按键触发绑定变量,则会去 NameToPtr里查找按按键对应的变量地址,并对变量进行数值修改。而AxisArray里则 是一个保存了所有浮点型Input类变量的数组。为什么要多一个AxisArray,而NameToPrt
表里不是也保存了同样一份浮点型的Input类变量的映射吗?从官方源码里看, AxisArray的主要作用是在每个Tick内,输入处理前,将每个float类型的input类 变 量重置为0,而其 他类型的input类型变量(例如Byte类型的input类型变量)则 不 需要每个Tick都重置。
在处理Axis类型的变量绑定时,是去NameToPtr里查找其中的float类型的input变量 ,并对该变量进行对应的输入的数值修改。
而在处理Button,Count, Toggle类型变量的绑定,则是去NameToPtr里查找Byte类型的input变量进行对应处理。
4,其他需要注意事项
UE3底层在加载.ini文件的时候,有一定的时间遵循规则,以输入相关的配置文件举例。UDKInput.ini和DefaultInput.ini的内容条目都会影响程序的按键绑定。首先,程序加载DefaultInput.ini文件,判断其文件时间戳数值是否比文件内记录的时间戳数值大,如果是,说明该文件改动过,则UDKInput.ini文件将被DefaultInput.ini文件内容覆盖。如果DefaultInput.ini文件未修改过,则程序先加载DefaultInput.ini的Bindings内容,再覆盖上UDKInput.ini的Bindings内容。
另外UDK*.ini系列文件,名称也不是非得要用“UDK”,你可以将它改成自己的游戏名称,但是本人论坛中得知,似乎只有官方授权的版本才允许这样做。源码中貌似是通过修改一个宏的名称即可。
由于UE3输入系统考虑了移动、平板、XBOX、PC等各种硬件输入设备,又考虑了 UI、游戏、Kismet等输入事件,另外还支持.ini文件的命令绑定按键配置语法和Unreal 脚本动态修改绑定,所以非常灵活和强大,能够满足开发者各种输入处理的开发需求,但也容易被各种输入事件混淆迷惑。
5,可映射按键
这是一个被映射到虚幻引擎 3 中的按键的列表。
键盘
功能键
· F1- 功能 1。
· F2- 功能 2。
· F3- 功能 3。
· F4- 功能 4。
· F5- 功能 5。
· F6- 功能 6。
· F7- 功能 7。
· F8- 功能 8。
· F9- 功能 9。
· F10- 功能 10。
· F11- 功能 11。
· F12- 功能 12。
字母按键
· A- 字每 A。
· B- 字母 B。
· C- 字母 C。
· D- 字母 D。
· E- 字母 E。
· F- 字母 F。
· G- 字母 G。
· H- 字母 H。
· I- 字母 I。
· J- 字母 J。
· K- 字母 K。
· L- 字母 L。
· M- 字母 M。
· N- 字母 N。
· O- 字母 O。
· P- 字母 P。
· Q- 字母 Q。
· R- 字母 R。
· S- 字母 S。
· T- 字母 T。
· U- 字母 U。
· V- 字母 V。
· W- 字母 W。
· X- 字母 X。
· Y- 字母 Y。
· Z- 字母 Z。
特殊键
· Escape-Escape(退出)键。
· Tab-Tab 键。
· Tilde-~(波浪号)。
· ScrollLock- 滚动锁。
· Pause- 暂停键。
· one-1。
· two-2。
· three-3。
· four-4。
· five-5。
· six-6。
· seven-7。
· eight-8。
· nine-9。
· zero-0。
· Underscore-_(下划线)。
· Equals-=(等号)。
· Backslash-(反斜线)。
· LeftBracket-[(左括号)。
· RightBracket-](右括号)。
· Enter- 回车键或数字键盘中的回车键。
· CapsLock- 大写锁定。
· Semicolon-;(分号)。
· Quote-'(引号)。
· LeftShift- 左侧的 shift 键。
· Comma-,(逗号)。
· Period-.(句点)。
· Slash-/(斜线)
· RightShift- 右侧的 Shift 键。
· LeftControl- 左侧的 control 键。
· LeftAlt- 左侧的 alt 键。
· SpaceBar- 退格条。
· RightAlt- 右侧 alt 键。
· RightControl- 右侧 control 键。
· Left- 左方向键。
· Up- 上方向键。
· Down- 下方向键。
· Right- 右方向键。
· Home-Home 键。
· End-End 键。
· Insert-Insert(插入)键。
· PageUp- 向上翻页。
· Delete-Delete(删除)键。
· PageDown- 向下翻页。
· NumLock- 数字锁定。
· Divide- 数字键盘 /。
· Multiply- 数字键盘 *。
· Subtract- 数字键盘 -。
· Add- 数字键盘 +。
· PageDown- 向下翻页。
· NumPadOne- 数字键盘 1。
· NumPadTwo- 数字键盘 2。
· NumPadThree- 数字键盘 3。
· NumPadFour- 数字键盘 4。
· NumPadFive- 数字键盘 5。
· NumPadSix- 数字键盘 6。
· NumPadSeven- 数字键盘 7。
· NumPadEight- 数字键盘 8。
· NumPadNine- 数字键盘 9。
· NumPadZero- 数字键盘 0。
· Decimal- 数字键盘小数点。
鼠标
· LeftMouseButton- 左鼠标按钮。
· RightMouseButton- 右鼠标按钮。
· ThumbMouseButton- 主要的鼠标上用拇指摸的按钮。
· ThumbMouseButton2- 次要的鼠标上用拇指摸的按钮。
· MouseScrollUp- 向上滚动的鼠标滑轮。
· MouseScrollDown- 向下滚动的鼠标滑轮。
· MouseX- 在 X 轴上的鼠标运动。
· MouseY- 在 Y 轴上的鼠标运动。
XBox360 控制器
· XboxTypeS_LeftThumbStick- 左拇指操纵杆作为按钮按。
· XboxTypeS_RightThumbStick- 右拇指操纵杆作为按钮按。
· XboxTypeS_DPad_Up- 控制台方向向上。
· XboxTypeS_DPad_Left- 控制台方向向左。
· XboxTypeS_DPad_Right- 控制台方向向右。
· XboxTypeS_DPad_Down- 控制台方向向下。
· XboxTypeS_Back- 返回按钮。
· XboxTypeS_Start- 开始按钮。
· XboxTypeS_Y-Y 按钮。
· XboxTypeS_X-X 按钮。
· XboxTypeS_B-B 按钮。
· XboxTypeS_A-A 按钮。
· XboxTypeS_LeftShoulder- 左肩按钮。
· XboxTypeS_RightShoulder- 右肩按钮。
· XboxTypeS_LeftTrigger- 作为按钮按下的左侧触发器。
· XboxTypeS_RightTrigger- 作为按钮按下的右侧触发器。
· XboxTypeS_LeftTriggerAxis- 按下一半的左侧触发器。
· XboxTypeS_RightTriggerAxis- 按下一半的右侧触发器。
· XboxTypeS_LeftX- 在作为模拟控件使用的时候左边大拇指摸的操纵杆水平位置。
· XboxTypeS_LeftY- 在作为模拟控件使用的时候左边大拇指摸的操纵杆垂直位置。
· XboxTypeS_RightX- 在作为模拟控件使用的时候右边大拇指操纵杆水平位置。
· XboxTypeS_RightY- 在作为模拟控件使用的时候右边大拇指操纵杆垂直位置。