版权声明 © 2025 万维网联盟 (World Wide Web Consortium)。适用 W3C® 责任限制、商标及宽松文档许可规则。
Gamepad 规范定义了一个表示游戏手柄设备的低级接口。
本节描述了本文件发布时的状态。当前的 W3C 出版物列表和本技术报告的最新版本可以在 W3C 标准和草案索引中找到。
这是正在进行中的工作。
本文档由 Web 应用工作组 作为工作草案发布,遵循 推荐标准路径。
发布为工作草案并不意味着 W3C 及其成员的认可。
这是一份草案文件,可能随时被其他文件更新、替换或废弃。将其作为进展中的工作以外的引用是不恰当的。
本文档由在一个受 W3C 专利政策 约束下运营的组织所编写。W3C 维护了一份 与该工作组交付成果相关的专利披露公开列表;该页面还包含披露专利的说明。任何知晓其认为包含 必要权利要求 的专利的个人,必须按照 W3C 专利政策第 6 节 的规定披露相关信息。
本文档受 2023 年 11 月 3 日 W3C 流程文档 管辖。
本节是非规范性的。
一些 用户代理 连接了游戏手柄设备。这些设备在游戏应用及“10英尺”用户界面(演示文稿、媒体查看器)中是非常理想且适用的输入设备。
目前,使用游戏手柄作为输入的唯一方式是模拟鼠标或键盘事件,但这会丢失信息,并且需要用户代理之外的额外软件来完成模拟。
与此同时,原生应用能够通过系统 API 访问这些设备。
Gamepad API 通过指定允许 Web 应用直接操作游戏手柄数据的接口,为该问题提供了一个解决方案。
如果以完全通用的方式处理与控制游戏的外部设备进行接口对接,其工作量可能会变得巨大且难以处理。在本规范中,我们明确选择缩小范围,提供一个可被广泛实现且具有普遍实用性的功能子集。
具体而言,我们选择仅支持支持游戏手柄所需的功能。支持游戏手柄需要两种输入类型:按钮和轴。按钮和轴均报告为模拟值,按钮范围为 [0 .. 1],轴范围为 [-1 .. 1]。
虽然主要目标是支持游戏手柄设备,但支持这两种类型的模拟输入也允许支持当前游戏系统中常见的其他类似设备,包括操纵杆、方向盘、踏板和加速度计。因此,“gamepad”(游戏手柄)这个名称是示例性的,而不是试图成为本规范所涉及的整套设备的总称。
我们明确排除了对某些可能在特定游戏场景中使用且更为复杂的设备的支持,包括那些涉及运动传感、深度传感、视频分析、手势识别等的设备。
一个 游戏手柄 是输入控件和输出控件的集合。一个 输入控件 拥有随时间更新的 输入值 集合。输入控件包括 游戏手柄 的按钮、触发器、操纵杆、拇指摇杆和触摸表面。一个 输出控件 是一种通过改变 游戏手柄 的行为来向用户提供反馈的特性。输出控件包括 游戏手柄 的触觉执行器。如果 用户代理 可以读取其 输入控件 的当前状态,则称该 游戏手柄 是 可用的。不可用的 游戏手柄 被称为 不可用的。在 游戏手柄 可用 期间,其 输入控件 和 输出控件 不能改变。
用户代理 负责以下工作:
游戏手柄 具有一个 游戏手柄标识字符串,这是一个标识 游戏手柄 品牌或样式的易读字符串。其内容由 用户代理 决定。
游戏手柄 可能具有一个 输入控件布局,描述了 游戏手柄 上每个 输入控件 的位置、方向和类型。用户代理 负责识别 游戏手柄 何时 符合标准布局,这意味着该 游戏手柄 具有一种 输入控件布局,使其能够与符合相同标准布局的其他 游戏手柄 互换使用。用户代理 应该 认为如果布局的 输入控件 与标准布局中所述的 输入控件 具有大致相同的位置和方向,则该布局符合标准布局。
用户代理 通常无法直接检查 游戏手柄 的 输入控件布局,并且 可以 使用启发式方法来确定布局。用户代理 在决定 游戏手柄 是否 符合标准布局 时 应该 考虑设备标识符。如果系统为每个 输入控件 分配了标签,且这些标签暗示了特定的布局,则 用户代理 应该 认为该 游戏手柄 具有该布局。当存在标准模型和具有相同 输入控件 的辅助功能模型时,用户代理 应该 认为辅助功能模型具有与标准模型相同的 输入控件布局。
每个输入控件都有一个或多个关联的 输入值,这些是表示控件当前状态的数值。输入值 可以随时更新。用户代理 负责检测 输入值 何时更新,并 应该 尽量减少更新与读取更新后的值之间的延迟。
读取 输入值 会返回其 逻辑值,这是对当前状态的非缩放数值表示。输入值 还具有 逻辑最小值 和 逻辑最大值,它们定义了范围内的最小和最大 逻辑值。
输入值 可能具有关联的 HID 使用标识符,这是一个标识输入值所代表数据类型的 32 位值。HID 使用情况不能精确描述 输入控件布局,但按照惯例,许多具有相似布局的 游戏手柄 使用相似的使用情况。用户代理 在决定 输入控件布局 时 应该 依赖围绕 HID 使用标识符 的惯例。
游戏手柄 可能具有 轴 输入。轴 是一个 输入值,表示控件相对于参考位置的当前位移。
游戏手柄 拥有一个 轴列表,这是一个包含 游戏手柄 的所有 轴 输入的 列表,其顺序由 用户代理 确定。
输入控件 可能设计为在用户停止与 输入控件 交互时自动将 轴 返回到中心位置。如果是这样,则该 轴 具有一个 首选轴状态。具有 首选轴状态 的 轴 可能还具有一个额外的 输入值,即 中心位置值,这是 轴 居中时的 逻辑值。
游戏手柄 可能具有 触摸表面。触摸表面 是一种输入控件,提供表示接触点的 2D 位置数据。游戏手柄 拥有一个 触摸表面列表,这是一个包含 游戏手柄 的 触摸表面 的 列表。列表的排序方式使得越靠近 游戏手柄 左侧的 触摸表面 在列表中出现得越靠前。
触摸表面 具有一个 活动触摸点列表 输入值,这是一份零个或多个 触摸点 的 列表,代表当前传感器检测到的接触点。触摸点 表示在特定时间点的单个接触点。触摸点 具有 触摸 X 坐标 和 触摸 Y 坐标,表示 触摸表面 坐标系中的位置。如果 触摸表面 位于 游戏手柄 的顶部、底部、正面或背面,则 触摸 X 坐标 沿左右轴测量,否则沿顶底轴测量。触摸 Y 坐标 沿垂直轴测量。
如果 触摸表面 位于 游戏手柄 的左侧或右侧,则其任何维度都不会与水平轴对齐。
触摸表面 可能具有表面维度 输入值。表面宽度 和 表面高度 输入值 是 触摸表面 的维度,单位与 触摸 X 坐标 和 触摸 Y 坐标 相同。触摸表面 要么同时具有这两个维度值,要么都不具备。
触摸点 可能是新的接触点,也可能是早期接触的延续。如果 用户代理 识别出它是早期 GamepadTouch 所代表的 触摸点 的延续,则该 触摸点 被认为是 现有活动触摸点的一部分。对于 现有活动触摸点的一部分 的 触摸点,其 活动触摸点 ID 是早期 GamepadTouch 的 touchId。
游戏手柄 可能具有 触觉执行器。触觉执行器 是一种输出控件,能够以用户可以感知的方式移动 游戏手柄。触觉执行器 可用于生成提供用户反馈的 触觉效果。来自多个执行器的振动结合起来生成更复杂的效果。用户代理 负责命令 触觉执行器 在 可用 的 游戏手柄 上播放和停止 触觉效果。
游戏手柄 可能具有一个 振动执行器,这是一个能够播放 触觉效果 来振动整个 游戏手柄 的 触觉执行器。
触觉执行器 拥有一个 支持的效果类型 列表,包含一个或多个 GamepadHapticEffectType 值,这些值在 游戏手柄 可用 期间不能更改。
该接口定义了一个独立的游戏手柄设备。
WebIDL[Exposed=Window]
interface Gamepad {
readonly attribute DOMString id;
readonly attribute long index;
readonly attribute boolean connected;
readonly attribute DOMHighResTimeStamp timestamp;
readonly attribute GamepadMappingType mapping;
readonly attribute FrozenArray<double> axes;
readonly attribute FrozenArray<GamepadButton> buttons;
readonly attribute FrozenArray<GamepadTouch> touches;
[SameObject] readonly attribute GamepadHapticActuator vibrationActuator;
};
用于与系统通信的算法通常异步完成,在 游戏手柄任务源 上排队工作。
Gamepad 的实例在创建时带有下表中描述的内部槽位:
| 内部槽位 | 初始值 | 描述(非规范性) |
|---|---|---|
| [[connected]] |
false
|
指示设备已连接到系统的标志 |
| [[timestamp]] | undefined | 此 Gamepad 的数据最后一次更新的时间 |
| [[axes]] | 一个空 序列 | 表示该设备暴露的轴的当前状态的 double 值序列 |
| [[buttons]] | 一个空 序列 | 表示该设备暴露的按钮的当前状态的 GamepadButton 对象序列 |
| [[exposed]] |
false
|
指示 Gamepad 对象是否已暴露给脚本的标志 |
| [[axisMapping]] | 一个空 有序映射 | 从未映射轴索引到 axes 数组中索引的映射 |
| [[axisMinimums]] | 一个空 列表 | 包含每个轴的逻辑最小值的 列表 |
| [[axisMaximums]] | 一个空 列表 | 一个包含每个轴的逻辑最大值的 列表 |
| [[buttonMapping]] | 一个空 有序映射 | 从未映射按钮索引到 buttons 数组中索引的映射 |
| [[buttonMinimums]] | 一个空 列表 | 包含每个按钮的逻辑最小值的 列表。 |
| [[buttonMaximums]] | 一个空 列表 | 包含每个按钮的逻辑最大值的 列表 |
| [[touches]] | 一个空 列表 | 保存用户生成的触摸列表(如果有)。如果游戏手柄不支持触摸表面,则列表保持为空。 |
| [[nextTouchId]] | 0 |
用于下一个传入触摸的 touchId 值。 |
| [[vibrationActuator]] | undefined | 一个能够生成振动整个游戏手柄的触觉效果的 GamepadHapticActuator 对象 |
id 属性游戏手柄的识别字符串。该字符串标识已连接的游戏手柄设备的品牌或样式。
id 字符串的确切格式未指定。建议 用户代理 选择一个能识别产品但不能唯一识别设备的字符串。例如,USB 游戏手柄可以通过其 idVendor 和 idProduct 值来识别。序列号或蓝牙设备地址等唯一标识符 绝不能 包含在 id 字符串中。
index 属性Navigator 中的索引。当多个游戏手柄连接到 用户代理 时,索引 必须 从零开始按先到先得的原则分配。如果游戏手柄断开连接,之前分配的索引 绝不能 重新分配给持续连接的游戏手柄。然而,如果一个游戏手柄断开连接,随后连接了相同或不同的游戏手柄,则 必须 重用最低的先前使用过的索引。connected 属性指示此对象表示的物理设备是否仍连接到系统。当游戏手柄变得不可用时(无论是物理断开、关机还是以其他方式不可用),connected 属性 必须 设置为 false。
connected 获取器的步骤为:
this.[[connected]]。timestamp 属性timestamp 允许作者确定此游戏手柄的 axes 或 buttons 属性最后一次更新的时间。每当系统 收到来自设备的新按钮或轴输入值 时,该值 必须 设置为 当前高分辨率时间。如果未从硬件接收到任何数据,timestamp 必须 为 Gamepad 首次对脚本可用时的 当前高分辨率时间。
timestamp 获取器的步骤为:
this.[[timestamp]]。mapping 属性该设备正在使用的映射。如果 用户代理 了解设备的布局,则它 应该 通过将 mapping 设置为相应的 GamepadMappingType 值来指示映射正在使用中。
要为游戏手柄设备 选择映射,请执行以下步骤:
axes 属性游戏手柄所有轴的值数组。所有轴值 必须 线性归一化到 [-1 .. 1] 范围。如果控制器垂直于地面且方向摇杆指向向上,-1 应该 对应于“向前”或“左”,1 应该 对应于“向后”或“右”。从 2D 输入设备获取的轴 应该 在轴数组中彼此相邻,X 在前,Y 在后。建议 轴按重要性递减顺序出现,使得元素 0 和 1 通常代表方向摇杆的 X 和 Y 轴。在 用户代理 需要返回不同值(或不同顺序的值)之前,必须 返回相同的对象。
axes 获取器的步骤为:
this.[[axes]]。buttons 属性游戏手柄所有按钮的按钮状态数组。建议 按钮按重要性递减顺序出现,使得主按钮、次要按钮、第三按钮等分别作为按钮数组的元素 0、1、2...。在 用户代理 需要返回不同值(或不同顺序的值)之前,必须 返回相同的对象。
buttons 获取器的步骤为:
this.[[buttons]]。touches 属性由所有触摸表面生成的 GamepadTouch 对象的 列表。
touches 获取器的步骤为:
this.[[touches]]。vibrationActuator 属性表示设备主要振动执行器的 GamepadHapticActuator 对象。
vibrationActuator 获取器的步骤为:
this.[[vibrationActuator]]。当系统 收到新的按钮或轴输入值 时,执行以下步骤:
要为 gamepad 更新游戏手柄状态,执行以下步骤:
[[timestamp]] 设置为 now。Navigator 对象。[[hasGamepadGesture]] 为 false 且 gamepad 包含游戏手柄用户手势:[[hasGamepadGesture]] 设置为 true。[[gamepads]] 中的 每一项 connectedGamepad:null:[[exposed]] 设置为 true。[[timestamp]] 设置为 now。Document;否则为 null。null 且为 完全活跃的,则在 游戏手柄任务源 上 排队一个全局任务,以在 gamepad 的 相关全局对象 上 触发 名为 gamepadconnected 的事件,使用 GamepadEvent,并将其 gamepad 属性初始化为 connectedGamepad。要为 gamepad 映射并归一化轴,执行以下步骤:
[[axisMapping]][rawAxisIndex]。[[axisMinimums]][rawAxisIndex]。[[axisMaximums]][rawAxisIndex]。[[axes]][axisIndex] 设置为 normalizedValue。要为 gamepad 映射并归一化按钮,执行以下步骤:
[[buttonMapping]][rawButtonIndex]。[[buttonMinimums]][rawButtonIndex]。[[buttonMaximums]][rawButtonIndex]。[[buttons]][mappedIndex]。[[value]] 设置为 normalizedValue。如果按钮具有指示纯按下或释放状态的数字开关,则如果按钮被按下,将 button.[[pressed]] 设置为 true,如果未按下则设置为 false。
否则,如果值高于 按钮按下阈值,则将 button.[[pressed]] 设置为 true,否则设置为 false。
如果按钮具备检测触摸的能力,且按钮当前正被触摸,则将 button.[[touched]] 设置为 true。
否则,将 button.[[touched]] 设置为 button.[[pressed]]。
要为 gamepad 记录触摸,执行以下步骤:
Gamepad.[[touches]] 为空。touch.surfaceDimensions 设置为一个 DOMRectReadOnly,其 width 和 height 初始化为触摸表面上设备单位下的最大 X 和 Y 维度。GamepadTouch 对象。surfaceId 设置为 surfaceId。touchId 设置为该活动触摸点的 touchId。touchId 设置为 gamepad.[[nextTouchId]],并递增 gamepad.[[nextTouchId]]。如果游戏手柄有多个触摸表面,触摸 ID 在各表面间将是唯一的。
position 设置为一个 新的 DOMPointReadOnly,其 x 初始化为相对于设备触摸表面的设备 X 坐标并归一化到 [-1 .. 1](-1 为最左侧坐标,1 为最右侧坐标),y 初始化为相对于设备触摸表面并归一化到 [-1 .. 1](-1 为最顶部坐标,1 为最底部坐标)。
x = (2.0 * touchData.x / surfaceDimensions.width) - 1
y = (2.0 * touchData.y / surfaceDimensions.height) - 1
[[touches]]。
代表已连接游戏手柄设备的 新 Gamepad 是通过执行以下步骤构造的:
Gamepad 实例。id 属性初始化为游戏手柄的识别字符串。index 属性初始化为 选择未使用游戏手柄索引 的结果。mapping 属性初始化为 选择映射 的结果。[[connected]] 设置为 true。[[timestamp]] 设置为 gamepad 的 相关全局对象 的 当前高分辨率时间。[[axes]] 设置为 初始化轴 的结果。[[buttons]] 设置为 初始化按钮 的结果。[[vibrationActuator]] 设置为 构造 GamepadHapticActuator 的结果。要为 gamepad 选择一个未使用的游戏手柄索引,执行以下步骤:
Navigator 对象。[[gamepads]] 的 大小 - 1。[[gamepads]][gamepadIndex] 为 null,则返回 gamepadIndex。null 到 navigator.[[gamepads]]。[[gamepads]] 的 大小 - 1。要为 gamepad 初始化轴,执行以下步骤:
[[axisMinimums]] 设置为一个 列表,其 大小 等于 inputCount,包含每个轴输入的逻辑最小值。[[axisMaximums]] 设置为一个 列表,其 大小 等于 inputCount,包含每个轴输入的逻辑最大值。否则
[[axisMapping]][rawInputIndex] 设置为 canonicalIndex。否则,追加 rawInputIndex 到 unmappedInputList。
[[axisMapping]][rawInputIndex] 设置为 axisIndex。要为 gamepad 初始化按钮,请执行以下步骤
[[buttonMinimums]] 设置为一个 列表,包含大小等于 inputCount 的 unsigned long 值,其中包含每个按钮输入的最小逻辑值。[[buttonMaximums]] 设置为一个 列表,包含大小等于 inputCount 的 unsigned long 值,其中包含每个按钮输入的最大逻辑值。否则
[[buttonMapping]][rawInputIndex] 设置为 canonicalIndex。否则,追加 rawInputIndex 到 unmappedInputList。
[[buttonMapping]][rawInputIndex] 设置为 buttonIndex。GamepadButton 到 buttons。此接口定义了支持此类输入的游戏手柄触摸表面上的触摸。该对象包含一个 touchId,它从输入介质(例如手指、触控笔等)接触触摸设备那一刻起,直到输入介质不再与触摸设备接触为止,唯一地标识该触摸点。
WebIDLdictionary GamepadTouch {
unsigned long touchId;
octet surfaceId;
DOMPointReadOnly position;
DOMRectReadOnly? surfaceDimensions;
};
touchId 属性surfaceId 属性position 属性DOMPointReadOnly,它保存了触摸的 x, y 坐标。z 和 w 值当前未使用。每个坐标的范围归一化为 [-1 .. 1]。沿 x 轴,-1 指最左侧坐标,1 指最右侧坐标。沿 y 轴,-1 指最顶部坐标,1 指最底部坐标。surfaceDimensions 属性width 和 height 初始化的 DOMRectReadOnly。如果不可用,则为 null。此枚举定义了游戏手柄的一组已知映射。
WebIDLenum GamepadMappingType {
"",
"standard",
"xr-standard",
};
""
standard"xr-standard"getGamepads() 返回的 Gamepad 对象 绝不 报告 "xr-standard" 的 mapping。GamepadHapticActuator 对应于可施加力以实现触觉反馈的电机或其他执行器的配置。
WebIDL[Exposed=Window]
interface GamepadHapticActuator {
[SameObject] readonly attribute FrozenArray<GamepadHapticEffectType> effects;
Promise<GamepadHapticsResult> playEffect(
GamepadHapticEffectType type,
optional GamepadEffectParameters params = {}
);
Promise<GamepadHapticsResult> reset();
};
GamepadHapticActuator 实例创建时带有下表中描述的内部槽位
| 内部槽位 | 初始值 | 描述 |
|---|---|---|
| [[effects]] | 一个空的 GamepadHapticEffectType 列表。 |
表示执行器支持的效果。 |
| [[playingEffectPromise]] |
null
|
播放某个效果的 Promise,如果没有效果正在播放,则为 null。 |
effects 属性表示执行器支持的所有触觉效果类型的 GamepadHapticEffectType 值数组。此属性列出了执行器支持的 GamepadHapticEffectType 值,除非 用户代理 不支持播放该类型的效果。
effects 获取器步骤为
[[effects]]。playEffect() 方法调用 playEffect() 方法的步骤,使用 GamepadHapticEffectType type 和 GamepadEffectParameters params,为
TypeError。Document。null 或 document 不是 完全激活的,或者 document 的 可见性状态 为 "hidden",则返回 一个被拒绝的 promise,并附带 "InvalidStateError" DOMException。[[playingEffectPromise]] 不为 null[[playingEffectPromise]]。[[playingEffectPromise]] 设置为 null。preempted" 解决 (resolve) effectPromise。GamepadHapticActuator 不能 播放 type 类型的效果,则返回 一个被拒绝的 promise,理由为 NotSupportedError。[[playingEffectPromise]] 为 一个新的 promise。[[playingEffectPromise]] 不为 null,则在 游戏手柄任务源上,使用 此 对象的 相关全局对象,排入一个全局任务,以运行以下步骤[[playingEffectPromise]] 为 null,则中止这些步骤。[[playingEffectPromise]],使用 "complete"。[[playingEffectPromise]] 设置为 null。[[playingEffectPromise]]。reset() 方法reset() 方法的步骤为
Document。null 或 document 不是 完全激活的,或者 document 的 可见性状态 为 "hidden",则返回 一个被拒绝的 promise,并附带 "InvalidStateError" DOMException。[[playingEffectPromise]] 不为 null,则并行执行以下步骤[[playingEffectPromise]]。[[playingEffectPromise]] 仍然相同,则将 此.[[playingEffectPromise]] 设置为 null。preempted" 解决 (resolve) effectPromise。complete"如果 type 可以在 [[effects]] 列表中找到,则 GamepadHapticActuator 可以 播放 type 类型的效果。
要检查 GamepadHapticEffectType type 和 GamepadEffectParameters params 是否描述了一个 有效效果,请执行以下步骤
GamepadHapticEffectType type 的值,切换dual-rumble"false。trigger-rumble"false。true要对执行器 发布触觉效果,用户代理 必须 向设备发送命令以呈现 type 类型的效果,并尝试使用提供的 params。当 params.startDelay 不为 0.0 时,用户代理 应该 使用提供的 playEffectTimestamp 以获得更精确的播放时序。用户代理 可以 修改效果以增加兼容性。例如,为震动电机设计的效果可以转换为支持波形触觉但缺少震动电机的设备的波形效果。
要对执行器 停止触觉效果,用户代理 必须 向设备发送命令以中止当前正在播放的任何效果。如果触觉效果被中断,执行器 应该 尽可能快地恢复到静止状态。
当 document 的 可见性状态 变为 "hidden" 时,对每个 GamepadHapticActuator actuator 执行这些步骤
[[playingEffectPromise]] 为 null,则中止这些步骤。[[playingEffectPromise]] 为 null,则中止这些步骤。[[playingEffectPromise]],使用 "preempted"。[[playingEffectPromise]] 设置为 null。
一个新的 gamepadHapticActuator 代表一个 Gamepad 的主振动执行器,通过执行以下步骤构建
GamepadHapticActuator 实例。supportedEffectsList 为一个空 列表。GamepadHapticEffectType 的每个枚举值 type,如果 用户代理 可以发送命令在该执行器上启动该类型的效果,则将 type 追加到 supportedEffectsList。[[effects]] 设置为 supportedEffectsList。WebIDLenum GamepadHapticsResult {
"complete",
"preempted"
};
complete
触觉效果播放完成。
preempted
当前效果被另一个效果停止或替换(即“抢占”)。
效果类型定义了执行器如何解释效果参数。
WebIDLenum GamepadHapticEffectType {
"dual-rumble",
"trigger-rumble"
};
dual-rumble" 效果类型"dual-rumble" 描述了一种触觉配置,在标准游戏手柄的每个手柄中都有一个偏心旋转质量 (ERM) 振动电机。在这种配置中,任一电机都能使整个游戏手柄振动。每个电机产生的振动效果是不等的,因此可以将它们结合起来以产生更复杂的触觉效果。
"dual-rumble" 效果是一种针对此类执行器的固定持续时间、恒定强度的振动效果。"dual-rumble" 效果由 startDelay、duration、strongMagnitude 和 weakMagnitude 定义,这些都不是必需的,因为它们默认为 0。
strongMagnitude 和 weakMagnitude 设置低频和高频振动的强度级别,归一化到 [0 .. 1] 范围,默认值为 0。
给定 GamepadEffectParameters params,一个 有效的双重震动效果 必须具有 有效的 duration,有效的 startDelay,并且 strongMagnitude 和 weakMagnitude 都必须在 [0 .. 1] 范围内。
trigger-rumble" 效果类型"trigger-rumble" 描述了一种触觉配置,在 标准游戏手柄 的每个底部前部按钮(规范索引 6 和 7 的按钮)中都有一个振动电机,此外还有用于 "dual-rumble" 的两个手柄电机。这些按钮通常采用弹簧触发器的形式。在这种配置中,任一电机都能在按钮表面提供局部的触觉反馈。
"trigger-rumble" 效果是一种针对此类执行器的固定持续时间、恒定强度的振动效果。"trigger-rumble" 效果由 startDelay、duration、strongMagnitude、weakMagnitude、leftTrigger 和 rightTrigger 定义,这些都不是必需的,因为它们默认为 0。
startDelay, duration, strongMagnitude, weakMagnitude 与 "dual-rumble" 具有相同的定义。leftTrigger 和 rightTrigger 分别设置左侧和右侧底部前部按钮振动的强度级别,归一化到 [0 .. 1] 范围,默认值为 0。
给定 GamepadEffectParameters params,一个 有效的触发器震动效果 必须具有 有效的 duration,有效的 startDelay,并且 strongMagnitude, weakMagnitude, leftTrigger, 和 rightTrigger 必须都在 [0 .. 1] 范围内。
GamepadEffectParameters 字典包含触觉效果所使用的参数键。每个键的含义由触觉效果定义,某些键可能未使用。
为了减少不必要的长时间运行效果,用户代理 可以 将 有效效果 的总持续时间限制为某个最大持续时间。用户代理 建议 使用最长 5 秒的时间。
WebIDLdictionary GamepadEffectParameters {
unsigned long long duration = 0;
unsigned long long startDelay = 0;
double strongMagnitude = 0.0;
double weakMagnitude = 0.0;
double leftTrigger = 0.0;
double rightTrigger = 0.0;
};
duration 成员duration 设置振动效果的持续时间(以毫秒为单位)。startDelay 成员startDelay 设置从调用 playEffect() 到振动开始之间的延迟持续时间(以毫秒为单位)。在延迟间隔期间,执行器 不应该 振动。strongMagnitude 成员dual-rumble" 或 "trigger-rumble" 效果中低频震动的振动幅度。weakMagnitude 成员dual-rumble" 或 "trigger-rumble" 效果中高频震动的振动幅度。leftTrigger 成员trigger-rumble" 效果中左下前按钮(规范索引 6)震动的振动幅度。rightTrigger 成员trigger-rumble" 效果中右下前按钮(规范索引 7)震动的振动幅度。WebIDL[Exposed=Window]
interface GamepadEvent: Event {
constructor(DOMString type, GamepadEventInit eventInitDict);
[SameObject] readonly attribute Gamepad gamepad;
};
gamepad 属性gamepad 属性提供对与此事件关联的游戏手柄数据的访问。WebIDLdictionary GamepadEventInit : EventInit {
required Gamepad gamepad;
};
gamepad 成员Gamepad。每个设备制造商都创造了许多不同的产品,每种产品都有独特的按钮和轴样式及布局。预期 用户代理 将尽可能多地支持这些产品。
此外,还有游戏机普及的事实标准布局。当 用户代理 识别出连接的设备时,建议 在可能的情况下将其重新映射为规范顺序。未识别的设备仍应以其原始形式暴露。
目前有一个规范布局,即 标准游戏手柄。重新映射时,axes 和 buttons 中的索引应尽可能紧密地对应于下图中的物理位置。此外,mapping 应该 设置为 "standard"。
标准游戏手柄 按钮布局包括左侧四个按钮的簇、右侧四个按钮的簇、中心三个按钮的簇,以及游戏手柄左右两侧的一对前置按钮。"标准游戏手柄"的四个轴与一对模拟摇杆相关联,一个在左,一个在右。下表描述了按钮/轴及其物理位置。
如果轴输入报告了拇指摇杆轴的输入值,该拇指摇杆位于与对应 标准游戏手柄 拇指摇杆大致相同的位置,并且轴的方向(上下或左右)与 标准游戏手柄 轴的方向匹配,则该轴输入 代表一个标准游戏手柄轴。如果有多个轴代表同一个 标准游戏手柄 轴,则 用户代理 应该 选择一个作为 标准游戏手柄 轴,并为另一个轴分配不同的索引。
如果按钮输入报告了按钮或触发器的输入值,且该按钮或触发器位于与对应 标准游戏手柄 按钮大致相同的位置,则该按钮输入 代表一个标准游戏手柄按钮。
如果轴或按钮输入代表 标准游戏手柄 轴或按钮,则其 规范索引 是对应 标准游戏手柄 轴或按钮的索引。
| 类型 | 索引 | 位置 (Location) |
|---|---|---|
| Button | 0 | 右侧簇中的底部按钮 |
| 1 | 右侧簇中的右侧按钮 | |
| 2 | 右侧簇中的左侧按钮 | |
| 3 | 右侧簇中的顶部按钮 | |
| 4 | 左上前按钮 | |
| 5 | 右上前按钮 | |
| 6 | 左下前按钮 | |
| 7 | 右下前按钮 | |
| 8 | 中心簇中的左侧按钮 | |
| 9 | 中心簇中的右侧按钮 | |
| 10 | 左摇杆按下按钮 | |
| 11 | 右摇杆按下按钮 | |
| 12 | 左侧簇中的顶部按钮 | |
| 13 | 左侧簇中的底部按钮 | |
| 14 | 左侧簇中的左侧按钮 | |
| 15 | 左侧簇中的右侧按钮 | |
| 16 | 中心簇中的中心按钮 | |
| axes | 0 | 左摇杆水平轴(负向为左/正向为右) |
| 1 | 左摇杆垂直轴(负向为上/正向为下) | |
| 2 | 右摇杆水平轴(负向为左/正向为右) | |
| 3 | 右摇杆垂直轴(负向为上/正向为下) |
检查 Gamepad 对象的能力可能会被用作主动指纹识别的一种手段。用户代理(user agent)可以更改通过 API 暴露的设备信息,以减少指纹识别面。例如,实现可以要求 Gamepad 对象拥有在 标准手柄 (Standard Gamepad) 布局中定义的按钮和轴的精确数量,即使所连接的设备实际存在更多或更少的输入。 [FINGERPRINTING-GUIDANCE]
本节是非规范性的。
下面的示例演示了访问手柄的典型方式。请注意与 requestAnimationFrame() 方法的关系。
function runAnimation() {
window.requestAnimationFrame(runAnimation);
for (const pad of navigator.getGamepads()) {
// todo; simple demo of displaying pad.axes and pad.buttons
console.log(pad);
}
}
window.requestAnimationFrame(runAnimation);
requestAnimationFrame() 的协调交互式应用程序通常会使用 requestAnimationFrame() 方法来驱动动画,并希望将动画与用户手柄输入进行协调。因此,应尽可能在执行动画回调之前立即轮询手柄数据,且频率需与动画频率匹配。也就是说,如果动画回调以 60Hz 运行,则手柄输入也应以该速率进行采样。
当系统中有手柄可用时,执行以下步骤:
Document;否则为 null。null 且 不允许使用 "gamepad" 权限,则中止这些步骤。Gamepad。Navigator 对象。[[gamepads]][gamepad.index] 设置为 gamepad。[[hasGamepadGesture]] 为 true[[exposed]] 设置为 true。null 且 完全活跃,则在 gamepad 的 相关全局对象 上,使用其 gamepad 属性初始化为 gamepad 的 GamepadEvent,触发一个名为 gamepadconnected 的事件。
实现本规范的 用户代理 必须提供一个名为 gamepadconnected 的新 DOM 事件。相应的事件 必须 为 GamepadEvent 类型,并 必须 在 Window 对象上触发。
当用户连接手柄时,用户代理 必须 分发此事件类型以作指示。如果页面加载时手柄已连接,则 应该 在用户按下按钮或移动轴时分发 gamepadconnected 事件。
当系统中有手柄变为不可用时,执行以下步骤:
Gamepad。[[connected]] 设置为 false。Document;否则为 null。[[exposed]] 为 true,且 document 不为 null 且 完全活跃,则在 gamepad 的 相关全局对象 上,使用其 gamepad 属性初始化为 gamepad 的 GamepadEvent,触发一个名为 gamepaddisconnected 的事件。Navigator 对象。[[gamepads]][gamepad.index] 设置为 null。[[gamepads]] 不为空 且 navigator.[[gamepads]] 的最后一个 项 为 null 时,移除 navigator.[[gamepads]] 的最后一个 项。
实现本规范的 用户代理 必须提供一个名为 gamepaddisconnected 的新 DOM 事件。相应的事件 必须 为 GamepadEvent 类型,并 必须 在 Window 对象上触发。
当手柄与 用户代理 断开连接时,如果 用户代理 此前已向某个 Window 分发过该手柄的 gamepadconnected 事件,则 必须 向同一个 Window 分发一个 gamepaddisconnected 事件。
仍需讨论是否包含或排除轴和按钮变更事件,以及是否将它们合并("gamepadchanged"?)、稍微分离("gamepadaxischanged"?)或者按单个轴和按钮进行分离。
本规范扩展了来自 HTML 的 WindowEventHandlers 接口混合,以添加 事件处理程序 IDL 属性,从而促进事件处理程序的注册。
WebIDLpartial interface mixin WindowEventHandlers {
attribute EventHandler ongamepadconnected;
attribute EventHandler ongamepaddisconnected;
};
本规范定义了一个由策略控制的功能,标识为字符串 "gamepad"。其 默认允许列表 为 *。
文档 的 权限策略 决定了该文档中的任何内容是否被允许访问 getGamepads()。如果在任何文档中被禁用,则该文档中的内容将 不允许使用 getGamepads(),也不会触发 gamepadconnected 和 gamepaddisconnected 事件。
除了标记为非规范性的章节外,本规范中的所有创作指南、图表、示例和注释均为非规范性内容。本规范中的其他所有内容均为规范性内容。
本文档中的关键词 MAY(可以)、MUST(必须)、MUST NOT(不得)、RECOMMENDED(推荐)、SHOULD(应该)和 SHOULD NOT(不应该)仅在它们以全大写形式出现时,按 BCP 14 [RFC2119] [RFC8174] 中的描述进行解释,如此处所示。
本节是非规范性的。
以下人员为本文件的制定做出了贡献。
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自