HTML 5 - Spiel programmieren (Live Demo)

HTML 5 - Spiel programmieren (Live Demo)

In diesem Artikel wird die Entwicklung eines einfachen 2D HTML5 Spiel, welches mit purem Javascript geschrieben wurde, Schritt für Schritt erklärt. Es wird elementares Wissen in Javascript vorausgesetzt.

Wie es am Ende aussieht

screen1.png

Was soll es können?

In dem Spiel gibt es einen Hauptcharakter, der mit den Tasten W, A, S, D gesteuert wird und mit der linken Maustaste einen Feuerball abfeuern kann. Das Ziel ist es Gegnerwellen zu besiegen, welche mit jedem Level größer werden.

Todo

  • Eine Hintergrund Grafik
  • Eine Charakter Grafik
  • Eine Feuerball Grafik
  • Eine Gegner Grafik
  • Laden der Grafiken
  • Grafiken zeichnen
  • Erkennung von Tastatur und Maus Eingabe
  • Feuern unseres Feuerballs in Mauszeiger Richtung
  • Generieren von Gegnern
  • Kollisions- und Spiellogik

Unsere Grafiken

Ich habe ein paar simple Grafiken erstellt, die hier für das Spiel verwendet werden.

background.png character.png enemy.png shot.png

Das Grundgerüst

Fangen wir mit der HTML Datei an (index.html):

<!DOCTYPE html>
<html>
   <head>
   </head>
   
   <body>
      <canvas id="canvas" width="800" height="600">Kein Support</canvas>
      <script type="text/javascript" src="game.js"></script>
   </body>
</html>

Das HTML5 Dokument besteht hauptsächlich aus einem HTML5 Canvas, welches wir später mit der eingebundenen game.js ansteuern werden.

Kommen wir zum Eingemachten. Wir erstellen nun die erwähnte Javascript Datei. Die game.js! In diesem Tutorial arbeiten wir nur mit einer einzigen Javascript Datei, natürlich wäre es sinnvoller und auf Dauer bei größeren Projekten übersichtlicher einige Dinge in mehreren Dateien auszulagern und "Klassen" zu schreiben, aber einfachheitshalber bleiben wir hier bei einer Javascript Datei mit simplen Funktionen.

1. Ansprechen des HTML 5 Canvas Elements

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');


Wir greifen mithilfe der getElementById Funktion und der oben vergebenen id "canvas" auf das Canvas Element zu. Dann erstellen wir einen 2D Kontext, in dem wir später unsere Grafiken zeichnen werden.

2. Nötige Variablen hinzufügen

var backgroundImg = new Image();
var enemyImg = new Image();

var character = null;
var fireball = null;

var enemies = {};
var keysDown = {};

var frametimeBefore = Date.now();
var level = 0;

Mehr als das benötigen wir nicht. Wir haben zwei Image Objekte, einmal für die Hintergrund- und einmal für die Gegner Grafik. Dann haben wir ein Charakter Obekt und das Feuerball Objekt, welches der Charakter abfeuern kann. Die Gegner, welche wir später zum Level hinzufügen, kommen alle in das enemies Array und die Tastatur Eingaben in das keysDown Array. Warum wir die Tastatureingaben in ein Array packen, wird später erklärt. Zum Abschluss haben wir einmal eine Variable, die wir für die Berechnung der Frametime benötigen und eine Level Variable, in der wir das aktuelle Level speichern.

3. Laden der Grafiken und erstellen der essentiellen Objekte

function init()
{
    character = { img: new Image(), posX: 200, posY: 200, shooting: false, hp: 1000, speed: 300 };
    shot = { img: new Image(), posX: 0, posY: 0, dirX: 0, dirY: 0, speed: 600 };
    
    backgroundImg.src = 'images/background.png';
    character.img.src = 'images/character.png';
    shot.img.src = 'images/shot.png';
    enemyImg.src = 'images/enemy.png';
}

Wir erstellen das Charakter Objekt und den Feuerball, diese bestehen jeweils aus einem Image Objekt, der Position und der Bewegungsgeschwindigkeit in px/s. Im Charakter Objekt speichern wir zusätzlich den Status, ob gerade ein Feuerball aktiv ist und die Lebenspunkte des Charakters. Im Feuerball Objekt speichern wir zusätzlich die Richtung, in der sich dieser bewegt. Dann laden wir alle Grafiken, indem wir das src Element der Image Objekte setzen.

4. Zeichnen von Grafiken und Text

function draw()
{
    ctx.drawImage(backgroundImg, 0, 0);
    
    if(character.hp > 0)
    {
        for(var i = 0; i < enemies.length; ++i)
        {
            ctx.drawImage(enemies[i].img, enemies[i].posX, enemies[i].posY);
        }

        ctx.drawImage(character.img, character.posX, character.posY);

        if(character.shooting)
        {
            ctx.drawImage(shot.img, shot.posX, shot.posY);
        }
    }
    
    ctx.font = "20px Verdana";
    ctx.fillStyle = 'white';
    ctx.fillText("Level: " + level, 20, 30)
    ctx.fillText("HP: " + Math.ceil(character.hp), 20, 60);
}

Da wir alle Grafiken geladen haben und alle Objekte erstellt sind, können wir diese schon zeichnen. In der ersten Zeile zeichnen wir den Hintergrund an der Position (0, 0). Dann zeichnen wir alle Gegner, den Charakter und den Feuerball (sofern dieser Aktiv ist, also abgefeuert wurde). Gegner, Charakter und Feuerball werden nur gezeichnet, wenn der Charakter mehr als 0 Lebenspunkte hat, also noch lebt. Dann zeichnen wir noch zwei Texte, einmal einen der uns das Level und einmal einen der uns die aufgerundeten Lebenspunkte des Charakters anzeigt. Warum hier gerundet wird, wird später noch ersichtlich.

5. Die Spielschleife

Vielleicht auch interessant
Gether - Final (Full Code)
Gether - Final (Full Code)

Gether - Kooperatives Multiplayer Game - Full Code

Damit auch wirklich gezeichnet wird und etwas passiert, müssen wir die erstellten Funktionen aufrufen. Die init Funktion soll nur einmal aufgerufen werden, während die draw Funktion so oft wie möglich aufgerufen werden soll. Dazu erstellen wir eine Funktion die als Spielschleife dient, wo später auch die Frametime Berechnung und die Logik des Spiels hinzu kommt.

function gameLoop()
{
    draw();
}

init();
setInterval(gameLoop, 0);

Wir erstellen also die Funktion gameLoop und rufen diese mithilfe von setInterval ohne Verzögerung (die 0 steht für 0 Millisekunden Pause bis zum nächsten Aufruf von gameLoop) ständig auf. Davor rufen wir die vorher erstellte init Funktion auf, welche die Grafiken lädt, Objekte erstellt usw. Die momentane game.js sieht also nun insgesamt so aus:

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var backgroundImg = new Image();
var enemyImg = new Image();

var character = null;
var fireball = null;

var enemies = {};
var keysDown = {};

var frametimeBefore = Date.now();
var level = 0;

function init()
{
    character = { img: new Image(), posX: 200, posY: 200, shooting: false, hp: 1000, speed: 300 };
    shot = { img: new Image(), posX: 0, posY: 0, dirX: 0, dirY: 0, speed: 600 };
    
    backgroundImg.src = 'images/background.png';
    character.img.src = 'images/character.png';
    shot.img.src = 'images/shot.png';
    enemyImg.src = 'images/enemy.png';
}

function draw()
{
    ctx.drawImage(backgroundImg, 0, 0);
    
    if(character.hp > 0)
    {
        for(var i = 0; i < enemies.length; ++i)
        {
            ctx.drawImage(enemies[i].img, enemies[i].posX, enemies[i].posY);
        }

        ctx.drawImage(character.img, character.posX, character.posY);

        if(character.shooting)
        {
            ctx.drawImage(shot.img, shot.posX, shot.posY);
        }
    }
    
    ctx.font = "20px Verdana";
    ctx.fillStyle = 'white';
    ctx.fillText("Level: " + level, 20, 30)
    ctx.fillText("HP: " + Math.ceil(character.hp), 20, 60);
}

function gameLoop()
{
    draw();
}

init();
setInterval(gameLoop, 0);

Und das momentane Ergebnis sollte so aussehen:

screen2.png

Da das vielleicht etwas viel auf einmal ist, habe ich diesen Artikel in zwei geteilt. Im zweiten Teil kümmern wir uns um das wichtigste, die Spielmechanik.

Nächste Seite
Finale
Hinterlasse gerne einen Like oder Kommentar (~‾▿‾)~
Name Text
Kommentare
Simon Hennig> 6 Mt.SUPER
Wladimir Schreiber> 1 JDu hast bei der "Die Spiellogik / logic" ein " } ". Bitte verbessere es wenn du es kannst.
Erich> 1 J@CodeTastisch: Das liegt daran, dass in deinem dritten EventListener "shoot" steht. Die Fehlermeldung lautet "shoot is not defined". Außerdem am Ende der Zeile << ctx.fillText("Level: " + level, 20, 30) >> ein Semikolon.
CodeTastisch> 2 JHey bei mir geht es null : html: <!DOCTYPE html> <html> <head> <title>Apfel vs. Twix | HTML Game</title> </head> <body> <canvas id="canvas" width="800" height="600">Kein Support</canvas> <script type="text/javascript" src="Game.js"> </script> </body> </html> js: var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var backgroundImg = new Image(); var enemyImg = new Image(); var character = null; var fireball = null; var enemies = {}; var keysDown = {}; var frametimeBefore = Date.now(); var level = 0; function init() { character = { img: new Image(), posX: 200, posY: 200, shooting: false, hp: 1000, speed: 300 }; shot = { img: new Image(), posX: 0, posY: 0, dirX: 0, dirY: 0, speed: 600 }; backgroundImg.src = 'images/HG.png'; character.img.src = 'images/character.png'; shot.img.src = 'images/shot.png'; enemyImg.src = 'images/enemy.png'; window.addEventListener("keydown", function(e) { keysDown[e.keyCode] = true; }, false); window.addEventListener("keyup", function(e) { delete keysDown[e.keyCode]; }, false); window.addEventListener("click", shoot, false); } function draw() { ctx.drawImage(backgroundImg, 0, 0); if(character.hp > 0) { for(var i = 0; i < enemies.length; ++i) { ctx.drawImage(enemies[i].img, enemies[i].posX, enemies[i].posY); } ctx.drawImage(character.img, character.posX, character.posY); if(character.shooting) { ctx.drawImage(shot.img, shot.posX, shot.posY); } } ctx.font = "20px Verdana"; ctx.fillStyle = 'white'; ctx.fillText("Level: " + level, 20, 30) ctx.fillText("HP: " + Math.ceil(character.hp), 20, 60); } function gameLoop() { draw(); } init(); setInterval(gameLoop, 0);