DCS 自定添加控制input脚本(自定按键与轴)
亿点题外话
前阵子入手了成都造的f18起降面板,除了稍微贵了一点不过还是很香的:2个霍尔拨杆轴,一个电位器旋钮轴,1个电位器摇杆(游戏手柄上可push的那种)+可同步灯效起落架杆等其他开关和按钮

当初看中的是背光可和游戏同步,以及起落手柄灯。结果到手后惊呼被坑,原来官方当时支持f18机模,我不甘心,简单的琢磨了下原理,感觉能自己添加支持,就一顿瞎操作修改它驱动自带lua脚本实现了对AV8b和a10c的支持。,
当然我自己改好脚本后就联系了官方告知了他们具体解决办法,成都速度也很快。第2天中午就更新修好bug(早上先发现一个会和a10c起冲突的bug),第2天晚上就根据我提供的api在驱动软件内部添加的支持列表

然后测试灯效完毕后,准备把起飞面板的灯光旋钮设定到游戏中时才发现,游戏中这玩意是按键操控,而面板上这个是个轴。不过花了几个小时后还是终于搞定了。

下面开始正文(字数警告,不知不觉码字太多)
注意:修改lua文件最好使用专门的编辑器,比如notepad++或sublime text,同时做好备份原文件的准备,以备不测。
同时修改机模的input文件也不会触发dcs的文件完整性检查
一。定位修改文件
为了给某个机型添加按键input控制我们首先要动的是default.lua文件,这里面有所有操控设定,它位于
dcs安装目录 【机型文件夹】 【机型操作类型】 键盘或外设
\DCSWorld\Mods\aircraft\A-10C_2\Input\A-10C_2\joystick\default.lua
dcs安装目录 这个不用说,大家都知道应该怎么找
机型文件夹 这个也是对着找就是了。
机型操作类型 在某些有简易操控的飞机里有不同 区别是后面加_easy(基本是官方模组才有)
键盘或外设 这层目录有多个 我们需要改的基本是joystick和keyboard,如果需要键盘操作就需要改键盘目录下的,2者的改法都相同。下面都以joystick目录下改外设操控的方法做说明
default.lua 这层目录中 有的时候不只一个lua,还有很多其他以外设名字开头的lua文件,比如有2个文件是abc.lua和abc.diff.lua。这里dcs的逻辑是,当你的外设名字符合abc时。首先加载abc.diff.lua、然后找是否有abc.lua、如果没有则加载default.lua。default.lua和abc.lua是只取其一。abc.diff.lua是默认键位设置,而abc.lua和default.lua才是完整配置,这才是我们需要改动的
提示:如果当你修改default.lua后发现在控制设置中无法在特定外设中设定按键,说明该设备有独立的abc.lua。我们只需要修改对应名称的abc.lua即可

二。找到目标文件并确认插入代码的位置
编辑器打开default.lua ,绝大部分机模这里的代码分为3块。
forceFeedback = {
这其中的是力回馈设置
},
keyCommands = {
这其中的代码是按键设置
},
axisCommands = {
这其中的就是轴设置
},
开发组不同这里的写法可能也不同,比如AV8b这里是join(res.axisCommands,{。不过没什么影响

三。插入代码
因为我们是要添加一个轴控制,所以直接在axisCommands = {下面插入一行 键入如下代码,
{cockpit_device_id = devices.LTINT,action = int_light_commands.Knob_Console_Lights,name = _('Console背光灯轴控制')},
然后保存,启动dcs,打开控制设置。就成功了。

主要是因为a10c与AV8b背光灯的轴的api输入输出的值是0~1的浮点数。而一个轴在dcs中默认输出的是-1~1的浮点数。所以我们需要调整轴控制。(有部分飞机的灯光轴数据可以接受-1~1,比如米格21.具体情况自己调整)

如此这般就能正常使用外设上的轴来同步控制背光灯了

四。代码中的参数与数值的意义
cockpit_device_id = 代表的是要控制的部分属于哪一快子系统 可写定义名或deviveid,注意某些机模只可id
action = 代表的是要具体操控的按钮是该子系统里的哪一个 可写定义名或buttonid,注意某些机模只可id
name = 自然就是名称定义。_('')实际是一个多语言翻译函数。对应DCSWorld\l10n\cn\LC_MESSAGES的翻译对照表进行整句翻译。我们不管,只需要在里面键入你想取的名称即可。
4.1另外一些不在轴中使用,而在按键中使用的参数,以AV8b中自带的作为例子
//这是一个AV8b的水3档开关的设定范例,下面这是其2条代码中的一条
{down = engine_commands.Switch_H20,up = engine_commands.Switch_H20,cockpit_device_id = devices.DECS,value_down = 0.0, value_up = 0.5,name = _('H2O LN else OFF'),category = _('Joystick Switch Abstractions')},
down = 当设定的键位按下时触发的按钮buttonid 某些机模也使用pressed
value_down = 按下时传递的参数数值 使用iCommandPlanexxx方法时不需要该参数 某些机模也使用value_pressed等同value_down
pressed = 当设定的键位按下时触发的按钮buttonid 与down不同,当按键被按住时会不停的发送该参数
value_pressed = 传递的参数数值 使用iCommandPlanexxx方法时不需要该参数
up = 当设定的键位弹起时触发的按钮buttonid
value_up = 弹起时传递的参数数值 使用iCommandPlanexxx方法时不需要该参数
category = 该按键设定属于哪个分类下。分类定义。值同name使用_('')
4.2其他可用参数,以AV8b中自带的作为例子
{combos = defaultDeviceAssignmentFor("pitch") , action = iCommandPlanePitch, name = _('Pitch')},
combos 表示的是默认键位设定的是某某轴/键位。这个轴通过defaultDeviceAssignmentFor("pitch") 这个方法读取dcs设定的某个默认设定。一般我们自定按键可以不需要设定这个参数。
自带的action 中的定义iCommandPlanePitch,会在很多地方看到类似的 iCommandPlanexxxxxxxxxxx,这是封包在机模的dll中,作为内部的调用方式来实现的。如果使用iCommandPlane的方法,就不需要定义cockpit_device_id参数

五。如何查找参数的数据
经过上面的说明,锅碗瓢盆都有了,最关键的菜从哪里来呢。。
5.1找到按钮/开关/旋钮的定义名称
TIP.在dcs当中所有可输出显示数据的元素都有一个唯一的InterfaceID。这些元素包括这种可操控的按钮,旋钮,开关、 以及表示飞机状态的各种参数。这些id基本上是在机模的Scripts脚本目录中定义的。除去那些只负责输出显示数据的InterfaceID。可操控的都还有deviveid和buttonid。绝大部分可交互操控的都位于[机型文件夹]\Cockpit\(或者是子目录Scripts)目录下的clickabledata.lua之中。我们要操纵的开关是同时具有用于操控的deviveid和buttonid、以及用于输出状态的InterfaceID。
当我们需要实现一个操控时,要先定位这个开关/按钮的deviveid和buttonid。方法很多、我个人通常是多种方式使用;
通过关键字去github上找InterfaceID、再用找到的InterfaceID去clickabledata.lua查找或者直接用关键字去clickabledata.lua查找,再验证找到的InterfaceID是否正确。
其实还可以通过按键设定里的中文名字,去搜翻译对照表的原文,再用原文去搜clickabledata.lua,但是这一步还涉及到反编译mo汉化文件,不深入讲解了。
一般来说通过关键字找到的字符串就能判断出是否正确。
这里推荐2个github:
一是某佬转存并维护的ExportScripts脚本,里面有很多机模的InterfaceID。缺点是ExportScripts早已被原作者咕咕咕了,很多是2年前的数据 有些机模都没更新了 https://github.com/jieweiyang/DCS-ExportScripts/blob/master/Scripts/DCS-ExportScript/ExportsModules/
二是helios的github,这里的都是比较新的,缺点也很明显,只有部分机型有https://github.com/HeliosVirtualCockpit/Helios/tree/master/Helios/Interfaces/DCS 点进去 查看各个飞机目录里Interface.cs的源码
5.2如果找到的定义名称不可直接使用,则找它们的Index序号。(看5.4注意事项)
deviveid查找devices.lua中对应名称的序号,一般在后面会有注释index。如果没有就从上往下一个个的数
buttonid查找command_defs.lua中device_commands.xxx的定义的序号。
5.3确定按钮值。
value_down =或 value_up = 的值 还是在clickabledata.lua中查看,这一部分解释起来比较繁琐。
dcs中大部分按钮和轴所接受的数值基本上都是-1~1之间,有整数 也有小数。
如果会一点脚本语言,可以查看其调用的函数,查看函数中定义的arg_value和arg_lim来确定开关状态值。然而这部分实在是难以用傻瓜化的教程教会大家,这里只能自己多多摸索或者反复试错了。
大部分2档开关要么是0.0或者1.0
3档开关有可能是-1.0和0.0和1.0,也有部分机模是0.0和0.5和1.0
还有部分那种多档旋钮开关的 就是0.0和0.1和0.2和0.3到1.0这样的
机模不同,开发组不同,定义按钮参数都没有统一,只能多试或多查看函数
5.4注意事项
在某些机模中 down up cockpit_device_id action 这些参数不可写定义名称,只能使用id数字,比如A10C。
当你第一次用定义名称 启动游戏在控制设置中发现该机模没有显示任何按键设置 就是不兼容,换id吧

六。简单实例
a10c的3档襟翼设定,默认给的是适合用2个键盘按键来操控的设定,按一次往上或往下拨动一次开关,虽然ed专门写了内置的适合猪油那种2键3档开关的方法,但我们暂时先不使用内置方法来讲解。
--适合2键型3档开关on-off-on 缺陷:中位无法同步(内置方法同样无法同步中位)
{down = 3002, up = 3002, cockpit_device_id = 39,value_down = 0.0, value_up = 0.5, name = _('襟翼上/中'), category = _('Systems')},
{down = 3002, up = 3002, cockpit_device_id = 39,value_down = 1.0, value_up = 0.5, name = _('襟翼下/中'), category = _('Systems')},
39是襟翼所在的devices.CPT_MECH模块id,3002则是该模块的第2按钮id。0.0是开关在UP位时的状态,中位是0.5 ,DOWN则是1.0。将外设的上档位绑定第1条,下档位按钮绑定第2条。
当按钮被拨动到上档时,触发down按下事件,传递0.0参数到39的3002按钮,襟翼被设为收起;当按钮从上档位拨动到中时,触发上档位按钮的up弹起事件,传递0.5参数到39的3002按钮,襟翼被设为起飞档。
看似很完美,但是由于2键型3档开关的中位其实没键位的,只能由上或下拨动到中位时的up事件来响应,而dcs中的同步hotas设备功能是无法同步up事件的。
所以就有了下面这种写法,适合有3键3档位开关的外设。
--完美对应3键模式on-on-on
{down = 3002, cockpit_device_id = 39,value_down = 0.0, name = _('襟翼上'), category = _('Systems')},
{down = 3002, cockpit_device_id = 39,value_down = 0.5, name = _('襟翼中'), category = _('Systems')},
{down = 3002, cockpit_device_id = 39,value_down = 1.0, name = _('襟翼下'), category = _('Systems')},
给外设的上中下键位分别设定即可。
七。特殊实例
DCS中还有很多很奇特的开关。这些开关要么是有远古模块遗留下来所引发的缺陷,有要么是某些开发组瞎造轮子不按套路出牌整出来的。要来自定它们的功能,真的是非常麻烦以及很难达到效果
7.1添加2档开关型防滑开关,适用与2键型2档开关或1键型2档开关(A10C)
2键型2档开关:代表 成都的起降、火控面板的2档开关
这种开关的特点是3线接线,电气特性是ON-ON。在游戏控制器上是作为2个按键来看待,来回拨动时 属于[按键1弹起按键2按下] [按键2弹起按键1按下]的切换,也就是2个按键一直有一个处于按住的状态。用键盘理解就是 1个手指交替按住2个键中的一个。
(注意,成都造实际可能也还是2线接线,由固件模拟出的2键。而且目前还有点逻辑问题)
1键型2档开关:代表 疣猪油门的2档开关
这种开关的特点是2线接线,电气特性是ON-OFF。在游戏控制器上是作为1个按键来看待,来回拨动时 属于[按键1按下] [按键1弹起]的切换。用键盘理解就是一只手指按住一个按键 与松开
而A10C上默认的防滑开关是一个切换型,通过设定一个按键在开启与关闭2个状态之间切换,这肯定不符合我们要用在的2档开关上,不论是2档三线开关 还是2档二线开关。
根据上面的思路找到防滑开关并设定数值:
{down = 3028,cockpit_device_id = 38, value_down = 1, name = _('防滑开关 开启'), category = _('Landing gear panel')},
{down = 3028, cockpit_device_id = 38, value_down = 0, name = _('防滑开关 关闭'), category = _('Landing gear panel')},
可是!目前a10c里的防滑开关有比较坑爹的情况存在
在A10C里 这个防滑开关的定义是“电磁保持开关”。我们先不谈现实的电磁保持开关的特性,单说在游戏里的参数特性:在这一个开关里 它有2个buttonId。都只接受1和0的参数:
按钮1的参数1是切换。从on切换到off或者从off切换到on,参数0只能将on切换到off。
按钮2的参数0没有任何用处,参数1只能将off变成on位,且只有外观作用,实际游戏中的开关逻辑没有打开,所以按钮2可以无视。
因如上的特性,在实际绑定按键后且开启了dcs设置的“任务时同步HOTAS设备”时 就会用同步错误出现,比如外设的设备明明是关的状态,游戏是开的。或掰动开关时游戏内无反应 要多掰动2次之后才正常。
A10C中使用这种内部逻辑方式的按钮有7个,均为电磁保持开关,分别是防滑开关,EAC预位开关,防撞灯,SAS偏航及俯仰增稳通道的4个开关。而EAC开关 ED为它写了内部iCommand方法来实现开及关的状态,用于适配猪油上的EAC开关。
至此 这个防滑开关没有完美适配的办法吗?是的 可惜没有。除非ed为它们都写上iCommand方法。目前针对2键型2档开关有一个凑合还算能用的状态(任务开始时游戏内防滑开关是on时)(任务开始时游戏内是off时,外设开关在on位置无法同步游戏内的开关到on)我们这样写:
{down = 3028, up = 3028, cockpit_device_id = 38, value_down = 0, value_up = 1, name = _('防滑开关 按住关闭松开开启'), category = _('Landing gear panel')},
通俗来讲,就是我们将外设上这个开关的OFF位绑定上面的代码,这个开关的ON位不绑定任何东西。用OFF位的按下来关闭,用OFF位的弹起来开启。这是利用dcs的同步功能不响应UP事件的特性。
而针对1键型2档开关的外设来说 还是凑合用下面这种吧
{down = 3028, up = 3028, cockpit_device_id = 38, value_down = 1, value_up = 0, name = _('防滑开关 按住开启松开关闭'), category = _('Landing gear panel')},

7.2添加精准控制的HDG、CRS旋钮(F18暂时除外)
TIP.如果有用成都面板或者其他有编码器旋钮外设的飞友们,如果直接将旋钮设定给机模默认提供的按键操作 可能会遇见转动旋钮没反应 或者快速旋转,但是游戏中才转了一点的现象。这主要是可能是因为原始的操作模式是给键盘使用的。那个机制据我判断是通过按住的按键时长来控制游戏内的旋钮转多久,而大部分外设编码器是快速发送按下并弹起的按键信号,通常维持住的按住时长在5~20ms之间。
我们现在要自己添加一个控制方式给编码器用,并且希望实现编码器每转动一个触发、游戏内就转动1度的功能.
那么先看代码,以A10C为例,
{down = 3001, cockpit_device_id = 45,value_down = math.rad(1),name = _('HSI 航向旋钮 向右 步进1度'), category = _('Systems')},
{down = 3001, cockpit_device_id = 45,value_down = -math.rad(1),name = _('HSI 航向旋钮 向左 步进1度'), category = _('Systems')},
其他的参数常考上面的方法去寻找,这里最关键的是math.rad(1)这个参数。
以往别的插件或者默认操作的最小单位 在这个值这里设定的是0.01。而通过查看a10c的hsi代码HSI_param.lua我们能发现
ptrsBias = {{valmin = math.rad(0), valmax = math.rad(360), bias = math.rad(1)}}
旋钮的值与度数采用的是角度弧度公式来计算的。最小单位是1度的弧度值math.rad(1)=0.0174532925199433。
那么我们就可以直接把math.rad(1)作为步进数值填入。
但是这里有一个小问题,无论你是填入math.rad(1)还是0.0174532925199433,dcs的input代码处理的时候 都会将参数作为单浮点来处理(0.01745329)(推测的)。而helios可以传入双浮点数据(0.0174532925199433)给dcs且dcs支持。所以最终的情况就是 调整后,通过helios转十几个360度,航线的数字标识也不会错位。而用input代码控制使用外设编码器转个2-3圈的360度,数字标识就开始错位了。不过这点小问题并不碍事,毕竟一般来说也不太可能连续转个2圈以上了。只要不是强迫症就无所谓

另外:关于同步,虽然把所有off位都设定为能同步更拟真一些,但是在某些刚开始任务的场景下,并不是说所有按钮都设定同步为好。某些按钮能用一条有down和up的代码来实现,并且off的同步与否并不十分必要的话,就没有必要写2条down的代码去实现off的同步。这其实还是看个人习惯与外设是否支持off位设键位了

码字太多,写到后面发现有些地方写的并不是很完善,而且很多时候是想到什么就写上什么,显得有点乱,b站专栏似乎并不适合写需要经常修改更新的文字内容,况且还有发布后编辑限制,就离谱。。。文中有错误或疏漏欢迎各位大佬指点、评论留言查漏补缺
后续咕咕咕我会放个我用猎户油门 和成都起降面板的设定,来介绍下我改了哪些按键、并如何分配操作的