From 074785cea106179cb3305637055ab0a009ca74f2 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:42:52 -0500 Subject: initial commit --- files/ru/web/api/gamepad_api/index.html | 99 ++++++ .../gamepad_api/using_the_gamepad_api/index.html | 341 +++++++++++++++++++++ 2 files changed, 440 insertions(+) create mode 100644 files/ru/web/api/gamepad_api/index.html create mode 100644 files/ru/web/api/gamepad_api/using_the_gamepad_api/index.html (limited to 'files/ru/web/api/gamepad_api') diff --git a/files/ru/web/api/gamepad_api/index.html b/files/ru/web/api/gamepad_api/index.html new file mode 100644 index 0000000000..53ecea9f2b --- /dev/null +++ b/files/ru/web/api/gamepad_api/index.html @@ -0,0 +1,99 @@ +--- +title: Gamepad API +slug: Web/API/Gamepad_API +tags: + - API + - Gamepad API + - Игры + - Обзор + - Экспериментальный +translation_of: Web/API/Gamepad_API +--- +
{{DefaultAPISidebar("Gamepad API")}}
+ +

API Gamepad - это простой и последовательный способ для разработчиков получать доступ к сигналам с геймпадов и других игровых контроллеров и реагировать на них. Он содержит три интерфейса, два события и одну специализированную функцию, чтобы реагировать на подключение и отключение геймпадов, а также получать доступ к другой информации о самих геймпадах и о том, какие кнопки и другие элементы управления в настоящее время нажимаются.

+ +

Интерфейсы

+ +
+
Gamepad
+
Представляет собой геймпад/контроллер, подключенный к компьютеру.
+
GamepadButton
+
Представлят собой кнопку одного из подлюченных контроллеров
+
GamepadEvent
+
Объект события, представляющий запущенные события, связанные с геймпадами.
+
+ +

Экспериментальные расширения геймпада

+ +
+
GamepadHapticActuator
+
Представляет собой аппаратное обеспечение в контроллере, предназначенное для обеспечения тактильной обратной связи с пользователем (если таковая имеется), чаще всего вибрационное оборудование.
+
GamepadPose
+
Представляет собой позу контроллера (например, положение и ориентацию в трехмерном пространстве) в случае контроллера WebVR. Это не используется в более новом стандарте WebXR.
+
+ +

Сморите также extensions to the Gamepad interface (расширения интерфейса геймпада) для функций, которые позволяют получить доступ к вышеуказанной информации.

+ +

Расширения для других интерфейсов

+ +

Навигатор

+ +
+
{{domxref("Navigator.getGamepads()")}}
+
Расширение объекта {{domxref("Navigator")}}, возвращающее масссив объектов {{domxref("Gamepad")}} по одному для каждого подключенного геймпада.
+
+ +

События окна

+ +
+
{{domxref("Window.ongamepadconnected")}}
+
Представляет собой обработчик событий, который будет выполняться при подключении геймпада (когда срабатывает событие {{event('gamepadconnected')}}).
+
{{domxref("Window.ongamepaddisconnected")}}
+
Представляет собой обработчик событий, который будет выполняться при отключении геймпада (когда срабатывает событие {{event('gamepaddisconnected')}}).
+
+ +
+
+ +

Учебные пособия и руководства

+ + + +

Технические характеристики

+ + + + + + + + + + + + + + + + + + + +
СпецификацияСтатусПримечание
{{SpecName("GamepadExtensions")}}{{Spec2("GamepadExtensions")}}Определяет {{anch("Experimental Gamepad extensions")}}.
{{SpecName("Gamepad", "", "The Gamepad API specification")}}{{Spec2("Gamepad")}}Первононачальное определение
+ +

Совместимость браузера

+ + + +

{{Compat("api.Gamepad")}}

+ +

Прочтите также

+ + diff --git a/files/ru/web/api/gamepad_api/using_the_gamepad_api/index.html b/files/ru/web/api/gamepad_api/using_the_gamepad_api/index.html new file mode 100644 index 0000000000..6dea1ea38c --- /dev/null +++ b/files/ru/web/api/gamepad_api/using_the_gamepad_api/index.html @@ -0,0 +1,341 @@ +--- +title: Using the Gamepad API +slug: Web/API/Gamepad_API/Using_the_Gamepad_API +translation_of: Web/API/Gamepad_API/Using_the_Gamepad_API +--- +

{{DefaultAPISidebar("Gamepad API")}}

+ +

HTML5 представляет большое количесво необходимых компонентов для полной и интерактивной разработки игр. Такие технологии, как <canvas>, WebGL, <audio>, и <video>, вместе с Javascript сейчас поддерживают большое количество задач, для которых раньше требовались нативные программы.  Gamepad API - это способ полученния данных с геймпада и других игровых контроллеров. 

+ +

Gamepad API добавляет в  объект {{ domxref("Window") }}  новые события для получения событий контроллера. Дополнительно к этим событиям, API также добавляет объект {{ domxref("Gamepad") }}, который позволяет получить  состояние подключенного контроллера, и метод {{ domxref("navigator.getGamepads()") }} который позволяет получить все контроллеры, определенные на странице браузера.

+ +

Подключение геймпада

+ +

Когда новый  геймпад подключается, на странице страбатывают события  {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }}. Если геймпад уже был подключен к моменту загрузки страницы, события  {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} сработают, когда пользователь нажмет на любую кнопку или передвинет стики. 

+ +
+

В Firefox геймпад определяется только тогда, когда пользователь взаимодействует с ним, и при этом страница видна и в фокусе. Это помогает предотвратить использование геймпадов для идентификации пользователя. После взаимодействия с одним геймпадом другие подключенные геймпады будут автоматически видны.

+
+ +

Вы можете использовать  {{domxref("Window/gamepadconnected_event", "gamepadconnected")}} как в примере:

+ +
window.addEventListener("gamepadconnected", function(e) {
+  console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
+    e.gamepad.index, e.gamepad.id,
+    e.gamepad.buttons.length, e.gamepad.axes.length);
+});
+
+ +

Каждый геймпад имеет уникальный ID, который доступен в свойстве {{domxref("GamepadEvent.gamepad", "gamepad")}}.

+ +

Отключение геймпада

+ +

When a gamepad is disconnected, and if a page has previously received data for that gamepad (e.g. {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }}), a second event is dispatched to the focused window, {{ event("gamepaddisconnected") }}:

+ +
window.addEventListener("gamepaddisconnected", function(e) {
+  console.log("Gamepad disconnected from index %d: %s",
+    e.gamepad.index, e.gamepad.id);
+});
+ +

The gamepad's {{domxref("Gamepad.index", "index")}} property will be unique per-device connected to the system, even if multiple controllers of the same type are used. The index property also functions as the index into the {{jsxref("Array")}} returned by {{ domxref("Navigator.getGamepads()") }}.

+ +
var gamepads = {};
+
+function gamepadHandler(event, connecting) {
+  var gamepad = event.gamepad;
+  // Note:
+  // gamepad === navigator.getGamepads()[gamepad.index]
+
+  if (connecting) {
+    gamepads[gamepad.index] = gamepad;
+  } else {
+    delete gamepads[gamepad.index];
+  }
+}
+
+window.addEventListener("gamepadconnected", function(e) { gamepadHandler(e, true); }, false);
+window.addEventListener("gamepaddisconnected", function(e) { gamepadHandler(e, false); }, false);
+
+ +

This previous example also demonstrates how the gamepad property can be held after the event has completed — a technique we will use for device state querying later.

+ +

Querying the Gamepad object

+ +

As you can see, the gamepad events discussed above include a gamepad property on the event object, which returns a {{ domxref("Gamepad") }} object. We can use this in order to determine which gamepad (i.e., its ID) had caused the event, since multiple gamepads might be connected at once. We can do much more with the {{ domxref("Gamepad") }} object, including holding a reference to it and querying it to find out which buttons and axes are being pressed at any one time. Doing so is often desirable for games or other interactive web pages that need to know the state of a gamepad now vs. the next time an event fires.

+ +

Performing such checks tends to involve using the {{ domxref("Gamepad") }} object in conjunction with an animation loop (e.g., {{ domxref("Window.requestAnimationFrame","requestAnimationFrame") }}), where developers want to make decisions for the current frame based on the state of the gamepad or gamepads.

+ +

The {{domxref("Navigator.getGamepads()")}} method returns an array of all devices currently visible to the webpage, as {{ domxref("Gamepad") }} objects (the first value is always null, so null will be returned if there are no gamepads connected.) This can then be used to get the same information. For example, the first code example above you be rewritten as shown below:

+ +
window.addEventListener("gamepadconnected", function(e) {
+  var gp = navigator.getGamepads()[e.gamepad.index];
+  console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
+    gp.index, gp.id,
+    gp.buttons.length, gp.axes.length);
+});
+ +

The {{ domxref("Gamepad") }} object's properties are as follows:

+ + + +
+

Note: The Gamepad object is available on the {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} event rather than the {{ domxref("Window") }} object itself, for security reasons. Once we have a reference to it, we can query its properties for information about the current state of the gamepad. Behind the scenes, this object will be updated every time the gamepad's state changes.

+
+ +

Using button information

+ +

Let's look at a simple example that displays connection information for one gamepad (it ignores subsequent gamepad connections) and allows you to move a ball around the screen using the four gamepad buttons on the right hand side of the gamepad. You can view the demo live, and find the source code on Github.

+ +

To start with, we declare some variables: The gamepadInfo paragraph that the connection info is written into, the ball that we want to move, the start variable that acts as the ID for requestAnimation Frame, the a and b variables that act as position modifiers for moving the ball, and the shorthand variables that will be used for the {{ domxref("Window.requestAnimationFrame", "requestAnimationFrame()") }} and {{ domxref("Window.cancelAnimationFrame", "cancelAnimationFrame()") }} cross browser forks.

+ +
var gamepadInfo = document.getElementById("gamepad-info");
+var ball = document.getElementById("ball");
+var start;
+var a = 0;
+var b = 0;
+
+ +

Next we use the {{domxref("Window/gamepadconnected_event", "gamepadconnected")}} event to check for a gamepad being connected. When one is connected, we grab the gamepad using {{ domxref("Navigator.getGamepads()") }}[0], print information about the gamepad into our gamepad info div, and fire the gameLoop() function that starts the whole ball movement process up.

+ +
window.addEventListener("gamepadconnected", function(e) {
+  var gp = navigator.getGamepads()[e.gamepad.index];
+  gamepadInfo.innerHTML = "Gamepad connected at index " + gp.index + ": " + gp.id + ". It has " + gp.buttons.length + " buttons and " + gp.axes.length + " axes.";
+
+  gameLoop();
+});
+ +

Now we use the {{domxref("Window/gamepaddisconnected_event", "gamepaddisconnected")}} event to check if the gamepad is disconnected again. If so, we stop the {{DOMxRef("Window.requestAnimationFrame", "requestAnimationFrame()")}} loop (see below) and revert the gamepad information back to what it was originally.

+ +
window.addEventListener("gamepaddisconnected", function(e) {
+  gamepadInfo.innerHTML = "Waiting for gamepad.";
+
+  cancelRequestAnimationFrame(start);
+});
+ +

Chrome does things differently here. Instead of constantly storing the gamepad's latest state in a variable it only stores a snapshot, so to do the same thing in Chrome you have to keep polling it and then only use the {{ domxref("Gamepad") }} object in code when it is available. We have done this below using {{ domxref("Window.setInterval()") }}; once the object is available the gamepad info is outputted, the game loop is started, and the interval is cleared using {{ domxref("Window.clearInterval()") }}. Note that in older versions of Chrome {{ domxref("Navigator.getGamepads()") }} is implemented with a webkit prefix. We attempt to detect and handle both the prefixed version and the standard version of the function for backwards compatibility.

+ +
var interval;
+
+if (!('ongamepadconnected' in window)) {
+  // No gamepad events available, poll instead.
+  interval = setInterval(pollGamepads, 500);
+}
+
+function pollGamepads() {
+  var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
+  for (var i = 0; i < gamepads.length; i++) {
+    var gp = gamepads[i];
+    if (gp) {
+      gamepadInfo.innerHTML = "Gamepad connected at index " + gp.index + ": " + gp.id +
+        ". It has " + gp.buttons.length + " buttons and " + gp.axes.length + " axes.";
+      gameLoop();
+      clearInterval(interval);
+    }
+  }
+}
+ +

Now on to the main game loop. In each execution of the loop we check if one of four buttons is being pressed; if so, we update the values of the a and b movement variables appropriately, then update the {{ cssxref("left") }} and {{ cssxref("top") }} properties, changing their values to the current values of a and b respectively. This has the effect of moving the ball around the screen.  In current versions of Chrome (version 34 as of this writing) the button values are stored as an array of double values, instead of {{ domxref("GamepadButton") }} objects. This is fixed in development versions.

+ +

After all this is done, we use our requestAnimationFrame() to request the next animation frame, running gameLoop() again.

+ +
function buttonPressed(b) {
+  if (typeof(b) == "object") {
+    return b.pressed;
+  }
+  return b == 1.0;
+}
+
+function gameLoop() {
+  var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
+  if (!gamepads) {
+    return;
+  }
+
+  var gp = gamepads[0];
+  if (buttonPressed(gp.buttons[0])) {
+    b--;
+  } else if (buttonPressed(gp.buttons[2])) {
+    b++;
+  }
+  if (buttonPressed(gp.buttons[1])) {
+    a++;
+  } else if (buttonPressed(gp.buttons[3])) {
+    a--;
+  }
+
+  ball.style.left = a * 2 + "px";
+  ball.style.top = b * 2 + "px";
+
+  start = requestAnimationFrame(gameLoop);
+}
+ +

Complete example: Displaying gamepad state

+ +

This example shows how to use the {{ domxref("Gamepad") }} object, as well as the {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} and {{domxref("Window/gamepaddisconnected_event", "gamepaddisconnected")}} events in order to display the state of all gamepads connected to the system. You can find a working demo and look at the full source code on Github.

+ +
var haveEvents = 'ongamepadconnected' in window;
+var controllers = {};
+
+function connecthandler(e) {
+  addgamepad(e.gamepad);
+}
+
+function addgamepad(gamepad) {
+  controllers[gamepad.index] = gamepad;
+
+  var d = document.createElement("div");
+  d.setAttribute("id", "controller" + gamepad.index);
+
+  var t = document.createElement("h1");
+  t.appendChild(document.createTextNode("gamepad: " + gamepad.id));
+  d.appendChild(t);
+
+  var b = document.createElement("div");
+  b.className = "buttons";
+  for (var i = 0; i < gamepad.buttons.length; i++) {
+    var e = document.createElement("span");
+    e.className = "button";
+    //e.id = "b" + i;
+    e.innerHTML = i;
+    b.appendChild(e);
+  }
+
+  d.appendChild(b);
+
+  var a = document.createElement("div");
+  a.className = "axes";
+
+  for (var i = 0; i < gamepad.axes.length; i++) {
+    var p = document.createElement("progress");
+    p.className = "axis";
+    //p.id = "a" + i;
+    p.setAttribute("max", "2");
+    p.setAttribute("value", "1");
+    p.innerHTML = i;
+    a.appendChild(p);
+  }
+
+  d.appendChild(a);
+
+  // See https://github.com/luser/gamepadtest/blob/master/index.html
+  var start = document.getElementById("start");
+  if (start) {
+    start.style.display = "none";
+  }
+
+  document.body.appendChild(d);
+  requestAnimationFrame(updateStatus);
+}
+
+function disconnecthandler(e) {
+  removegamepad(e.gamepad);
+}
+
+function removegamepad(gamepad) {
+  var d = document.getElementById("controller" + gamepad.index);
+  document.body.removeChild(d);
+  delete controllers[gamepad.index];
+}
+
+function updateStatus() {
+  if (!haveEvents) {
+    scangamepads();
+  }
+
+  var i = 0;
+  var j;
+
+  for (j in controllers) {
+    var controller = controllers[j];
+    var d = document.getElementById("controller" + j);
+    var buttons = d.getElementsByClassName("button");
+
+    for (i = 0; i < controller.buttons.length; i++) {
+      var b = buttons[i];
+      var val = controller.buttons[i];
+      var pressed = val == 1.0;
+      if (typeof(val) == "object") {
+        pressed = val.pressed;
+        val = val.value;
+      }
+
+      var pct = Math.round(val * 100) + "%";
+      b.style.backgroundSize = pct + " " + pct;
+
+      if (pressed) {
+        b.className = "button pressed";
+      } else {
+        b.className = "button";
+      }
+    }
+
+    var axes = d.getElementsByClassName("axis");
+    for (i = 0; i < controller.axes.length; i++) {
+      var a = axes[i];
+      a.innerHTML = i + ": " + controller.axes[i].toFixed(4);
+      a.setAttribute("value", controller.axes[i] + 1);
+    }
+  }
+
+  requestAnimationFrame(updateStatus);
+}
+
+function scangamepads() {
+  var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
+  for (var i = 0; i < gamepads.length; i++) {
+    if (gamepads[i]) {
+      if (gamepads[i].index in controllers) {
+        controllers[gamepads[i].index] = gamepads[i];
+      } else {
+        addgamepad(gamepads[i]);
+      }
+    }
+  }
+}
+
+window.addEventListener("gamepadconnected", connecthandler);
+window.addEventListener("gamepaddisconnected", disconnecthandler);
+
+if (!haveEvents) {
+ setInterval(scangamepads, 500);
+}
+ +

Specifications

+ + + + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName("Gamepad", "#gamepad-interface", "Gamepad")}}{{Spec2("Gamepad")}}Initial defintion
+ +

Browser compatibility

+ + + +

{{Compat("api.Gamepad")}}

-- cgit v1.2.3-54-g00ecf