Stellen Sie sich eine Spielfigur namens "Bob the Butcher" vor, die alleine in einem dunklen Raum steht, während Horden von mutierten Wurstzombies durch Türen und zerbrochene Fenster strömen. An diesem Punkt wäre es eine gute Idee für Bob, die Wurst-Zombies in winzige Fleischstücke zu sprengen, aber wie wird Bob das in einem Cross-Plattform-Spiel tun? Muss der Spieler eine oder mehrere Tasten auf einer Tastatur drücken, mit der Maus klicken, auf den Bildschirm tippen oder eine Taste auf einem Gamepad drücken?
Wenn Sie ein plattformübergreifendes Spiel programmieren, ist dies die Art von Dingen, mit der Sie wahrscheinlich viel Zeit damit verbringen, zu kämpfen, wenn Sie nicht darauf vorbereitet sind. Wenn Sie nicht vorsichtig sind, kann es zu massiven Spaghetti-ähnlichen Ergebnissen kommen ob
Aussagen oder Schalter
Anweisungen für alle verschiedenen Eingabegeräte.
In diesem Lernprogramm werden wir die Dinge viel einfacher machen, indem Sie eine einzige Klasse erstellen, die mehrere Eingabegeräte vereinheitlicht. Jede Instanz der Klasse repräsentiert eine bestimmte Spielaktion oder ein bestimmtes Verhalten (z. B. "Schießen", "Laufen" oder "Springen") und kann angewiesen werden, verschiedene Tasten, Tasten und Zeiger auf mehreren Eingabegeräten zu hören.
Hinweis: Die in diesem Lernprogramm verwendete Programmiersprache ist JavaScript. Die Technik, mit der mehrere Eingabegeräte vereinheitlicht werden, kann problemlos auf jede andere plattformübergreifende Programmiersprache übertragen werden, die APIs für Eingabegeräte bereitstellt.
Bevor wir mit dem Schreiben des Codes für die Klasse, die wir in diesem Lernprogramm erstellen werden, beginnen, werfen wir einen Blick darauf, wie die Klasse tatsächlich verwendet werden könnte.
// Erzeuge eine Eingabe für die Aktion "Schießen". schießen = neuer GameInput (); // Sag der Eingabe, worauf sie reagieren soll. shoot.add (GameInput.KEYBOARD_SPACE); shoot.add (GameInput.GAMEPAD_RT); // Überprüfen Sie bei jedem Spielupdate die Eingabe. function update () if (shoot.value> 0) // Bitten Sie Bob, die mutierten Wurstzombies zu erschießen! else // Sage Bob, dass er mit dem Schießen aufhören soll.
GameInput
ist die Klasse, die wir erstellen werden, und Sie können sehen, wie viel einfacher die Dinge werden. Das schießen.wert
Eigenschaft ist eine Zahl und ist ein positiver Wert, wenn die Leertaste auf einer Tastatur wird gedrückt oder die rechter Abzug auf einem Gamepad wird gedrückt. Wenn weder die Leertaste noch der rechte Auslöser gedrückt wird, ist der Wert Null.
Das erste, was wir tun müssen, ist das Schließen einer Funktion für die GameInput
Klasse. Der größte Teil des Codes, den wir schreiben werden, ist eigentlich nicht Teil der Klasse. Er muss jedoch von innerhalb der Klasse aus zugänglich sein, während er vor allem anderen verborgen bleibt. Eine Funktionsschließung erlaubt uns dies in JavaScript.
(In einer Programmiersprache wie ActionScript oder C # können Sie einfach private Klassenmitglieder verwenden, aber das ist in JavaScript leider kein Luxus.)
(function () // Code geht hier hin) ();
Der Rest des Codes in diesem Lernprogramm ersetzt den Kommentar "Code goes here".
Der Code benötigt nur eine Handvoll Variablen, die außerhalb von Funktionen definiert werden müssen. Diese Variablen lauten wie folgt.
var TASTATUR = 1; var POINTER = 2; var GAMEPAD = 3; var GERÄT = 16; var CODE = 8; var __pointer = currentX: 0, currentY: 0, previousX: 0, previousY: 0, distanceX: 0, distanceY: 0, Bezeichner: 0, verschoben: falsch, gedrückt: false; var __keyboard = ; var __inputs = []; var __channels = []; var __mouseDetected = false; var __touchDetected = false;
Die Konstante TASTATUR
, ZEIGER
, GAMEPAD
, GERÄT
und CODE
Werte werden zur Definition verwendet Eingabegerät Kanäle, sowie GameInput.KEYBOARD_SPACE
, und ihre Verwendung wird später im Tutorial klar.
Das __Zeiger
Das Objekt enthält Eigenschaften, die für Eingabegeräte für Maus und Touchscreen relevant sind __Tastatur
Objekt wird verwendet, um den Status der Tastaturtasten zu verfolgen. Das __inputs
und __channels
Arrays werden zum Speichern verwendet GameInput
Instanzen und alle zu diesen Instanzen hinzugefügten Eingabegerätekanäle. Endlich, das __mouseDetected
und __touchDetected
Zeigt an, ob eine Maus oder ein Touchscreen erkannt wurde.
Hinweis: Den Variablen müssen nicht zwei Unterstriche vorangestellt werden. Das ist einfach die Codierungskonvention, die ich für den Code in diesem Lernprogramm verwendet habe. Es hilft, sie von in Funktionen definierten Variablen zu trennen.
Hier kommt der Großteil des Codes, daher möchten Sie vielleicht einen Kaffee oder etwas trinken, bevor Sie mit dem Lesen dieses Teils beginnen!
Diese Funktionen werden nach den Variablen im vorherigen Abschnitt dieses Lernprogramms definiert und in der Reihenfolge des Erscheinungsbilds definiert.
// Initialisiert das Eingabesystem. function main () // Den GameInput-Konstruktor verfügbar machen. window.GameInput = GameInput; // Füge die Event Listener hinzu. addMouseListeners (); addTouchListeners (); addKeyboardListeners (); // Einige UI-Aktionen, die wir in einem Spiel verhindern sollten. window.addEventListener ("contextmenu", killEvent, true); window.addEventListener ("selectstart", killEvent, true); // Starten Sie die Aktualisierungsschleife. window.requestAnimationFrame (Update);
Das Main()
Funktion wird am Ende des Codes aufgerufen, dh am Ende des Codes Funktionsabschluss haben wir zuvor erstellt. Es macht, was es auf der Dose sagt und bringt so alles zum Laufen GameInput
Klasse kann verwendet werden.
Eine Sache, die ich auf Sie aufmerksam machen sollte, ist die Verwendung von requestAnimationFrame ()
Funktion, die Teil der W3C-Spezifikation für das Animations-Timing ist. Moderne Spiele und Anwendungen verwenden diese Funktion, um Update- oder Rendering-Schleifen auszuführen, da sie in den meisten Webbrowsern für diesen Zweck stark optimiert wurden.
// Aktualisiert das Eingabesystem. function update () window.requestAnimationFrame (Aktualisierung); // Aktualisiere zuerst die Zeigerwerte. updatePointer (); var i = __inputs.length; var input = null; var channels = null; während (i -> 0) input = __inputs [i]; channels = __channels [i]; if (input.enabled === true) updateInput (Eingang, Kanäle); else input.value = 0;
Das aktualisieren()
Funktion rollt durch die Liste der aktiven GameInput
Instanzen und Updates der aktivierten. Folgende updateInput ()
Funktion ist ziemlich lang, daher füge ich den Code hier nicht hinzu; Sie können den Code vollständig anzeigen, indem Sie die Quelldateien herunterladen.
// Aktualisiert eine GameInput-Instanz. Funktion updateInput (Eingabe, Kanäle) // Anmerkung: siehe Quelldateien
Das updateInput ()
Die Funktion betrachtet die Eingabegerätekanäle, die zu einem hinzugefügt wurden GameInput
Beispiel und klappt was Wert
Eigentum der GameInput
Instanz sollte auf gesetzt sein. Wie im gesehen Würstchen schießen Beispielcode, der Wert
Diese Eigenschaft gibt an, ob ein Eingabegerätekanal ausgelöst wird, und dies ermöglicht einem Spiel, entsprechend zu reagieren, indem es beispielsweise Bob anweist, die mutierten Wurst-Zombies abzuschießen.
// Aktualisiert den Wert einer GameInput-Instanz. Funktion updateValue (Eingabe, Wert, Schwelle) if (Schwelle! == undefined) if (Wert < threshold ) value = 0; // The highest value has priority. if( input.value < value ) input.value = value;
Das updateValue ()
Funktion bestimmt, ob die Wert
Eigentum eines GameInput
Instanz sollte aktualisiert werden. Das Schwelle
wird hauptsächlich verwendet, um zu verhindern, dass analoge Geräteeingangskanäle, wie Gamepad-Tasten und -Sticks, die sich nicht richtig zurücksetzen, ständig ein Signal auslösen GameInput
Beispiel. Dies passiert häufig bei fehlerhaften oder schmuddeligen Gamepads.
Wie updateInput ()
Funktion, die folgende updatePointer ()
Funktion ist ziemlich lang, ich werde den Code hier nicht hinzufügen. Sie können den Code vollständig anzeigen, indem Sie die Quelldateien herunterladen.
// Aktualisiert die Zeigerwerte. Funktion updatePointer () // Hinweis: siehe Quelldateien
Das updatePointer ()
Funktion aktualisiert die Eigenschaften in der __Zeiger
Objekt. Kurz gesagt: Die Funktion klemmt die Position des Zeigers fest, um sicherzustellen, dass er das Ansichtsfenster des Webbrowsers nicht verlässt, und berechnet die Entfernung, die der Zeiger seit der letzten Aktualisierung verschoben wurde.
// Wird aufgerufen, wenn ein Mauseingabegerät erkannt wird. function mouseDetected () if (__mouseDetected === false) __mouseDetected = true; // Berührungsereignisse ignorieren, wenn eine Maus verwendet wird. removeTouchListeners (); // Wird aufgerufen, wenn ein Touchscreen-Eingabegerät erkannt wird. Funktion touchDetected () if (__touchDetected === false) __touchDetected = true; // Mausereignisse ignorieren, wenn ein Touchscreen verwendet wird. removeMouseListeners ();
Das mouseDetected ()
und touchDetected ()
Funktionen weisen den Code an, das eine oder andere Eingabegerät zu ignorieren. Wenn eine Maus vor einem Touchscreen erkannt wird, wird der Touchscreen ignoriert. Wenn ein Touchscreen vor einer Maus erkannt wird, wird die Maus ignoriert.
// Wird aufgerufen, wenn ein zeigerähnliches Eingabegerät gedrückt wird. FunktionszeigerPressed (x, y, Bezeichner) __pointer.identifier = Bezeichner; __pointer.pressed = true; pointerMoved (x, y); // Wird aufgerufen, wenn ein zeigerähnliches Eingabegerät freigegeben wird. function pointerReleased () __pointer.identifier = 0; __pointer.pressed = false; // Wird aufgerufen, wenn ein zeigerähnliches Eingabegerät verschoben wird. FunktionszeigerMoved (x, y) __pointer.currentX = x >>> 0; __pointer.currentY = y >>> 0; if (__pointer.moved === false) __pointer.moved = true; __pointer.previousX = __pointer.currentX; __pointer.previousY = __pointer.currentY;
Das pointerPressed ()
, pointerReleased ()
und pointerMoved ()
Funktionen übernehmen die Eingabe über eine Maus oder einen Touchscreen. Alle drei Funktionen aktualisieren einfach die Eigenschaften im __Zeiger
Objekt.
Nach diesen drei Funktionen haben wir eine Handvoll Standard-JavaScript-Ereignisbehandlungsfunktionen. Die Funktionen sind selbsterklärend, daher füge ich den Code hier nicht hinzu. Sie können den Code vollständig anzeigen, indem Sie die Quelldateien herunterladen.
// Fügt einer GameInput-Instanz einen Eingabegerätekanal hinzu. Funktion inputAdd (Eingang, Kanal) var i = __inputs.indexOf (Eingang); if (i === -1) __inputs.push (Eingabe); __channels.push ([channel]); Rückkehr; var ca = __channels [i]; var ci = ca.indexOf (Kanal); if (ci === -1) ca.push (channel); // Entfernt einen Eingabegerätkanal in eine GameInput-Instanz. Funktion inputRemove (Eingang, Kanal) var i = __inputs.indexOf (Eingang); if (i === -1) return; var ca = __channels [i]; var ci = ca.indexOf (Kanal); if (ci! == -1) ca.splice (ci, 1); if (ca.length === 0) __inputs.splice (i, 1); __channels.splice (i, 1); // Setzt eine GameInput-Instanz zurück. function inputReset (Eingabe) var i = __inputs.indexOf (Eingabe); if (i! == -1) __inputs.splice (i, 1); __channels.splice (i, 1); input.value = 0; input.enabled = true;
Das inputAdd ()
, inputRemove ()
und inputReset ()
Funktionen werden von a aufgerufen GameInput
Instanz (siehe unten). Die Funktionen ändern die __inputs
und __channels
Arrays abhängig davon, was zu tun ist.
EIN GameInput
Die Instanz wird als aktiv betrachtet und der hinzugefügt __inputs
Array, wenn ein Eingabegerätkanal dem hinzugefügt wurde GameInput
Beispiel. Wenn ein aktiver GameInput
Instanz hat alle Eingabegerätkanäle entfernt, die GameInput
Instanz als inaktiv betrachtet und aus dem entfernt __inputs
Array.
Nun kommen wir am an GameInput
Klasse.
// GameInput-Konstruktor. function GameInput () GameInput.prototype = value: 0, aktiviert: true, // Fügt einen Eingabegerätekanal hinzu. add: function (channel) inputAdd (this, channel); , // Entfernt einen Eingabegerätekanal. remove: function (channel) inputRemove (this, channel); , // Entfernt alle Eingabegerätekanäle. reset: function () inputReset (this); ;
Ja, das ist alles, was es gibt - es ist eine superleichte Klasse, die im Wesentlichen als Schnittstelle zum Hauptcode dient. Das Wert
Eigenschaft ist eine Zahl, die von reicht 0
(null) bis zu 1
(ein). Wenn der Wert ist 0
, es bedeutet, dass die GameInput
Die Instanz empfängt nichts von den hinzugefügten Kanälen der Eingabegeräte.
Das GameInput
Klasse hat einige statische Eigenschaften, daher werden wir diese jetzt hinzufügen.
// Die X-Position des Zeigers im Fenster-Ansichtsfenster. GameInput.pointerX = 0; // Die Y-Position des Zeigers im Fenster-Ansichtsfenster. GameInput.pointerY = 0; // Die Entfernung, um die sich der Zeiger in Pixeln pro Bild bewegen muss, damit // der Wert einer GameInput-Instanz gleich 1,0 ist. GameInput.pointerSpeed = 10;
Tastaturgerätekanäle:
GameInput.KEYBOARD_A = TASTATUR << DEVICE | 65 << CODE; GameInput.KEYBOARD_B = KEYBOARD << DEVICE | 66 << CODE; GameInput.KEYBOARD_C = KEYBOARD << DEVICE | 67 << CODE; GameInput.KEYBOARD_D = KEYBOARD << DEVICE | 68 << CODE; GameInput.KEYBOARD_E = KEYBOARD << DEVICE | 69 << CODE; GameInput.KEYBOARD_F = KEYBOARD << DEVICE | 70 << CODE; GameInput.KEYBOARD_G = KEYBOARD << DEVICE | 71 << CODE; GameInput.KEYBOARD_H = KEYBOARD << DEVICE | 72 << CODE; GameInput.KEYBOARD_I = KEYBOARD << DEVICE | 73 << CODE; GameInput.KEYBOARD_J = KEYBOARD << DEVICE | 74 << CODE; GameInput.KEYBOARD_K = KEYBOARD << DEVICE | 75 << CODE; GameInput.KEYBOARD_L = KEYBOARD << DEVICE | 76 << CODE; GameInput.KEYBOARD_M = KEYBOARD << DEVICE | 77 << CODE; GameInput.KEYBOARD_N = KEYBOARD << DEVICE | 78 << CODE; GameInput.KEYBOARD_O = KEYBOARD << DEVICE | 79 << CODE; GameInput.KEYBOARD_P = KEYBOARD << DEVICE | 80 << CODE; GameInput.KEYBOARD_Q = KEYBOARD << DEVICE | 81 << CODE; GameInput.KEYBOARD_R = KEYBOARD << DEVICE | 82 << CODE; GameInput.KEYBOARD_S = KEYBOARD << DEVICE | 83 << CODE; GameInput.KEYBOARD_T = KEYBOARD << DEVICE | 84 << CODE; GameInput.KEYBOARD_U = KEYBOARD << DEVICE | 85 << CODE; GameInput.KEYBOARD_V = KEYBOARD << DEVICE | 86 << CODE; GameInput.KEYBOARD_W = KEYBOARD << DEVICE | 87 << CODE; GameInput.KEYBOARD_X = KEYBOARD << DEVICE | 88 << CODE; GameInput.KEYBOARD_Y = KEYBOARD << DEVICE | 89 << CODE; GameInput.KEYBOARD_Z = KEYBOARD << DEVICE | 90 << CODE; GameInput.KEYBOARD_0 = KEYBOARD << DEVICE | 48 << CODE; GameInput.KEYBOARD_1 = KEYBOARD << DEVICE | 49 << CODE; GameInput.KEYBOARD_2 = KEYBOARD << DEVICE | 50 << CODE; GameInput.KEYBOARD_3 = KEYBOARD << DEVICE | 51 << CODE; GameInput.KEYBOARD_4 = KEYBOARD << DEVICE | 52 << CODE; GameInput.KEYBOARD_5 = KEYBOARD << DEVICE | 53 << CODE; GameInput.KEYBOARD_6 = KEYBOARD << DEVICE | 54 << CODE; GameInput.KEYBOARD_7 = KEYBOARD << DEVICE | 55 << CODE; GameInput.KEYBOARD_8 = KEYBOARD << DEVICE | 56 << CODE; GameInput.KEYBOARD_9 = KEYBOARD << DEVICE | 57 << CODE; GameInput.KEYBOARD_UP = KEYBOARD << DEVICE | 38 << CODE; GameInput.KEYBOARD_DOWN = KEYBOARD << DEVICE | 40 << CODE; GameInput.KEYBOARD_LEFT = KEYBOARD << DEVICE | 37 << CODE; GameInput.KEYBOARD_RIGHT = KEYBOARD << DEVICE | 39 << CODE; GameInput.KEYBOARD_SPACE = KEYBOARD << DEVICE | 32 << CODE; GameInput.KEYBOARD_SHIFT = KEYBOARD << DEVICE | 16 << CODE;
Zeigergerätekanäle:
GameInput.POINTER_UP = POINTER << DEVICE | 0 << CODE; GameInput.POINTER_DOWN = POINTER << DEVICE | 1 << CODE; GameInput.POINTER_LEFT = POINTER << DEVICE | 2 << CODE; GameInput.POINTER_RIGHT = POINTER << DEVICE | 3 << CODE; GameInput.POINTER_PRESS = POINTER << DEVICE | 4 << CODE;
Gamepad-Gerätekanäle:
GameInput.GAMEPAD_A = GAMEPAD << DEVICE | 0 << CODE; GameInput.GAMEPAD_B = GAMEPAD << DEVICE | 1 << CODE; GameInput.GAMEPAD_X = GAMEPAD << DEVICE | 2 << CODE; GameInput.GAMEPAD_Y = GAMEPAD << DEVICE | 3 << CODE; GameInput.GAMEPAD_LB = GAMEPAD << DEVICE | 4 << CODE; GameInput.GAMEPAD_RB = GAMEPAD << DEVICE | 5 << CODE; GameInput.GAMEPAD_LT = GAMEPAD << DEVICE | 6 << CODE; GameInput.GAMEPAD_RT = GAMEPAD << DEVICE | 7 << CODE; GameInput.GAMEPAD_START = GAMEPAD << DEVICE | 8 << CODE; GameInput.GAMEPAD_SELECT = GAMEPAD << DEVICE | 9 << CODE; GameInput.GAMEPAD_L = GAMEPAD << DEVICE | 10 << CODE; GameInput.GAMEPAD_R = GAMEPAD << DEVICE | 11 << CODE; GameInput.GAMEPAD_UP = GAMEPAD << DEVICE | 12 << CODE; GameInput.GAMEPAD_DOWN = GAMEPAD << DEVICE | 13 << CODE; GameInput.GAMEPAD_LEFT = GAMEPAD << DEVICE | 14 << CODE; GameInput.GAMEPAD_RIGHT = GAMEPAD << DEVICE | 15 << CODE; GameInput.GAMEPAD_L_UP = GAMEPAD << DEVICE | 16 << CODE; GameInput.GAMEPAD_L_DOWN = GAMEPAD << DEVICE | 17 << CODE; GameInput.GAMEPAD_L_LEFT = GAMEPAD << DEVICE | 18 << CODE; GameInput.GAMEPAD_L_RIGHT = GAMEPAD << DEVICE | 19 << CODE; GameInput.GAMEPAD_R_UP = GAMEPAD << DEVICE | 20 << CODE; GameInput.GAMEPAD_R_DOWN = GAMEPAD << DEVICE | 21 << CODE; GameInput.GAMEPAD_R_LEFT = GAMEPAD << DEVICE | 22 << CODE; GameInput.GAMEPAD_R_RIGHT = GAMEPAD << DEVICE | 23 << CODE;
Um den Code zu finalisieren, müssen wir einfach die Main()
Funktion.
// Das Eingabesystem initialisieren. Main();
Und das ist alles Code. Wieder ist alles in den Quelldateien vorhanden.
Bevor wir das Tutorial mit einer Schlussfolgerung abschließen, werfen wir einen Blick auf ein weiteres Beispiel GameInput
Klasse kann verwendet werden. Diesmal werden wir Bob die Fähigkeit geben, sich zu bewegen und zu springen, da die Horden mutanter Wurstzombies für ihn zu viel werden könnten, als dass er alleine damit umgehen könnte.
// Erstellen Sie die Eingaben. var jump = new GameInput (); var moveLeft = new GameInput (); var moveRight = new GameInput (); // Sagen Sie den Eingängen, worauf Sie reagieren sollen. jump.add (GameInput.KEYBOARD_UP); jump.add (GameInput.KEYBOARD_W); jump.add (GameInput.GAMEPAD_A); moveLeft.add (GameInput.KEYBOARD_LEFT); moveLeft.add (GameInput.KEYBOARD_A); moveLeft.add (GameInput.GAMEPAD_LEFT); moveRight.add (GameInput.KEYBOARD_RIGHT); moveRight.add (GameInput.KEYBOARD_D); moveRight.add (GameInput.GAMEPAD_RIGHT); // Überprüfen Sie bei jedem Spiel-Update die Eingaben. function update () if (jump.value> 0) // Bitten Sie Bob, zu springen. else // Sag Bob, dass er aufhören soll zu springen. if (moveLeft.value> 0) // Bitten Sie Bob, sich nach links zu bewegen. else // Sag Bob, dass er aufhören soll, sich nach links zu bewegen. if (moveRight.value> 0) // Bitten Sie Bob, sich nach rechts zu bewegen. else // Bitten Sie Bob, sich nicht mehr nach rechts zu bewegen.
Schön und einfach. Denken Sie daran, dass die Wert
Eigentum von GameInput
Instanzen reichen von 0
durch zu 1
, Wir könnten also so etwas wie die Bewegungsgeschwindigkeit von Bob mit diesem Wert ändern, wenn einer der aktiven Eingangskanäle analog ist.
if (moveLeft.value> 0) bob.x - = bob.maxDistancePerFrame * moveLeft.value;
Habe Spaß!
Plattformübergreifende Spiele haben eines gemeinsam: Sie müssen alle mit einer Vielzahl von Spiel-Eingabegeräten (Controllern) umgehen, und der Umgang mit diesen Eingabegeräten kann zu einer entmutigenden Aufgabe werden. In diesem Lernprogramm wurde eine Möglichkeit zum Umgang mit mehreren Eingabegeräten mithilfe einer einfachen, einheitlichen API gezeigt.