Steam 输入是一项服务,允许 Steam 用户用自己喜爱的设备游玩任何支持控制器的游戏。 Steam 输入将通过手柄模拟、键鼠模拟或 Steam 输入 API 将用户的输入转化为游戏所能理解的信息。 在这里,我们将重点说明如何最好地使用 Steam 输入手柄模拟,来扩展您游戏现有的控制器支持。

什么是手柄模拟?

在 Windows 操作系统中,Steam 界面会和 XInput、DirectInput、RawInput 以及 Windows.Gaming.Input 挂钩,并注入模拟的 Xbox 控制器设备。 在 MacOS 和 Linux 中,模拟的控制器由驱动提供。

控制器将会作为 Xbox 控制器出现在您游戏中,也就意味着带有额外输入的控制器,有些输入会彼此重复。比如,Playstation 触控板点击和选项按键都会映射至 XInput 的 Start 按键。

除了一般的手柄输入外,还可以将 Switch、PlayStation 和 Steam 控制器陀螺仪输入绑定至鼠标模拟,提供动作控制。 这仅适用于单一本地玩家的游戏,因为只有一个鼠标输入,并且也依赖于游戏同时接收鼠标和手柄输入。 如果您对这些功能感兴趣同时又不想受到以上警告限制,请考虑添加 Steam Input API。

您可以询问 Steam 当前使用的控制器类型,来显示特定设备的字形,但仅限于您当前 Steamworks SDK 支持的类型。 如果您对永不过时的字形支持感兴趣,请整合 Steam Input API。

即使您已经支持某些控制器,可能还是有用户会通过 Steam 输入来使用这些控制器,因为 Steam 远程畅玩使用它在流式传输的同时提供输入,而有相当一部分用户也都启用了 Steam 输入,来在整个 Steam 库中重新配置其控制器。 总体来说,2020 年所有的控制器会话中,大约有四分之一使用的是 Steam 输入,其中包括近乎一半的 Playstation 控制器会话。

Steam 上有超过 2000 款游戏在为至少一种控制器使用手柄模拟,包括 Monster Hunter: World、Ace Combat 7、《勇者斗恶龙 XI》、Into the Breach、《中土世界:战争之影》等知名游戏,尽管不是所有这些游戏都采纳了全部的最佳实践 在这里,我们要以 Into the Breach 为例,因为这款游戏在遵照每一项最佳实践方面做得非常好。

显示设备专用艺术作品我们支持几种通过 Steam 输入手柄模拟获取设备专用字形的途径。一种针对可以在运行时载入永不过时的图像(即当 Steam 更新了 Steam 输入时这类图像无需任何游戏更新也能使用)的游戏;另外两种针对需要将图像烘焙进自己的资产中的游戏,或希望使用与控制台端口相同风格的艺术作品的游戏。 Into the Breach 为控制器使用了自己的艺术作品。Xbox/Steam:

PlayStation:

Nintendo Switch 控制器:

注意:即使您使用的是您自己的艺术作品,我们也建议您在无法识别控制器时回退至 Steam 艺术作品,或调用我们的帮助函数以找到您的游戏发行时存在的最接近选项,这样今后添加的设备会有适当的字形。

显示 Steam 的永不过时字形您需要使用以下函数:

// 在使用单个函数前初始化接口——仅需调用一次!

SteamInput()->Init()

// ...

// SteamAPI_RunCallbacks() 会为所有初始化的接口调用 RunFrame 函数,

// 且大部分游戏会已经开始定期调用此代码。 如果您没有这么做,则需要手动更新

// Steam 输入接口

SteamInput()->RunFrame();

// ...

// 用您正在询问的 XInput 槽位代替。 这一数值在 0 至 3 之间

// 如果您在决定使用哪个 API 之前就使用 RawInput 来检测设备,

// 请参阅“使用 RawInput 检测设备”一节。

int nXinputSlot = 0;

// 用您查询的按钮进行替代

EXboxOrigin eXboxButtonToGetGlyphFor = k_EXboxOrigin_A;

EInputActionOrigin buttonOrigin = k_EInputActionOrigin_XBoxOne_A;

// 如果控制器是通过 Steam 输入配置的,转换按钮

InputHandle_t controller1Handle = SteamInput()->GetControllerForGamepadIndex( nXinputSlot );

if ( controller1Handle > 0 )

{

// 有效的句柄不是 0,这是通过 Steam 输入配置的控制器

// 注意:使用 Steam 输入 API 的控制器将不会通过 GetControllerForGamepadIndex() 返回句柄

buttonOrigin = SteamInput()->GetActionOriginFromXboxOrigin( controller1Handle, k_EXboxOrigin_A );

}

else

{

// 有效的句柄不是 0,这是一个普通的 Xbox 控制器

// 继续使用原来的按键

}

// EInputActionOrigin 的值会随着 Steam 添加更多支持而增加,但这没有关系,

// 因为在此示例中,我们会从 Steam 处得到设备图像,同时也可以提供新的字形图像

// 从 Steam 客户端获取图像

const char *localGlyphPath = SteamInput()->GetGlyphForActionOrigin( buttonOrigin );

printf( "path = %s\n", localGlyphPath ); // "path = C:\Program Files (x86)\Steam\tenfoot\resource\images\library\controller\api\ps4_button_x.png"

// 将此替换为游戏中将文件路径转换为可用游戏纹理的函数

glyphTextureID = loadButtonGlyphTextureFromLocalPath( localGlyphPath )

显示您自己的艺术作品——字形面板如果您在使用多个字形面板并根据控制器类型来选择使用哪一个,您可以使用以下方法通过 Xinput 槽位来向 Steam 询问此信息:

// 在使用单个函数前初始化接口——仅需调用一次!

SteamInput()->Init()

// ...

// SteamAPI_RunCallbacks() 会为所有初始化的接口调用 RunFrame 函数,

// 且大部分游戏会已经开始定期调用此代码。 如果您没有这么做,则需要手动更新

// Steam 输入接口

SteamInput()->RunFrame();

// ...

// 用您正在询问的 XInput 槽位代替。 这一数值在 0 至 3 之间

// 如果您在决定使用哪个 API 之前就使用 RawInput 来检测设备,

// 请参阅“使用 RawInput 检测设备”一节。

int nXinputSlotIndex = 0;

InputHandle_t inputHandle = SteamInput()->GetControllerForGamepadIndex( nXinputSlotIndex );

if ( inputHandle == 0 )

{

// 有效的输入句柄不是 0,这是一个普通的 Xbox 控制器。

}

else

{

// Steam will always return an enum value that was valid for your SDK version.

ESteamInputType inputType = SteamInput()->GetInputTypeForHandle( inputHandle );

switch( inputType )

{

case k_ESteamInputType_Unknown:

printf( "unknown!\n" ); break;

case k_ESteamInputType_SteamController:

printf( "Steam controller!\n" ); break;

case k_ESteamInputType_XBox360Controller:

printf( "XBox 360 controller!\n" ); break;

case k_ESteamInputType_XBoxOneController:

printf( "XBox One controller!\n" ); break;

case k_ESteamInputType_GenericXInput:

printf( "Generic XInput!\n" ); break;

case k_ESteamInputType_PS4Controller:

printf( "PS4 controller!\n" ); break;

}

}

显示您自己的艺术作品——每个按键的艺术作品如果您在使用单一的由操作源索引的查找表,您可以使用这些函数来实现特定设备的字形,并将任何无法识别的控制器转换为最为接近的控制器。

// 在使用单个函数前初始化接口——仅需调用一次!

SteamInput()->Init()

// ...

// SteamAPI_RunCallbacks() 会为所有初始化的接口调用 RunFrame 函数,

// 且大部分游戏会已经开始定期调用此代码。 如果您没有这么做,则需要手动更新

// Steam 输入接口

SteamInput()->RunFrame();

// ...

// 用您正在询问的 XInput 槽位代替。 这一数值在 0 至 3 之间

// 如果您在决定使用哪个 API 之前就使用 RawInput 来检测设备,

// 请参阅“使用 RawInput 检测设备”一节。

int nXinputSlot = 0;

// 使用您查询的按钮进行替代

EXboxOrigin eXboxButtonToGetGlyphFor = k_EXboxOrigin_A;

EInputActionOrigin buttonOrigin = k_EInputActionOrigin_XBoxOne_A;

// 如果控制器是通过 Steam 输入来配置的,转换按钮

InputHandle_t controller1Handle = SteamInput()->GetControllerForGamepadIndex( nXinputSlot );

if ( controller1Handle > 0 )

{

// 有效的句柄不是 0,这是通过 Steam 输入配置的控制器。

// 注意:使用 Steam 输入 API 的控制器将不会通过 GetControllerForGamepadIndex() 返回句柄

buttonOrigin = SteamInput()->GetActionOriginFromXboxOrigin( controller1Handle, k_EXboxOrigin_A ); //i.e, k_EInputActionOrigin_PS4_X

}

else

{

// 有效句柄不是 0,这是一个普通的 Xobx 控制器

// 继续使用原来的按键

}

// Steam 将会继续添加操作,且将来的控制器将会超过当前的范围

// 如果您想要进行测试,您可以假装 Switch/PS5 控制器不存在,并将此更改为:

// if ( buttonOrigin >= k_EInputActionOrigin_XBox360_Reserved10 )

if ( buttonOrigin >= k_EInputActionOrigin_Count )

{

// 我们的游戏中没有为此源推出任何艺术作品! 我想 Steam 已添加了

// 对一个新控制器的支持。 让我们获取最接近于我们针对其而创建的 SDK 的值

buttonOrigin = SteamInput()->TranslateActionOrigin( k_ESteamInputType_Unknown, buttonOrigin );

}

// 将此替换为游戏中会为某个按键返回您的艺术作品的函数

int glyphTextureID = getHardCodedButtonGlyphTexture( buttonOrigin );

使用 RawInput 检测设备一些游戏会使用 RawInput 来支持 4 个以上的控制器,或使用它来判定是否有 PlayStation 控制器连接到了游戏。 此 API 还会返回其他非手柄类设备,且并没有可靠的索引以直接从 Steam 输入函数映射。 想要获得此信息,您可以从 GetRawInputDeviceInfo() 查询 RIDI_DEVICENAME 字符串,前者用真实设备的 USB VID/PID、手柄索引以及 Steam 输入句柄编码,格式如下:

\\.\pipe\HID#VID_045E&PID_028E&IG_00#{Real device VID}&{Real Device PID}&{Steam Input API handle}#{Steam Input Gamepad Index}#{ProcessID}

例如,\\.\pipe\HID#VID_045E&PID_028E&IG_00#045E&0B00&00045EB00704DAF3#0#20160 for an Xbox one controller with a VID/PID of 0x45e/0x0b00。

代码示例:

#define VALVE_DIRECTINPUT_GAMEPAD_VID0x28DE

#define VALVE_DIRECTINPUT_GAMEPAD_PID0x11FF

#define STEAM_INPUT_VID_INDEX37

#define STEAM_INPUT_PID_INDEX42

#define STEAM_INPUT_SIAPI_HANDLE_INDEX47

#define STEAM_INPUT_GAMEPAD_INDEX64

#define MAX_PATH 260

//...

void YourFunction()

{

PRAWINPUTDEVICELIST devices = NULL;

UINT i, j, device_count = 0;

if ((GetRawInputDeviceList(NULL, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!device_count)) {

return; /* oh well. */

}

devices = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * device_count);

if (devices == NULL) {

return;

}

if (GetRawInputDeviceList(devices, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) {

free(devices);

return; /* oh well. */

}

for (i = 0; i < device_count; i++) {

RID_DEVICE_INFO rdi;

char devName[MAX_PATH];

UINT rdiSize = sizeof(rdi);

UINT nameSize = MAX_PATH;

rdi.cbSize = sizeof(rdi);

if ( devices[i].dwType == RIM_TYPEHID &&

GetRawInputDeviceInfoA( devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize ) != (UINT)-1 &&

GetRawInputDeviceInfoA( devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize ) != (UINT)-1 )

{

if ( rdi.hid.dwVendorId == VALVE_DIRECTINPUT_GAMEPAD_VID && rdi.hid.dwProductId == VALVE_DIRECTINPUT_GAMEPAD_PID )

{

uint32 ulVID = strtoul( &devName[STEAM_INPUT_VID_INDEX], NULL, 16 );

uint32 ulPID = strtoul( &devName[STEAM_INPUT_PID_INDEX], NULL, 16 );

uint64 ulDeviceHandle = strtoull( &devName[STEAM_INPUT_SIAPI_HANDLE_INDEX], NULL, 16 );

uint32 unGamepadIndex = strtoul( &devName[STEAM_INPUT_GAMEPAD_INDEX], NULL, 16 );

[/i][/i][/i]

Log( "Raw input device: VID = 0x%x, PID = 0x%x, handle = 0x%llx, index = 0x%x, %s\n", ulVID, ulPID, ulDeviceHandle, unGamepadIndex, devName );

// 您现在可以使用 VID/PID 直接识别设备或使用 ulDeviceHandle 和 GetInputTypeForHandle

ESteamInputType inputType = SteamInput()->GetInputTypeForHandle( ulDeviceHandle );

switch( inputType )

{

case k_ESteamInputType_Unknown:

printf( "unknown!\n" ); break;

case k_ESteamInputType_SteamController:

printf( "Steam controller!\n" ); break;

case k_ESteamInputType_XBox360Controller:

printf( "XBox 360 controller!\n" ); break;

case k_ESteamInputType_XBoxOneController:

printf( "XBox One controller!\n" ); break;

case k_ESteamInputType_GenericGamepad:

printf( "Generic Gamepad(DirectInput)!\n" ); break;

case k_ESteamInputType_PS3Controller:

case k_ESteamInputType_PS4Controller:

case k_ESteamInputType_PS5Controller:

printf( "PlayStation controller!\n" ); break;

}

}

else

{

// 设备不是通过 Steam 输入而运行的控制器——请使用您的普通控制器 ID 逻辑

continue;

}

}

}

free(devices);

}

Setting the Steamworks Settings第一步是在应用程序->Steam 输入下 Steamworks 设置中的选择加入栏目里设置哪个控制器将会使用 Steam 输入。 对于带有 Xbox 控制器支持的标准游戏,我们建议勾选 Xbox 外的所有复选框:

如果您的游戏支持飞行摇杆或赛车方向盘,您需要取消勾选 Generic/DirectInput 控制器的复选框,因为 Steam 不支持重映射这些设备。

选择一个配置您可以创建您自己的配置或从我们预制的模板中选择一个。 您无需创建自己的配置,除非您计划不采用我们默认的配置,或者要按游戏中操作标记单个按键设置。 Steam 输入的内置模板已本地化为所有 Steam 支持的语言,因此请确保您同样对配置进行本地化,以避免向非英语使用者显示英语。

选择一个模板游戏主机控制器不区分各种不同的手柄模板,但这些设置对于 Steam 控制器而言很重要。 以下是对各模板的解释,以及各模板适用的游戏类型:

手柄 - 将右触控板转化为原始模拟摇杆,模拟 Xbox 控制器。 对双摇杆、平台游戏或体育类游戏非常有用。

高精度视角/瞄准手柄 - 第一人称射击或任何使用视角控制的游戏的理想配置。 在选择此项前,请认真测试您的游戏是否支持同时使用手柄和鼠标,包括所有辅助场景,如库存或地图界面等。

带视角控制的手柄 - 此配置从触控板获得鼠标样式输入的支持,并将其转换为仿真摇杆拨动。 如果您的游戏可以配置成摇杆极高灵敏度、无死区、具有直线加速曲线,那么此配置将有效运作。否则,请考虑使用鼠标/键盘配置。

您可以在此处了解在 Steamworks 中为您的游戏将模板设置为上线的步骤。

创建自定义配置您可以采用在多种控制器类型中自动转换的配置,但是您至少需要制作一个 Steam 控制器和 Xbox 控制器配置。

注意:如果您计划利用设备专用功能,如运动控制等,您还需要一个针对 Playstation 4 或 Nintendo Switch 控制器的配置。

文本输入

屏幕上的文本输入从技术上讲并不是 ISteamInput(Steam 输入)的组成部分,而是属于 ISteamUtils。 当前,仅在玩家通过大屏幕模式启动游戏时才会实现。

部分快速参考如下:

配置本地化

在不使用 Steam 输入 API 时制作本地化的配置会更复杂一些,但并非不可实现。

首先,确定您在上次导出配置之后未编辑过配置。 打开配置器进行编辑(可以在修改后再复原)。

打开 Steam\Logs\controller_ui.xt 日志,找到这样的内容:

Steam Controller Saving Controller Configuration Autosave for [控制器序列号]- AppID: [您的 APPID]。

Loaded Config for Local Selection Path for App ID [您的 APPID], Controller 0: F:\ProgramFiles\Steam\client\userdata\[STEAMID]\config\controller_configs\apps\[您的 APPID]\[控制器序列号]\guest\controller_configuration.vdf

插入您要本地化的按键设置本地化令牌

标题/描述:

"controller_mappings"

{

"version""3"

"revision""5"

"title""#title"

"description""#description"

...

按键设置:

"button_a"

{

"activators"

{

"Full_Press"

{

"bindings"

{

"binding""xinput_button A, #abutton"

}

}

}

}

...

然后为本地化程序块中的各语言提供相应的值:

"localization"

{

"english"

{

"title""This is a localized title"

"description""This is a localized description. The button diamond binding also have localized names."

"abutton""Your name here 1"

"bbutton""Your name here 2"

"xbutton""Your name here 3"

"ybutton""Your name here 4"

}

...

您现在可以导出配置,并按这些说明在合作伙伴站点进行设置。 您需要为您的一个配置设置“使用操作快”复选框。

您可以在浏览器中输入以下 URL 以在 Steam 客户端里预览示例配置:steam://controllerconfig/681280/1744110334,或者,您可以在这里找到 VDF 文件。

linux模拟手柄输入,Steam 输入手柄模拟最佳实践相关推荐

  1. 调试Linux系统挂起和休眠问题的最佳实践

    概述 当谈到Linux系统的挂起/休眠时, 我们指的是以下三种受支持的Linux系统休眠状态: STI(Suspend To Idle)是一种通用的.纯软件.轻量级系统睡眠状态.与特定于平台的驱动程序 ...

  2. linux部署vue项目_Vue项目部署的最佳实践

    点击上方"前端教程",选择"星标" 每天前端开发干货第一时间送达! 作者:沉末_ juejin.im/post/5eb2243e51882555d8457833 ...

  3. 模拟键盘按键 自动输入文字

    模拟键盘按键 自动输入文字 键盘对于每个操作电脑的人员来说是最熟悉不过的了.键盘上的按键可分为两类 按下后会在电脑的输入窗口上出现对应字符的按键,如字母键和数字键等,我们称之为字符键:按下后虽然看不到 ...

  4. 模拟滑动android无障碍,Android无障碍简单开发-模拟滑动点击输入等

    似乎除了input type=password的edit text 其他所有的UI都可以进行操作.直接开始. 首先配置环境. 创建一个service 清单中代码如下,就算用AS直接创建,也需要配置一些 ...

  5. java模拟器键盘输入_Java模拟鼠标和键盘输入

    用途 在电脑(Windows/Mac)上模拟鼠标和键盘输入 Mac运行需要打开相关权限,详见文末说明. 效果图 代码 import java.awt.*; import java.awt.event. ...

  6. -Java 模拟登录时需要输入验证码功能

    模拟登录时需要输入验证码功能 本文通过java,随机数实现登录时需要输入的验证码功能: 验证码有4个字符组成 验证码只能由数字和大写字母组成(机制可以随意添加更改验证码的组成元素) 验证码要求字符不能 ...

  7. linux c 文件键盘写入,linux - C非阻塞键盘输入

    linux - C非阻塞键盘输入 我正在尝试用C语言编写一个程序(在Linux上)循环直到用户按下一个键,但不应该要求按键继续每个循环. 有一个简单的方法吗? 我想我可以用select()这样做,但这 ...

  8. qt 调用linux键盘输入,嵌入式linux上QT标准键盘输入的实现

    1.嵌入式linux上QT标准键盘输入的实现 在嵌入式平台上运行QTE时,使用的键盘通常不是标准键盘,而是嵌入式设备外扩的普通按键.那么实现QTE键盘输入的方法大体上可以分为两类: (1)编写一个普通 ...

  9. PS5运行Linux,索尼发布新驱动 PS5手柄现已支持Linux系统用户

    据外媒Phoronix,索尼近日发布了一个新的"hid-playstation"驱动,为Linux操作系统用户提供了PS5手柄DualSense以及其他PlayStation硬件的 ...

  10. linux shell数据重定向(输入重定向与输出重定向)详细分析

    转载自: linux shell数据重定向(输入重定向与输出重定向)详细分析 - 程默 - 博客园 http://www.cnblogs.com/chengmo/archive/2010/10/20/ ...

最新文章

  1. What is corresponding Cron expression to fire in every X seconds, where X 60? --转载
  2. 拷贝mp3java_字节流复制mp3文件(带缓冲区)
  3. HDL输入设计详解攻略
  4. EIGRP的successor与fessible successor
  5. java如何获得当前文件路径
  6. 2021,我在枯燥乏味中寻找坚持下去的理由
  7. bzoj 4393 Usaco Fruit Feast
  8. 3-java学习笔记
  9. repo一个新工程使用步骤
  10. php复制按钮,【typecho】typecho优化之代码框添加复制按钮
  11. matlab 2018 adams,关于ADAMS与MATLAB联合仿真的一点经验
  12. html写简历怎么导出pdf,简历在线生成,在线生成PDF或word格式简历
  13. 求职之测试开发技能准备
  14. 建无根树+无根树转有根树
  15. CR渲染器全景图如何渲染颜色通道_【3D】日不落投影灯 VR/CR投影效果制作
  16. depts: deep expansion learning for periodic time series forecasting
  17. myeclipse10注册机详细使用方法
  18. 学完python可以从事哪些工作?
  19. 软件测试学习笔记与思考(2)---软件测试策略
  20. R语言环境下Bioconductor安装2020-10-31

热门文章

  1. 从《乔家大院》悟企业留人
  2. 常见函数泰勒公式展开(清晰)
  3. 【OpenCV】检测人脸、眼睛、鼻子、耳朵、嘴
  4. 卸载office2010后 再安装2013 错误1706 安装程序找不到需要的文件
  5. (转载)App原生开发、混合开发及HTML5开发的优劣
  6. 【随便说说】|| 老师用腾讯会议开直播,实在忍不住了,我......
  7. 如何快速分解CAD图纸中多个合并的CAD图形?
  8. python画画excel_用Python在Excel里画出蒙娜丽莎的方法示例
  9. 干货 | 团队建设共同愿景的探索
  10. 10大主流性能测试工具,总有一款适合你