diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:52 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:52 -0500 |
commit | 074785cea106179cb3305637055ab0a009ca74f2 (patch) | |
tree | e6ae371cccd642aa2b67f39752a2cdf1fd4eb040 /files/ru/games/techniques/controls_gamepad_api/index.html | |
parent | da78a9e329e272dedb2400b79a3bdeebff387d47 (diff) | |
download | translated-content-074785cea106179cb3305637055ab0a009ca74f2.tar.gz translated-content-074785cea106179cb3305637055ab0a009ca74f2.tar.bz2 translated-content-074785cea106179cb3305637055ab0a009ca74f2.zip |
initial commit
Diffstat (limited to 'files/ru/games/techniques/controls_gamepad_api/index.html')
-rw-r--r-- | files/ru/games/techniques/controls_gamepad_api/index.html | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/files/ru/games/techniques/controls_gamepad_api/index.html b/files/ru/games/techniques/controls_gamepad_api/index.html new file mode 100644 index 0000000000..edbb3e0a40 --- /dev/null +++ b/files/ru/games/techniques/controls_gamepad_api/index.html @@ -0,0 +1,260 @@ +--- +title: Реализация элементов управления с помощью API Gamepad +slug: Games/Techniques/Controls_Gamepad_API +tags: + - Gamepad API + - JavaScript + - Игры + - геймпады + - контроллеры +translation_of: Games/Techniques/Controls_Gamepad_API +--- +<div>{{GamesSidebar}}</div> + +<div></div> + +<p class="summary">В этой статье рассматривается реализация эффективной кросс-браузерной системы управления веб-играми с использованием API Gamepad, позволяющей управлять веб-играми с помощью консольных игровых контроллеров. В нем есть тематическое исследование игры "Голодный холодильник", созданный компанией <a href="http://enclavegames.com/">Enclave Games</a>.</p> + +<h2 id="Элементы_управления_для_web_игр">Элементы управления для web игр</h2> + +<p>Исторически игра на консоли, подключенной к телевизору, всегда была совершенно другим опытом, чем игра на ПК, в основном из-за уникальных элементов управления. В конце концов, дополнительные драйверы и плагины позволили нам использовать консольные геймпады с настольными играми - либо нативными играми, либо теми, которые работают в браузере. Теперь, в эпоху HTML5, у нас наконец есть API Gamepad, который дает нам возможность играть в браузерные игры с использованием геймпад-контроллеры без каких-либо плагинов. API Gamepad достигает этого, предоставляя интерфейс, демонстрирующий нажатия кнопок и изменения оси, которые могут быть использованы внутри кода JavaScript для обработки входных данных. Это хорошие времена для браузерных игр.</p> + +<p><img alt="gamepad-controls" src="http://end3r.com/tmp/gamepad/gamepadapi-hungryfridge-img01.png" style="display: block; height: 400px; margin: 0px auto; width: 600px;"></p> + +<h2 id="API_статус_и_поддержка_браузера">API статус и поддержка браузера</h2> + +<p><a href="http://www.w3.org/TR/gamepad/">Gamepad API</a> все еще находится на стадии рабочего проекта в процессе W3C, что означает, что его реализация все еще может измениться, но говорит о том, что поддержка браузера уже довольно хороша. Firefox 29+ и Chrome 35+ поддерживают его сразу после установки. Opera поддерживает API в версии 22+ (неудивительно, учитывая, что теперь они используют движок Blink Chrome.) И Microsoft недавно реализовала поддержку API в Edge,что означает, что четыре основных браузера теперь поддерживают API Gamepad.</p> + +<h2 id="Какие_геймпады_лучше_всего">Какие геймпады лучше всего?</h2> + +<p><img alt="gamepad devices" src="http://end3r.com/tmp/gamepad/devices.jpg" style="display: block; height: 450px; margin: 0px auto; width: 600px;"></p> + +<p>Наиболее популярные геймпады в наше время - геймпады от Xbox 360, Xbox One, PS3 и PS4 — они прошли все испытания и хорошо работают с Gamepad API, реализованном в браузерах на Windows и Mac OS X.</p> + +<p>Существует также ряд других устройств с различными макетами кнопок, которые более или менее работают в разных реализациях браузера. Код, описанный в этой статье, был протестирован с помощью нескольких геймпадов, но любимая конфигурация автора-это беспроводной контроллер Xbox 360 и браузер Firefox на Mac OS X.</p> + +<h2 id="Пример_исследования_Голодный_холодильник">Пример исследования: Голодный холодильник</h2> + +<p>The competition ran in November 2013 and decided to take part in it. The theme for the competition was "change", so they submitted a game where you have to feed the Hungry Fridge by tapping the healthy food (apples, carrots, lettuces) and avoid the "bad" food (beer, burgers, pizza.) A countdown changes the type of food the Fridge wants to eat every few seconds, so you have to be careful and act quickly. You can <a href="http://enclavegames.com/games/hungry-fridge/">try Hungry Fridge here</a>.</p> + +<p>Конкурс <a href="https://github.com/blog/1674-github-game-off-ii">GitHub Game Off II</a> состоялся в ноябре 2013 года, и <a href="http://enclavegames.com/">Enclave Games</a> решила принять в нем участие. Тема для конкурса была "переменна", поэтому они представили игру, в которой Вы должны накормить голодный холодильник, нажав на здоровую пищу (яблоки, морковь, салат) и избежать "плохой" пищи (пиво, гамбургеры, пицца.) Обратный отсчет меняет тип пищи, которую холодильник хочет съесть каждые несколько секунд, поэтому вы должны быть осторожны и действовать быстро. Вы можете попробовать Голодный холодильник <a href="http://enclavegames.com/games/hungry-fridge/">здесь</a>.</p> + +<p><img alt="hungryfridge-mainmenu" src="http://end3r.com/tmp/gamepad/gamepadapi-hungryfridge-img02.png" style="display: block; height: 333px; margin: 0px auto; width: 500px;"></p> + +<p>Вторая, скрытая реализация "изменения" - это возможность превратить статичный холодильник в полноценную движущуюся, стреляющую и едящую машину. Когда вы подключаете контроллер, игра значительно меняется (голодный холодильник превращается в супер турбо голодный холодильник), и вы можете управлять бронированным холодильником с помощью API Gamepad. Вы должны сбивать еду, но вы все еще должны искать тип пищи, которую холодильник хочет съесть в каждой точке, иначе вы потеряете энергию.</p> + +<p><img alt="hungryfridge-howtoplay-gamepad" src="http://end3r.com/tmp/gamepad/gamepadapi-hungryfridge-img03.png" style="display: block; height: 333px; margin: 0px auto; width: 500px;"></p> + +<p>Игра инкапсулирует два совершенно разных типа "изменений" - хорошая еда против плохой еды и мобильная против настольной.</p> + +<p><img alt="hungryfridge-gameplay-gamepad" src="http://end3r.com/tmp/gamepad/gamepadapi-hungryfridge-img04.png" style="display: block; height: 333px; margin: 0px auto; width: 500px;"></p> + +<h2 id="Демо-версия">Демо-версия</h2> + +<p>Сначала была построена полная версия игры Hungry Fridge, а затем, чтобы для демострации API Gamepad в действии и показа исходного кода JavaScript, была создана <a href="https://end3r.github.io/Gamepad-API-Content-Kit/demo/demo.html">простая демо-версия</a>. Это часть <a href="https://end3r.github.io/Gamepad-API-Content-Kit/">набора контента Gamepad API</a>, доступного на GitHub, где вы можете глубоко погрузиться в код и изучить, как именно он работает.</p> + +<p><img alt="Hungry Fridge demo using Gamepad API" src="http://end3r.com/tmp/gamepad/super-turbo.png" style="display: block; height: 333px; margin: 0px auto; width: 500px;"></p> + +<p>Код, описанный ниже, взят из полной версии игры Hungry Fridge, но он почти идентичен демо-версии — единственная разница заключается в том, что полная версия использует переменную turbo, чтобы решить, будет ли игра запущена с использованием режима Super Turbo. Он работает независимо, поэтому его можно включить, даже если геймпад не подключен.</p> + +<div class="note"> +<p><strong>ПРИМЕЧАНИЕ: </strong>время пасхальных яиц: есть скрытая опция для запуска Super Turbo Hungry Fridge на рабочем столе без подключения геймпада — нажмите на значок геймпада в правом верхнем углу экрана. Он запустит игру в режиме Super Turbo, и Вы сможете управлять холодильником с помощью клавиатуры: A и D для поворота башни влево и вправо, W для стрельбы и клавиш со стрелками для перемещения.</p> +</div> + +<h2 id="Реализация">Реализация</h2> + +<p>Есть два важных события, которые можно использовать вместе с API Gamepad-<code>gamepadconnected </code>и <code>gamepaddisconnected</code>. Первый срабатывает, когда браузер обнаруживает подключение нового геймпада, а второй - когда геймпад отключен (либо физически пользователем, либо из-за бездействия).) В демо-версии объект <code>gamepadAPI </code>используется для хранения всего, что связано с API:</p> + +<pre class="brush: js"><code>var gamepadAPI = { + controller: {}, + turbo: false, + connect: function() {}, + disconnect: function() {}, + update: function() {}, + buttonPressed: function() {}, + buttons: [], + buttonsCache: [], + buttonsStatus: [], + axesStatus: [] +};</code></pre> + +<p>Массив <code>кнопок</code> содержит клавиатуру Xbox 360 :</p> + +<pre class="brush: js"><code>buttons: [ + 'DPad-Up','DPad-Down','DPad-Left','DPad-Right', + 'Start','Back','Axis-Left','Axis-Right', + 'LB','RB','Power','A','B','X','Y', +],</code> +</pre> + +<p>Это может быть по-разному для разных типов геймпадов, таких как контроллер PS3 (или безымянный, универсальный), поэтому вы должны быть осторожны и не просто предполагать, что кнопка, которую вы ожидаете, будет той же самой кнопкой, которую вы на самом деле получите. Затем мы настроили два прослушивателя событий, чтобы получить данные:</p> + +<pre class="brush: js"><code>window.addEventListener("gamepadconnected", gamepadAPI.connect); +window.addEventListener("gamepaddisconnected", gamepadAPI.disconnect);</code> +</pre> + +<p>Из-за политики безопасности вы должны сначала начать взаимодействовать с контроллером, пока страница видна для запуска события. Если API работал без какого-либо взаимодействия с пользователем, его можно было использовать для снятия отпечатков пальцев без их ведома.</p> + +<p>Обе функции довольно просты:</p> + +<pre class="brush: js"><code>connect: function(evt) { + gamepadAPI.controller = evt.gamepad; + gamepadAPI.turbo = true; + console.log('Gamepad connected.'); +},</code></pre> + +<p>The <code>connect()</code> function takes the event as a parameter and assigns the <code>gamepad</code> object to the <code>gamepadAPI.controller</code> variable. We are using only one gamepad for this game, so it's a single object instead of an array of gamepads. We then set the <code>turbo</code> property to <code>true</code>. (We could use the <code>gamepad.connected</code> boolean for this purpose, but we wanted to have a separate variable for turning on Turbo mode without needing to have a gamepad connected, for reasons explained above.)</p> + +<p>Функция <code>connect()</code> принимает событие в качестве параметра и назначает объект <code>gamepad</code> объекту <code>gamepadAPI.controller</code>. Мы используем только один геймпад для этой игры, так что это один объект вместо массива геймпадов. Затем мы устанавливаем свойство turbo в true. (Мы могли бы использовать <code>gamepad.connected</code>, т.к для этой цели подключается логическое значение, но мы хотели иметь отдельную переменную для включения турбо-режима без необходимости подключения геймпада, по причинам, описанным выше.)</p> + +<pre class="brush: js"><code>disconnect: function(evt) { + gamepadAPI.turbo = false; + delete gamepadAPI.controller; + console.log('Gamepad disconnected.'); +},</code> +</pre> + +<p>Функция <code>disconnect</code> устанавливает <code>gamepad.turbo property</code> на <code>false</code> и удаляет переменную, содержащую объект gamepad.</p> + +<h3 id="Объект_геймпада">Объект геймпада</h3> + +<p>В объекте <code>gamepad </code>содержится много полезной информации, причем наиболее важными являются состояния кнопок и осей:</p> + +<ul> + <li><code>id</code>: Строка, содержащая информацию о контроллере.</li> + <li><code>index</code>: Уникальный индентификатор для подключенного устройства.</li> + <li><code>connected</code>: Логическая переменная. Возвращает <code>true</code> при подключении.</li> + <li><code>mapping</code>: Тип компоновки кнопок; Стандартный - единственный доступный вариант на данный момент.</li> + <li><code>axes</code>: Состояние каждой оси, представленное массивом значений с плавающей запятой.</li> + <li><code>buttons</code> : Состояние каждой кнопки, представленное массивом объектов GamepadButton, содержащих <code>pressed</code> и <code>value</code> свойства.</li> +</ul> + +<p>Переменная <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">index</span></font> полезна, если мы подключаем более одного контроллера и хотим идентифицировать их, чтобы действовать соответственно — например, когда у нас есть игра для двух игроков, требующая подключения двух устройств.</p> + +<h3 id="Запрос_объекта_геймпада">Запрос объекта геймпада</h3> + +<p>Помимо <code>connect()</code> и <code>disconnect()</code>, в объекте <code>gamepadAPI </code>есть еще два метода: <code>update()</code> и <code>buttonPressed()</code>. <code>update() </code>выполняется на каждом кадре внутри игрового цикла, чтобы регулярно обновлять фактическое состояние объекта геймпада:</p> + +<pre class="brush: js"><code>update: function() { + // clear the buttons cache + gamepadAPI.buttonsCache = []; + // move the buttons status from the previous frame to the cache + for(var k=0; k<gamepadAPI.buttonsStatus.length; k++) { + gamepadAPI.buttonsCache[k] = gamepadAPI.buttonsStatus[k]; + } + // clear the buttons status + gamepadAPI.buttonsStatus = []; + // get the gamepad object + var c = gamepadAPI.controller || {}; + + // loop through buttons and push the pressed ones to the array + var pressed = []; + if(c.buttons) { + for(var b=0,t=c.buttons.length; b<t; b++) { + if(c.buttons[b].pressed) { + pressed.push(gamepadAPI.buttons[b]); + } + } + } + // loop through axes and push their values to the array + var axes = []; + if(c.axes) { + for(var a=0,x=c.axes.length; a<x; a++) { + axes.push(c.axes[a].toFixed(2)); + } + } + // assign received values + gamepadAPI.axesStatus = axes; + gamepadAPI.buttonsStatus = pressed; + // return buttons for debugging purposes + return pressed; +},</code> +</pre> + +<p>На каждом кадре, <code>update()</code> сохраняет кнопки, нажатые во время предыдущего кадра, в массиве <code>buttonsCache </code>и берет новые из объекта <code>gamepadAPI.controller</code>. Затем он проходит по кнопкам и осям, чтобы получить их фактические состояния и значения.</p> + +<h3 id="Обнаружение_нажатия_кнопок">Обнаружение нажатия кнопок</h3> + +<p>Метод <code>buttonPressed()</code> также помещается в основной игровой цикл для прослушивания нажатий кнопок. Для этого требуется два параметра - кнопка, которую мы хотим прослушать, и (необязательно) способ сообщить игре, что удержание кнопки принято. Без него вам придется отпустить кнопку и нажать ее снова, чтобы получить желаемый эффект.</p> + +<pre class="brush: js"><code>buttonPressed: function(button, hold) { + var newPress = false; + // loop through pressed buttons + for(var i=0,s=gamepadAPI.buttonsStatus.length; i<s; i++) { + // if we found the button we're looking for... + if(gamepadAPI.buttonsStatus[i] == button) { + // set the boolean variable to true + newPress = true; + // if we want to check the single press + if(!hold) { + // loop through the cached states from the previous frame + for(var j=0,p=gamepadAPI.buttonsCache.length; j<p; j++) { + // if the button was already pressed, ignore new press + if(gamepadAPI.buttonsCache[j] == button) { + newPress = false; + } + } + } + } + } + return newPress; +},</code> +</pre> + +<p>Существует два типа действий, которые следует учитывать для кнопки: одно нажатие и удержание. Логическая переменная <code>newPress </code>будет указывать, есть ли новое нажатие кнопки или нет. Затем мы проходим через массив нажатых кнопок — если данная кнопка совпадает с той, которую мы ищем, переменная <code>newPress </code>устанавливается в <code>true</code>. Чтобы проверить, является ли нажатие новым, так как игрок не держит клавишу, мы перебираем кэшированные состояния кнопок из предыдущего кадра игрового цикла. Если мы находим его там, это означает, что кнопка удерживается, поэтому нового нажатия нет. В конце концов возвращается переменная <code>newPress</code>. Функция <code>buttonPressed </code>используется в цикле обновления игры следующим образом:</p> + +<pre class="brush: js"><code>if(gamepadAPI.turbo) { + if(gamepadAPI.buttonPressed('A','hold')) { + this.turbo_fire(); + } + if(gamepadAPI.buttonPressed('B')) { + this.managePause(); + } +}</code> +</pre> + +<p>Если <code>gamepadAPI.turbo</code> возвращает <code>true</code>, при нажатии (или удержании) данных кнопок мы выполняем соответствующие функции, возложенные на них. В этом случае нажатие или удержание "A" приведет к выстрелу пули, а нажатие "B" поставит игру на паузу.</p> + +<h3 id="Порог_оси">Порог оси</h3> + +<p>Кнопки имеют только два состояния: 0 или 1, но аналоговые стики могут иметь много различных значений — они имеют плавающий диапазон между -1 и 1 вдоль обеих осей X и Y.</p> + +<p><img alt="axis threshold" src="http://end3r.com/tmp/gamepad/axis-threshold.png" style="display: block; height: 300px; margin: 0px auto; width: 400px;"></p> + +<p>Геймпады могут запылиться от лежания в бездействии, что означает, что проверка точных значений -1 или 1 может быть проблемой. По этой причине может быть полезно установить пороговое значение для того, чтобы значение оси вступило в силу. Например, бак холодильника поворачивается вправо только тогда, когда значение X больше 0,5:</p> + +<pre><code>if(gamepadAPI.axesStatus[0].x > 0.5) { + this.player.angle += 3; + this.turret.angle += 3; +}</code> +</pre> + +<p>Даже если мы немного сдвинем его по ошибке или стик не вернется в исходное положение, танк не повернется неожиданно.</p> + +<h2 id="Обновление_спецификаций">Обновление спецификаций</h2> + +<p>После более чем годичной стабильности, в апреле 2015 года была обновлена спецификация API W3C Gamepad (<a href="https://w3c.github.io/gamepad/">см. последнюю версию</a>). Он не сильно изменился, но хорошо знать, что происходит — обновления заключаются в следующем...</p> + +<h3 id="Получение_геймпадов">Получение геймпадов</h3> + +<p>Метод {{domxref ("Navigator.getGamepads ()")}} был обновлен с более длинным объяснением и примером кода. Теперь длина массива геймпадов должна быть <code>n+1</code>, где <code>n</code>-количество подключенных устройств — когда подключено одно устройство и оно имеет индекс 1, длина массива равна 2, и он будет выглядеть следующим образом: <code>[null, [object Gamepad]]</code>. Если устройство отключено или недоступно, то для него устанавливается значение <code>null</code>.</p> + +<h3 id="Стандартное_отображение">Стандартное отображение</h3> + +<p>Тип отображения теперь является перечисляемым объектом вместо строки:</p> + +<pre>enum GamepadMappingType { + "", + "standard" +}; +</pre> + +<p>Это перечисление определяет набор известных отображений для геймпада. На данный момент доступна только компоновка <code>standart</code>, но в будущем могут появиться новые. Если макет неизвестен, он устанавливается в пустую строку.</p> + +<h3 id="События">События</h3> + +<p>В спецификации было доступно больше событий, чем просто <code>gamepadconnected </code>и <code>gamepaddisconnected</code>, но они были удалены из спецификации, поскольку считались не очень полезными. До сих пор продолжается дискуссия о том, следует ли их возвращать и в какой форме.</p> + +<h2 id="Подведение_итогов">Подведение итогов</h2> + +<p>API геймпада очень прост в разработке. Теперь это проще, чем когда-либо, вы можете использовать браузер как консоль без необходимости каких-либо плагинов. Вы можете играть в полную версию игры Hungry Fridge непосредственно в вашем браузере, установить ее из <a href="https://marketplace.firefox.com/app/hungry-fridge">Firefox Marketplace</a> или проверить исходный код демо-версии вместе со всеми другими ресурсами в <a href="https://github.com/EnclaveGames/Hungry-Fridge">комплекте контента Gamepad API</a>.</p> |