De HTML5 Gamepad-API gebruiken om controllerondersteuning toe te voegen aan browsergames

Naarmate web-based gaming populairder wordt, is een van de grootste knelpunten voor spelers invoercontrole. Hoewel mijn eerste FPS-games puur op muis en toetsenbord waren gebaseerd, ben ik nu zoveel meer gewend aan een juiste console-controller dat ik deze liever voor alles gebruik, inclusief webspellen. 

Gelukkig bestaat de HTML5 Gamepad-API om webontwikkelaars programmatische toegang te geven tot gamecontrollers. Helaas, hoewel deze API al heel lang bestaat, is het nu langzaam aan het overgaan naar de meest recente versies van desktopbrowsers. Het kwijnde lang weg in één versie van Firefox (niet één hoger gebouwd, niemand nachtelijk build) en was problematisch in Chrome. Nu is het -goed-niet-perfect, maar iets minder problematisch en eigenlijk vrij gemakkelijk te gebruiken. 

In dit artikel zal ik de verschillende functies van de API bespreken, hoe deze werken in zowel Firefox als Chrome, en een echt (als eenvoudig) spel laten zien en hoe gemakkelijk het is om ondersteuning voor gamepad toe te voegen.

De basis

De Gamepad-API bevat de volgende functies:

  • Het vermogen om te luisteren naar aansluiten en Loskoppelen events.
  • De mogelijkheid om meerdere gamepads te herkennen. (In theorie zou je zoveel gamepads kunnen aansluiten als je USB-poorten hebt.)
  • De mogelijkheid om deze gamepads te inspecteren en te herkennen hoeveel assen ze hebben (joysticks), hoeveel knoppen ze hebben (heb je de laatste tijd een moderne gameconsole gespeeld?) En in welke staat elk van deze afzonderlijke items zich bevindt.

Laten we beginnen met bespreken hoe u ondersteuning voor een gamepad op een hoog niveau kunt detecteren. 

Zowel Firefox als Chrome ondersteunen een methode navigator, getGamepads (), die een array van alle aangesloten gamepad-apparaten retourneert. We kunnen dit gebruiken als een eenvoudige methode om te detecteren of de Gamepad-API aanwezig is. Hier is een eenvoudige functie voor die controle:

function canGame () terug "getGamepads" in de navigator; 

Tot zover goed. Nu voor het funky gedeelte. De Gamepad-API biedt ondersteuning voor evenementen die detecteren wanneer een gamepad is aangesloten en losgekoppeld. Maar wat gebeurt er als de gebruiker al een gamepad op zijn laptop heeft aangesloten wanneer deze op je pagina terechtkomt? Normaal wacht de webpagina op de gebruiker om iets te doen, echt iets, met de eigenlijke gamepad. Dit betekent dat we de gebruiker een bericht moeten sturen dat hen laat weten dat ze de ondersteuning voor de gamepad moeten "ontwaken" als deze is aangesloten. Je zou kunnen zeggen dat ze op een knop moeten drukken of een stok moeten verplaatsen. 

Om het nog interessanter te maken, lijkt deze specifieke controle niet nodig te zijn wanneer u de pagina opnieuw laadt. U zult merken dat zodra u de Gamepad-API op een pagina heeft gebruikt en deze vervolgens opnieuw hebt geladen, de pagina dit feit herkent en deze automatisch als verbonden beschouwt.

Maar wacht - het wordt beter. Chrome biedt momenteel geen ondersteuning voor de verbonden (of niet-verbonden) gebeurtenissen. Het typische werk dat hiervoor wordt gedaan (en dat wordt aangetoond in de goede MDN-documenten voor de API) is om een ​​peiling in te stellen en te zien of een gamepad "verschijnt" in de lijst met verbonden apparaten.

Verwarrend? Laten we beginnen met een voorbeeld dat alleen Firefox ondersteunt:

           

In het bovenstaande voorbeeld controleren we eerst of de browser de Gamepad-API ondersteunt. Als dit het geval is, werken we eerst een div bij met instructies voor de gebruiker en beginnen dan meteen te luisteren naar beide aansluiten en Loskoppelen events. 

Als je dit met Firefox uitvoert en je gamepad aansluit, moet je dan ook op een knop drukken, waarna het evenement wordt afgevuurd en je klaar bent om te gaan. 

Nogmaals, in mijn testen, wanneer ik de pagina herlaad, is de verbinding evenement is onmiddellijk. Dit veroorzaakt een licht "flikkering" -effect dat misschien niet te verwachten is. U kunt zelfs een interval gebruiken om de routebeschrijving in te stellen voor iets als 250 ms nadat de DOM is geladen en alleen vragen als er in de tussentijd geen verbinding is gemaakt. Ik besloot om dingen eenvoudig te houden voor deze tutorial.

Onze code werkt voor Firefox, maar laten we nu Chrome-ondersteuning toevoegen:

           

De code is nu wat complexer, maar niet zo erg. Laad de demo in Chrome en kijk wat er gebeurt.

Merk op dat we een nieuwe globale variabele hebben, hasGP, die we zullen gebruiken als een algemene vlag voor het hebben van een gamepad aangesloten. Net als voorheen hebben we twee gebeurtenislisteners, maar nu hebben we een nieuw interval ingesteld om te controleren of er een gamepad bestaat. Dit is de eerste keer dat je het hebt gezien getGamepads in actie, en we zullen het in de volgende sectie een beetje meer beschrijven, maar weten nu dat het gewoon een array retourneert en als het eerste item bestaat, kunnen we dat gebruiken als een manier om te weten dat een gamepad is verbonden. 

We gebruiken jQuery om dezelfde gebeurtenis af te vuren die Firefox zou hebben ontvangen en vervolgens het interval te wissen. Merk op dat ditzelfde interval ook een keer in Firefox zal worden geactiveerd, wat enigszins verspillend is, maar eerlijk gezegd vond ik het een verspilling van tijd om extra ondersteuning toe te voegen aan het snuiven van Chrome versus Firefox. Een klein telefoontje als dit verspild in Firefox doet er helemaal niet toe.

Nu we een aangesloten gamepad hebben, laten we ermee werken!

Het Gamepad-object

Om je een idee te geven van hoe oud ik ben - hier is de ultramoderne joystick die ik gebruikte voor mijn eerste spelsysteem.


Afbeelding van Wikimedia Commons.

Leuk - eenvoudig - en het deed pijn als na een uur spelen. Moderne consoles hebben veel complexere gamepads. Overweeg de PS4-controller:

Afbeelding van Wikimedia Commons.

Deze controller heeft twee sticks, een richtingspad, vier hoofdknoppen, vier meer op de achterkant, a Delen en opties knop, a PS knop, een funky touch control-ding, een luidspreker en een lampje. Het heeft waarschijnlijk ook een flux-condensator en een keukengootsteen. 

Gelukkig hebben we toegang tot dit beest via het object Gamepad. Eigenschappen omvatten:

  • ID kaart: Dit is de naam van de controller. Verwacht hier niets vriendelijks van. Mijn DualShock 4 is gerapporteerd als 54c-5c4-Draadloze controller in Firefox, terwijl Chrome dezelfde controller heeft genoemd Draadloze controller (STANDARD GAMEPAD Leverancier: 054c Product: 05c4).
  • inhoudsopgave: Omdat de Gamepad-API meerdere controllers ondersteunt, kunt u hiermee bepalen welke genummerde controller deze is. Het kan worden gebruikt om speler één, twee enzovoort te identificeren.
  • in kaart brengen: Mapping is niet iets dat we hier gaan behandelen, maar in wezen is dit iets dat de browser kan doen om uw specifieke controller in een "standaard" configuratie van de controller te brengen. Als je meerdere consoles hebt gespeeld, weet je dat ze een aantal overeenkomsten vertonen op het gebied van besturing en de API probeert je controller in een standaard te "maaien". U hoeft zich hier voorlopig geen zorgen over te maken, maar als u meer details wilt, kijk dan in de kaartsectie van de API-documenten.
  • verbonden: Een Boolean die aangeeft of de controller nog steeds verbonden is.
  • toetsen: Een reeks knopwaarden. Elke knop is een instantie van GamepadButton. Merk op dat de GamepadButton object ondersteunt zowel een eenvoudige Boolean-eigenschap (geperste) evenals een waarde eigenschap voor analoge knoppen.
  • as: Een reeks waarden die de verschillende sticks op de gamepad vertegenwoordigen. Gegeven een gamepad met drie sticks, heb je een array van zes items, waarbij elke stick wordt weergegeven door twee arraywaarden. De eerste in het paar staat voor X, of links / rechts, terwijl de tweede staat voor Y, omhoog / omlaag beweging. In alle gevallen zal de waarde variëren van -1 naar 1: voor links / rechts-waarden, -1 is overgebleven en 1 is juist; voor op / neer waarden, -1 is op en 1 is uit de lucht. Volgens de API wordt de array gesorteerd op 'belangrijkheid', dus in theorie kun je je hierop concentreren assen [0] en assen [1] voor de meeste gaming-behoeften. Om dingen interessanter te maken, gebruikmakend van mijn DualShock 4, meldde Firefox drie assen (wat logisch is - zie de afbeelding hierboven), maar Chrome meldde er twee. Het lijkt alsof de d-pad-stok in Firefox als een as wordt gerapporteerd, maar er lijken geen gegevens uit te komen. In Chrome is het d-pad als extra knoppen weergegeven en correct gelezen.
  • tijdstempel: Ten slotte is deze waarde een tijdstempel die de laatste keer dat de hardware werd gecontroleerd, vertegenwoordigt. In theorie is dit waarschijnlijk niet iets dat je zou gebruiken.

Oké, dus dat is veel te verteren. In het onderstaande voorbeeld hebben we eenvoudig een interval toegevoegd om de eerste gamepad te krijgen en te inspecteren en de ID en vervolgens de knoppen en assen af ​​te drukken:

           

U kunt de demo in Chrome of Firefox proberen.

Ik neem aan dat dit allemaal vrij duidelijk is; het enige echte moeilijke deel was het hanteren van de assen. Ik loop over de array en tel door twee om beide links / rechts, omhoog / omlaag in een keer weer te geven. Als u dit in Firefox opent en een DualShock aansluit, ziet u mogelijk iets als dit.

Zoals je kunt zien, werd op knop 2 gedrukt toen ik mijn screenshot nam. (In het geval je nieuwsgierig bent, was dat het X knop.) Let op de stokken; mijn gamepad zat op mijn laptop en die waarden waren constant fluctuerend. Niet op een manier die zou impliceren dat de waarden dat waren slecht, per se - als ik het gamepad oppikte en helemaal in de ene richting duwde, zag ik de juiste waarde. Maar ik geloof dat wat ik zag precies was hoe gevoelig de controller voor de omgeving is. Of misschien gremlins.

Hier is een voorbeeld van hoe Chrome het weergeeft:

Ik hield opnieuw de X knop, maar let op hoe de knopindex hier anders is. Zoals je ziet, moet je een beetje ... masseren als je deze API voor een game wilt gebruiken. Ik kan me voorstellen dat je beide knoppen 1 en 2 kunt controleren op 'vuur' en een groot deel van de tests kunt uitvoeren.

Alles samenvoegen

Dus, wat dacht je van een echte demo? Zoals de meeste programmeurs die hun leven begonnen met het spelen van videogames, droomde ik ervan een videofunctiescreator te zijn toen ik opgroeide. Het blijkt dat wiskunde heel moeilijk wordt na calculus en blijkbaar heeft dit 'web'-spul een toekomst, dus hoewel die toekomst niet op mij rustte, zou ik me toch graag voorstellen dat ik op een dag deze webstandaarden zou kunnen veranderen vaardigheden in een speelbaar spel. Tot die dag, wat ik vandaag heb is een behoorlijk kreupele canvas-gebaseerde versie van pong. Single-player pong. Zoals ik al zei, zwak.

Het spel maakt gewoon een peddel en een bal en geeft je toetsenbord controle over de bal. Elke keer dat je de bal mist, gaat de score omhoog. Dat is logisch voor golf in plaats van pong, neem ik aan, maar laten we ons daar niet al te veel zorgen over maken. De code is te vinden in game1.html en je kunt de demo in je browser afspelen. 

Ik zal hier niet alle code doornemen, maar laten we eens naar enkele fragmenten kijken. Ten eerste, hier is de hoofdlusfunctie die alle animatiedetails afhandelt:

function loop () draw.clear (); ball.move (); ball.draw (); paddle.draw (); paddle.move (); draw.text ("Score:" + score, 10, 20, 20); 

De paddle wordt aangedreven door het toetsenbord met behulp van twee eenvoudige gebeurtenishandlers:

$ (window) .keydown (functie (e) switch (e.keyCode) case 37: input.left = true; break; case 39: input.right = true; break;); $ (venster) .keyup (functie (e) switch (e.keyCode) case 37: input.left = false; break; case 39: input.right = false; break;); 

De invoer variabele is een globale variabele die wordt opgepikt door een paddle-object verhuizing methode:

this.move = function () if (input.left) this.x - = this.speed; if (this.x < 0) this.x=0;  if(input.right)  this.x += this.speed; if((this.x+this.w) > canvas.width) this.x = canvas.width-this.w;  

Nogmaals, niets te complex hier. Hier is een screenshot van het spel in actie. (Ik weet het - ik moet mijn baan niet opgeven.)

Dus, hoe voegen we gamepad-ondersteuning toe? Gelukkig hebben we de code al voor ons gedaan. In de vorige demo hebben we al het nodige gedaan om updates van de code te controleren en op te merken. We kunnen die code nemen en deze gewoon toevoegen aan de bestaande code van het spel. 

Omdat het (vrijwel) hetzelfde is, zal ik het niet herhalen (hoewel de volledige lijst beschikbaar is als je het wilt), maar ik deel de aangepaste code elke 100 ms nadat een gamepad is gedetecteerd:

function checkGamepad () var gp = navigator.getGamepads () [0]; var axeLF = gp.axes [0]; if (axeLF < -0.5)  input.left = true; input.right = false;  else if(axeLF > 0.5) input.left = false; input.right = true;  else input.left = false; input.right = false;  

Nogmaals, je kunt de demo in beide browsers proberen.

Net als bij het vorige voorbeeld zijn we ervan uitgegaan dat we slechts om één gamepad geven. Omdat onze game alleen een paddle heeft en deze alleen horizontaal beweegt, kunnen we langskomen door alleen de allereerste as te controleren. Denk eraan, volgens de API zou dit de "belangrijkste" moeten zijn, en in mijn testen was dit de linker joystick, die vrij standaard is voor games. 

Omdat onze game een globale variabele gebruikt, invoer, om links en rechts beweging weer te geven, alles wat ik moet doen is die waarde aanpassen op basis van de aswaarde. Merk nu op dat ik niet eenvoudigweg controleerde op "minder dan nul" en "groter dan nul". Waarom? Als je je herinnert van de eerdere demo, was de gamepad erg gevoelig en rapporteerde ik vaak waarden, zelfs als ik niet dacht dat ik de stick echt had verplaatst. Een grenswaarde van gebruiken .5 geeft de besturing een beetje meer stabiliteit. (En natuurlijk is dit het soort ding dat je zou moeten aanpassen om te zien wat goed voelt.) 

Al met al heb ik ongeveer 25 regels code aan mijn spel toegevoegd om gamepad-ondersteuning toe te voegen. Dat klopt.

Game On!

Hopelijk heb je dat gezien, hoewel er zeker enkele eigenaardigheden zijn, heeft de Gamepad-API nu ondersteuning in twee grote browsers, en het is iets waarvan ik denk dat ontwikkelaars echt zouden moeten nadenken over hun spellen..

Middelen

Hier zijn een paar aanvullende bronnen om u te helpen meer te leren over de Gamepad-API.

  • De Gamepad-API gebruiken
  • Gamepad-specificatie
  • Gamepad.js - Een Javascript-bibliotheek om gamepads en joysticks in de browser in te schakelen.
  • Gamepad-besturingselementen voor HTML5-spellen
  • Wii U's versie (totaal verschillend van de specificaties - goed gedaan, Nintendo!)

Referenties

  • Voorbeeldafbeeldingstegoed: Videospelcontroller ontworpen door Uriel Sosa van het Noun-project