[DE] Game Development in nativem JavaScript

in #deutsch7 years ago

banner

<p dir="auto"><strong>Spiele in reinem JavaScript entwickeln scheint schwerer, als es eigentlich ist. Für ein einfaches Spiel benötigt es meist nicht mehr als die Game Loop, eine Menge Variablen und ein wenig Mathe. Im folgenden möchte ich Schritt für Schritt ein Einzelspieler Pong mit euch programmieren. <hr /> <h2>Was ist eine Game Loop? <p dir="auto">Die "Game Loop" oder "Hauptschleife" ist der Teil eines Spiel, welcher immer und immer wieder ausgeführt wird. In dieser Schleife wird alles für das Spiel relevante wie das Zeichnen der Objekte oder das das Berechnen der Physik abgearbeitet. Bei der Implementierung der Game Loop muss darauf geachtet, werden, dass diese richtig erstellt wird. So ist es aufgrund von Timing-Problemen nicht gut, einfach eine <code>while Schleife zu verwenden, da JavaScript single-threaded ist und somit alles andere neben der Schleife komplett blockiert wird. Stattdessen sollte die Funktion <code>requestAnimationFrame verwendet werden. Mehr dazu <a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame" target="_blank" rel="nofollow noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">hier. <h2>Mathe? <p dir="auto">Falls du zu den Leuten gehörst (und ich schließe mich da ein), welchen beim Wort "Mathe" schon ein Schauer über den Rücken läuft, kann ich dich beruhigen: die Mathematik, mit der wir im Folgenden konfrontiert werden, beschränkt sich auf die Grundrechenoperatoren. <h2>Das Spiel <h3>Das Grundgerüst <p dir="auto">Als aller erstes müssen wir das Grundgerüst erstellen, in dem unser Spiel laufen wird. Erstellen wir also die Datei <code>index.html: <pre><code> <!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Game Development in nativen JavaScript</title> <style> #game { background-color: #ccc; width: 480px; height: 320px; margin: auto auto; display: block; } </style> </head> <body> <canvas id="game" width="480" height="320"></canvas> <script src="game.js"></script> </body> </html> <p dir="auto">Wie wir sehen gibt es inhaltlich nur drei relevante Dinge: <ol> <li>das Styling für das Canvas <li>das Canvas selbst <li>das Script des Spiels <p dir="auto">Neben der <code>index.html erstellen wir noch die <code>game.js, in welcher unsere Logik schlummern wird: <pre><code>// get canvas and context const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); function draw() { // draw red circle ctx.beginPath(); ctx.arc(canvas.width / 2, canvas.height / 2, 10, 0, Math.PI * 2); ctx.fillStyle = 'red'; ctx.fill(); ctx.closePath(); // start (endless) game loop requestAnimationFrame(draw); } // initialize game loop requestAnimationFrame(draw); <p dir="auto">An diesem Punkt der Entwicklung haben wir die ersten Schritte geschafft und werden mit dem ersten sichtbaren Resultat belohnt: ein roter Ball auf einem grauen Spielfeld.<br /> <center><br /> <img src="https://images.hive.blog/768x0/https://stuff.flovolution.com/blog/game-dev_base-ball.PNG" alt="game-dev_base-ball" srcset="https://images.hive.blog/768x0/https://stuff.flovolution.com/blog/game-dev_base-ball.PNG 1x, https://images.hive.blog/1536x0/https://stuff.flovolution.com/blog/game-dev_base-ball.PNG 2x" /><br /> <h3>Bewegung <p dir="auto">Um den Ball in Bewegung zu setzen, erstellen wir vorerst ein paar Variablen und lagern den Code, um den Ball zu zeichnen, in seine eigene Funktion aus: <pre><code>// get canvas and context // ... const sizes = { ball: 10 }; const colors = { ball: '#ff0000' }; let positions = { ball: { x: canvas.width / 2, y: canvas.height - (sizes.ball * 3), }, }; let velocity = { ball: { x: 2, y: -2 }, }; function drawCircle(); { // draw red circle ctx.beginPath(); ctx.arc(positions.ball.x, positions.ball.y, sizes.ball, 0, Math.PI * 2); ctx.fillStyle = colors.ball; ctx.fill(); ctx.closePath(); } function draw() { drawCircle(); // start (endless) game loop requestAnimationFrame(draw); } // ... <p dir="auto">Das Ergebnis sollte ein roter Ball am unteren Ende des Spielfeldes sein.<br /> Damit sich der Ball nun noch bewegt ergänzen wir die <code>update Funktion: <pre><code>// get canvas and context // ... function update() { position.ball.x += velocity.ball.x; position.ball.y += velocity.ball.y; } function draw() { drawCircle(); update(); // ... } // ... <p dir="auto">Und nun bewegt sich der Ball. Aber was ist das?<br /> <center><br /> <img src="https://images.hive.blog/0x0/https://stuff.flovolution.com/blog/game-dev_base-movement.gif" alt="game-dev_base-movement" /><br /> <br /> Ich denke wir sollten unser Spielfeld nach jedem Zeichnen-Zyklus zurücksetzen. Dazu fügen wir einfach <code>ctx.clearRect(0, 0, canvas.width, canvas.height); als erste Zeile in die <code>draw Funktion ein.<br /> <center><br /> <img src="https://images.hive.blog/0x0/https://stuff.flovolution.com/blog/game-dev_base-movement-fix.gif" alt="game-dev_base-movement-fix" /><br /> <h3>Kollisions-Erkennung <p dir="auto">Jetzt wo sich der Ball bewegt, können wir uns der Kollisions-Erkennung widmen. Dazu prüfen wir einfach mit den uns zur Verfügung stehenden Variablen wo sich der Ball auf dem Spielfeld befindet und ändern je nach Standort die <code>velocity Variable. <pre><code>// get canvas and context // ... function update() { // check right & left wall if ( positions.ball.x + velocity.ball.x > canvas.width - sizes.ball || // right wall positions.ball.x + velocity.ball.x < sizes.ball // left wall ) { velocity.ball.x = -velocity.ball.x; // inverse velocity on x-axis } // check top wall if (positions.ball.y < sizes.ball) { velocity.ball.y = -velocity.ball.y; // inverse velocity on y-axis } // ... } function draw() { drawCircle(); update(); // ... } // ... <p dir="auto"><center><br /> <img src="https://images.hive.blog/0x0/https://stuff.flovolution.com/blog/game-dev_walls.gif" alt="game-dev_walls" /><br /> <h3>Der Spieler <p dir="auto">Nun fehlt uns noch der Spieler. Dazu erweitern wir unsere Variablen und erstellen uns eine neue Funktion, welche den Spieler zeichnet. <pre><code>// get canvas and context // ... const sizes = { ball: 10, paddle: { width: 75, height: 10 }, }; const colors = { ball: '#ff0000', paddle: '#0000ff', }; let positions = { ball: { x: canvas.width / 2, y: canvas.height - (sizes.ball * 3) }, paddle: { x: (canvas.width - sizes.paddle.width) / 2, y: canvas.height - sizes.paddle.height }, }; let velocity = { ball: { x: 2, y: -2 }, paddle: { x: 5 }, }; // ... function drawPaddle() { ctx.beginPath(); ctx.rect(positions.paddle.x, positions.paddle.y, sizes.paddle.width, sizes.paddle.height); ctx.fillStyle = colors.paddle; ctx.fill(); ctx.closePath(); } function draw() { drawCircle(); drawPaddle(); // ... } // ... <p dir="auto">Und schon haben wir mit wenig Code unser Spieler Objekt erstellt:<br /> <center><br /> <img src="https://images.hive.blog/0x0/https://stuff.flovolution.com/blog/game-dev_player.gif" alt="game-dev_player" /><br /> <h3>Spielerbewegung <p dir="auto">Als nächstes implementieren wir die Bewegung des Spielers. Dazu horchen wir einfach auf die Events <code>keydown und <code>keyup, um zu identifizieren, ob Tasten gedrückt wurden und ändern dann wie bei dem Ball die Position des Spielers im Spielfeld. <pre><code>// get canvas and context // ... let keyStates = { pressed: { left: false, right: false }, }; // ... function updatePaddle() { if (keyStates.pressed.left && positions.paddle.x > 0) { positions.paddle.x -= velocity.paddle.x; } if (keyStates.pressed.right && positions.paddle.x + sizes.paddle.width < canvas.width) { positions.paddle.x += velocity.paddle.x; } } function update () { // ... updatePaddle(); // ... } // ... function keyDownHandler(event) { if (event.keyCode === 37) { // left key-code keyStates.pressed.left = true; } else if (event.keyCode === 39) { // right key-code keyStates.pressed.right = true; } } function keyUpHandler(event) { if (event.keyCode === 37) { // left key-code keyStates.pressed.left = false; } else if (event.keyCode === 39) { // right key-code keyStates.pressed.right = false; } } document.addEventListener('keydown', keyDownHandler, false); document.addEventListener('keyup', keyUpHandler, false); // ... <p dir="auto">Mit ein wenig neuem Code können wir nun auch den Spieler bewegen:<br /> <center><br /> <img src="https://images.hive.blog/0x0/https://stuff.flovolution.com/blog/game-dev_player-movement.gif" alt="game-dev_player-movement" /><br /> <h3>Kollisions-Erkennung die zweite <p dir="auto">Was nun noch fehlt ist die Überprüfung, ob der Ball unseren Spieler berührt. Dazu lagern wir den alten Kollisions-Erkennungs-Code aus der <code>update Funktion aus und ergänzen ihn: <pre><code>// ... function updateBall() { // check right & left wall if ( positions.ball.x + velocity.ball.x > canvas.width - sizes.ball || // right wall positions.ball.x + velocity.ball.x < sizes.ball // left wall ) { velocity.ball.x = -velocity.ball.x; // inverse velocity on x-axis } const collideWithPlayer = ( positions.ball.y + velocity.ball.y > canvas.height - sizes.paddle.height - sizes.ball && positions.ball.x + velocity.ball.x > positions.paddle.x && positions.ball.x + velocity.ball.x < positions.paddle.x + sizes.paddle.width ); // check top wall && player if (positions.ball.y < sizes.ball || collideWithPlayer) { velocity.ball.y = -velocity.ball.y; // inverse velocity on y-axis } } // ... function update() { updateBall(); // ... } // ... <p dir="auto"><center><br /> <img src="https://images.hive.blog/0x0/https://stuff.flovolution.com/blog/game-dev_player-collision.gif" alt="game-dev_player-collision" /><br /> <h3>Zusatz <p dir="auto">Damit aus dem Hin und Her ein richtiges Spiel wird, ergänzen wir noch eine Punktzahl sowie einen Abbruch, wenn man den Ball nicht schafft zu erreichen: <pre><code>// ... let score = 0; let lost = false; // ... function updateBall() { // ... if (positions.ball.y < sizes.ball || collideWithPlayer) { // if collision score up and increase speed if (collideWithPlayer) { score += 1; velocity.ball.x += Math.random(); velocity.ball.y += Math.random(); velocity.paddle.x = Math.abs(velocity.ball.x * 2.5); // only positive numbers } velocity.ball.y = -velocity.ball.y; // inverse velocity on y-axis } else if (positions.ball.y - sizes.ball > canvas.height && !lost) { lost = true; alert(`You win with ${score} points!`); window.location.reload(); } } // ... function drawScore() { ctx.font = "30px Arial"; ctx.fillStyle = colors.text; ctx.fillText(`Score: ${score}`, 10, 30); } // ... function draw() { // ... drawScore(); // ... } // ... <p dir="auto">Und schon haben wir ein simples Einzelspieler-Spiel, geschrieben in purem JavaSript: <p dir="auto"><center><br /> <img src="https://images.hive.blog/0x0/https://stuff.flovolution.com/blog/game-dev_final.gif" alt="game-dev_final" /><br /> <p dir="auto">Der ganze Code der <code>game.js ist in diesem <a href="https://pastebin.com/Y8Tt9na2" target="_blank" rel="nofollow noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">Paste zu finden. <hr /> <p dir="auto"><strong>Du hast Fragen, Änderungsvorschläge oder Wünsche? Lass es mich in den Kommentaren wissen 😉<br /> In dem Sinne, frohes Coden.<br /> <br /><br /> <a href="https://steemit.com/@drookyn" target="_blank" rel="nofollow noreferrer noopener" title="This link will take you away from hive.blog" class="external_link"><img src="https://images.hive.blog/0x0/https://stuff.flovolution.com/blog/follow-@carlos-cabeza.gif" alt="credits:@carlos-cabeza" />
Sort:  

Sehr interessanter und ausführlicher Beitrag !
Freue mich wieder etwas von dir zu lesen :)



in diesem Beitrag.



Unter dem folgenden Link findest Du einige Anleitungen, die Dir den Einstieg in das Steem-Universum deutlich erleichtern werden: Deutschsprachige Tutorials für Steemit-Neulinge: Ein ÜberblickHallo @drookyn, herzlich willkommen auf Steemit.Wenn Du Fragen zu Steemit hast, oder Dich mit anderen „Steemians“ austauschen magst, schau einfach mal auf unserem Discord-Chat unter https://discord.gg/g6ktN45 vorbei. Mehr Informationen über den deutschsprachigen Discord-Chat findest Du Wenn Du auf Deutsch schreibst, verwende immer #deutsch als einen der 5 Hashtags, um Deine Reichweite zu erhöhen.