Entwicklungsumgebung und Workflow

Nach Abschluss des letzten Blogpost habe ich mich kurzfristig entschlossen meine Entwicklungsumgebung fit und nutzbar zu machen für alle die, die es mögen und möchten. Wer sich den ganzen Text und Infos sparen möchte, kann direkt auf Github oder NPM den Code ansehen und nutzen.

Basis ist NodeJS mitsamt NPM

Es gibt viele Programmiersprachen und Umgebungen mit Vorteilen und Nachteilen. Nachdem ich PhaserJS und damit JavaScript in der Entwicklung des Spiels nutzen werde, war es für mich ein leichtes auch JavaScript auf der Tooling Seite nutzen zu wollen.

JavaScript auf dem Server ist seit NodeJS ein wunderbares Werkzeug und die Entwicklungsumgebung für die Programmierung des Spiels ist eigentlich nichts anderes als ein Server der lokal läuft. Da wir aber nicht nur einen Webserver benötigen der Daten ausliefert, sondern auch ein Tooling das bei der Entwicklung des Spiels an allen möglichen Stellen helfen soll, nehme ich NodeJS nicht nur als Webserver, sondern als allgemeines Tool für sämtliche Aufgaben/Tasks. Wenn ihr mehr zu NodeJS erfahren wollt, dann hilft euch das Internet mit Millionen Treffern in der Suchmaschine des Vertrauens. Für diesen Blogpost ist jetzt nur wichtig: NodeJS ist Javascript und es steht vielen anderen Sprachen als allgemeine Tooling Plattform in nichts nach.

Tooling - was ist denn das?

Ich verwende immer das Wort Tooling für unterstützende Helfer beim Programmieren. Ein Tool ist ein Werkzeug und NodeJS ist quasi der Werkzeugkoffer, der fast jedes Problem lösen kann. Wo werde ich den Tools benötigen? Eigentlich kann man sagen, das jede Aufgabe die man öfters wiederholen muss, durch ein Tool ersetzen sollte das diese Aufgabe automatisiert durchführt.

Das automatisieren von Aufgaben funktioniert natürlich nur bei programmatischen Aufgaben, also kopiere Datei A in Ordner B oder reloade ein Fenster. Ein Tool, das mir automatisiert fantastische Grafiken für ein Spiel generiert habe ich noch nicht gefunden, aber sicherlich wird es in der Richtung bereits etwas geben.

Bleiben wir erst mal bei den doch einfachen Tasks. In dem letzten Intro Blogpost zu PhaserJS wurden die States vorgestellt und jeder State war in einer eigenen Datei gespeichert. Dann musste jede Datei in der HTML Datei einzeln referenziert werden damit wir damit arbeiten können. Jetzt könnte man auch einfach eine große JavaScript Datei erstellen und alle States dort rein packen, aber das wäre gegen meine Vorstellung von Organisation von Code und die Abgrenzung von Inhalten. Logisch wäre jetzt, wenn wir nach dem Erstellen der State Files diese zusammenfügen in eine Datei und dann diese eine Datei nur noch im HTML referenzieren. Klingt gut, ist einfach, aber wird beim zweiten Durchführen bereits lästig und damit haben wir auch schon unser erstes Tool definiert: Nehme alle JavaScript Dateien und füge diese in eine JavaScript Datei zusammen.

Es gibt weitere einfache Beispiele, die ich jetzt nicht alle benennen möchte, aber ein weiteres wäre: Sobald eine Datei geändert und gespeichert wurde, führe den oben beschriebenen Task automatisch aus (zusammenführen einzelner JS Files). Der nächste Task wäre: Wenn eine neue Zusammengeführte JavaScript Datei erstellt wurde, reload das Browserfenster.

Diese drei beschrieben Tasks würde dann während der Entwicklung des Spiels bedeuten, das ich Code schreibe und irgendwann speichere, direkt zu meinem Browserfenster wechseln kann und sehe den aktuellsten Code direkt im Browser laufen. Ohne Dateien anfassen, ohne F5 im Browser drücken.

Willkommen NodeJS

Die oben beschriebenen Tasks habe alle Webdesigner und Entwickler in einer Form in ihren Projekten und daher ist es kein Wunder das die JavaScript / NodeJS / WebDeveloper Community hier bereits ein unglaubliches Portfolio an Tools entwickelt und frei zur Verfügung gestellt hat. Da mein kleines Spiel auch ein Web Ding ist, können wir auf diese Tools 1:1 zurückgreifen.

Tools die verwendet werden:

  • Gulp
  • JSHint
  • Browserify
  • Jade
  • Stylus
  • UglifyJS
  • CSS Optimizer
  • ImageMin
  • LiveReload
  • Serve

Jeweils eine kurze Erklärung zu den Tools:

Gulp

Gulp ist ein Taskrunner, also ein Tool das ein oder mehrere definierte Tasks in einer Reihenfolge durchführen kann. In anderen Programmiersprachen sind das ANT, MAKE usw.

JSHint

JSHint ist ein Tool um grobe Schnitzer im JS Code aufzudecken und an zu mahnen. Es kann nicht jeden Fehler im Code entdecken oder logische Fehler erkennen, aber Tippfehler, ungültige Nutzung von Funktionen und Sprachkonstrukten werden erkannt. Das hilft sehr gut gegen komische Effekte und unerklärliche Erscheinungen im Spiel/App.

Browserify

Browserify ist unser Bundler, also das Tool das mehrere JavaScript Dateien zusammenführt und daraus eine JavaScript Datei macht. Es ist eigentlich noch viel mehr, aber als Erklärung soll das hier ausreichen. Beim Schreiben von JavaScript Code bedeutet das nur eine kleine Änderung und zwar das man das Prinzip CommonJS verwenden muss (module.exports).

Jade

Jade ist ein HTML Preprocessor und um das zu Erklären könnte man einen eigenen Blogpost schreiben. Kurzum: Wer schon öfters HTML Markup geschrieben hat der wird irgendwann keine Lust mehr haben dauern die <> Syntax zu verwenden. Bei mir ist der Punkt erreicht, an dem ich nichts mehr mit HTML Markup zu tun haben will, denn es nervt. Daher Jade als Helfer in der Not vor HTML Markup. Jade ist eine eigene Sprache und ermöglicht es mittels eine eigenen Syntax (ähnlich HTML) daraus dann valides HTML zu generieren. Anstatt also zu schreiben

<div id="myId" class="hello">Text</div>

kann man in Jade schreiben

div#myId.hello Text

und der Jade Preprocessor wandelt es in das obige HTML Fragment um. Herrlich!

Stylus

Stylus ist ebenfalls ein Preprocessor nur für CSS und es hat den gleichen Gedanken wie Jade für HTML: Weniger schreiben, mehr erreichen. Und hier ist direkt das vergleichende Beispiel für CSS versus Stylus:

CSS

#myId .hello {
  margin: 10px;
  position: relative;
  float: left
}

kann man in Stylus schreiben

#myId .hello
  margin 10px
  position relative
  float left

und es wird das gleiche CSS erzeugt ohne die Verwendung der Zeichen {};: usw. Auch hier gibt es unendlich viele weitere Features (die ich auch nutze), aber für die Vorstellung in diesem Blogpost nicht notwendig sind. Googlen und lesen...

UglifyJS

UglifyJS komprimiert JavaScript Dateien auf die möglichst kleinste Dateigröße. Das ist bei Webanwendung wichtig, denn der Ladevorgang von JS Files im Web (gerade im Mobile Bereich) erzeugt die längste Wartezeit. In der Zeit der lokalen Entwicklung des Spiels ist das Tool allerdings nicht zwingend notwendig.

CSS Optimizer

CSSO ist wie UglifyJS ein Optimierer allerdings für CSS. Es organisiert den CSS Code und kann dadurch doppelte oder ungültige CSS Definitionen erkennen und entfernen (Ziel: kleinerer Code).

ImageMin

ImageMin ist ebenfalls ein Optimierer für Bilddaten. Teilweise werden Grafiken von den erstellenden Tools nicht optimal gespeichert oder es ist ein gewollter Overhead in den Grafiken vorhanden. Da dies aber für den Enduser nicht das beste Erlebnis bietet, wenn ein Bild nur großer ist weil irgendein unnötiger Overhead die Ladezeiten der Webseite erhöht, wird das Tool verwendet um entgegenzuwirken.

LiveReload

LiveReload ist das Tool das den Browser automatisch refreshed, wenn eine bestimmte Aktion diesen Refresh ausgelöst hat. Das F5 drücken im Browser dauert zwar immer nur 2 Sekunden, aber hunderte Male manuell den Refresh anstoßen ergibt eine Menge Lebenszeit. Einmal genutzt will man es nie wieder missen.

Serve

Gulp-Serve ist ein lokalen Webserver für statische Dateien. Wie ganz am Anfang erwähnt benötigen wir einen Webserver der unser Spiel an den Browser ausliefert und diese minimale Tool macht es.

Kosten

Wenn man sich die ganzen Tools anschaut und was sie alles beherrschen könnte die Frage aufkommen, was das den alles kostet. Nicht das ich etwas dagegen hätte Geld auszugeben für Tools, die mir das Leben viel einfacher machen, aber wenn es kostenlos geht, sage ich nicht Nein.

Und das ist auch das gute an den ganzen Tools: Fast alle stehen unter der MIT Lizenz oder anderen freien OpenSource Lizenzen und die private und sogar kommerzielle Nutzung ist ausdrücklich erlaubt. Damit sind wir wieder bei einem Punkt, der bereits in einer der letzten Postings erwähnt wurde: Die "Share & Help" Mentalität der JavaScript Community!

Entwicklungsumgebung und Workflow

Nachdem ganzen Vorstellen von Tools und was sie alle für sich alleine können, kommt nun die Frage, wie das ganze nun im Zusammenspiel funktioniert. Ich habe die Tools bereits zusammengeführt und bestmöglich aufeinander abgestimmt und im Sinne des "Share & Help" Gedankens zur Verfügung gestellt auf Github, bzw. NPM

Wenn ihr das auch nutzen wollt, dann schaut euch einfach mal die README an. Dort ist beschrieben, was alles notwendig ist um das Tooling direkt nutzen zu können.

Für den Blog eine kurze Zusammenfassung: NodejS + NPM + Yeoman installieren, ein Order erstellen und meinen Generator laufen lassen. Fertig.

Wenn ihr den Generator nutzt, dann wird innerhalb kurzer Zeit ein Template für euer neues Game aufgesetzt. Eine Ordnerstruktur, die ganzen Tools mit den Tasks im Background.

Die Ordnerstruktur sieht dann wie folgt aus:

.
├── app                       // main app folder, contains Jade files
│   ├── images                // image folder for your website images, not game images
│   ├── js                    // main folder vor all your javascripts including game js files
│   │   ├── components        // components in your game
│   │   ├── prefabs           // prefabs in your game
│   │   └── states            // states folder with predefined states
│   └── stylesheets           // folder for the Stylus files
├── assets                    // main assets folder for your game assets
│   ├── audio                 // audio game assets, used when build game
│   ├── designs               // design folder, contains only design which are not used in the game
│   ├── images                // game image folder, which will be used when build
│   ├── js                    // extra JS files, which are not directly game related
│   └── tilemaps              // tilemaps folder
├── build                     // build folder will be created on each build
│   ├── assets                // assets copied from the /assets folder
│   │   ├── audio
│   │   └── images
│   ├── css                   // generated CSS files from stylus
│   └── js                    // copied JS files (game js files, included JS)
├── dist                      // distribution folder, all assets are optimized and minified
└── gulp                      // gulp folder with config
    └── task                  // js files with the definition of all tasks

Im Ordner app/js/states sind dann die einzelnen JavaScript State Files (boot.js, preload.js ...) und die werden durch die Tools automatisch bei jedem Speichern in den Ordner build/js zusammengeführt, optimiert usw. usf.

Mein Workflow mit diesem Template Generator lässt mich also sofort das Spiel entwickeln und ich muss keine Zeit mehr verschwenden um Dateien zu kopieren, zusammenzuführen oder F5 im Browser zu drücken.

Weitere Infos

Der Generator enthält ein kompletten Flow von boot zum gameover State mitsamt dem beispielhaften Laden von Bildern und Keybindings. Startet man also Gulp (indem man gulp in der Kommandozeilen ausführt) und den Browser auf die Webseite http://localhost:3000 führt, wird ein neues Phaser.Game initiiert und die States boot und preload durchlaufen. Den preload State wird man nicht wahrnehmen, da die Ladephase vom lokalen Server zu schnell erfolgt. Nach dem Ladevorgang wird dann im menu State ein einfaches Menu angezeigt mit einem Play Button, der beim klicken den play State startet. Der play State stellt einfach nur den sich drehenden Play Button da und soll zeigen, das man im play State ist. Beim drücken von ESC springt man in den gameover State und ein kompletter Flow durch das Phaser.Game ist erfolgt.

Natürlich dient das nur als Orientierung, wo und wann man etwas einfügen und programmieren kann. Für mich ist es immer eine Gedankenstütze und so soll es auch gesehen werden.

CommonJS und JS Code schreiben

Wie weiter oben geschrieben nutzen wir die Methodik CommonJS um unseren Code zu schreiben. CommonJS ist ein Best-Practice wie JS Code, primär durch NodeJS und die Entwicklung auf dem NodeJS Server, strukturiert sein soll, damit er mit NodeJS strukturiert geladen werden kann.

Grobe Zusammenfassung CommonJS:

  • es gibt Module
  • jedes Modul wird in eine eigene Datei abgelegt
  • jedes Modul hat einen Zweck/Funktion/Ziel
  • Module können untereinander eingebunden werden

Ziel des ganzen ist es den Code zu strukturieren und das Prinzip DRY (Dont Repeat Yourself) zu etablieren. Wenn wir Code haben der einen einfachen Zweck erfüllt, wird daraus Module-A erstellt. Haben wir ein Modul-B, das genaue diesen Zweck aus Module-A benötigt um zu funktionieren, können wir Module-A einbinden durch das Keywordrequire()`. Somit müssen wir den Zweck in Modul-B nicht neu entwickeln und bei Bugs in der Implementierung des Zwecks in Modul-A, müssen wir nur Modul-A korrigieren.

Beispiel (Zwei Dateien: modul-a.js und modul-b.js):

// module-a.js

function addTwo(num) {
  return num + 2;
}

module.exports = addTwo;
// end of modul-a.js
// -----
// modul-b.js

var add2 = require('./modul-a');
var val = 10;
console.log(add2(val)); // will print 12
// end of modul-b.js

In Modul-A müssen wir also sagen, welche Funktion (oder Variablen) exportiert werden mit module.exports. In Modul-B binden wir das Modul-A dann mit require('./modul-a') ein und können es dort nutzen.

Bei dem Spiel das ich hier entwickle nutzen wir dieses Prinzip natürlich aus, da ein organisierter Code der frei von redundanten Code einfacher schöner aussieht und viel einfacher zu warten ist. Um das ganze an den bisher bekannten States sichtbar zu machen (Beispiel boot.js State):


function Boot() {}

Boot.prototype = {
  // definition of preload(), create() et cetera
};

module.exports = Boot;

Diese reduzierte Version der in dem letzten Blogpost gezeigten boot.js Datei exportiert die Funktion Boot als Modul. In dem Blogpost habe ich dann die ganzen States einzeln im HTML Code durch das <script> HTML Tag eingebunden und dann später in der script.js genutzt. Mit CommonJS und dem automatisierten Workflow sieht die gleiche Datei nun wie folgt aus:

  window.onload = function() {
    var Boot = require('./states/boot');
    var Preload = require('./states/preload');
    // more states here

    var game = new Phaser.Game(960, 640, Phaser.AUTO, 'gamecanvas');

    game.state.add('boot', Boot);
    game.state.add('preload', Preload);
    // add more states here

    // boot the game
    game.state.start('boot');
  };

Dank des Workflows wird das ganze automatisch nun zu einer Javascript Datei gebündelt und diese eine Javascript Datei muss dann nur noch im HTML Code referenziert werden mittels <script> Tag. Easy!

Zusammenfassung

Die Zusammenfassung fällt mir etwas schwer, denn der Workflow ist für mich tägliches Brot und viele Punkte sind für mich selbstverständlich und ich denke darüber einfach gar nicht mehr nach. Da ich nicht weiß ob und wer meine Leser sind, versuche ich für ein Level zu dokumentieren, das auch ein Einsteiger in das Thema hoffentlich sich zurecht findet. Das wirkt zumindest für mich beim Gegenlesen des Textes teilweise sehr komisch, da ich nicht abschätzen kann, was noch Anfänger-Unwissenheit oder Profi-Wissen ist. Alles in allem hoffe ich jedoch, das es mindestens zum Nachdenken und selbst ausprobieren anregt oder man weitere Quellen sucht, sich mit dem Thema zu beschäftigen.

Zum Workflow kann ich zu diesem Zeitpunkt nicht mehr sagen, denn alles sollte gesagt sein. Wenn dieser Workflow und die Tools nicht gefallen, es gibt viele weitere dieser Yeoman Generators oder andere Optionen die man für den Einstieg nutzen kann. Es muss auch nicht NodeJS sein, sondern kann Python, Java, PHP sein.

Feedback via Twitter würde mich freuen :)