Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Ninja Fight — HTML5/JS-Portierung

Ninja Fight ist ursprünglich ein 2D-Plattform-Kampfspiel, das in Adobe Animate / ActionScript 3 entwickelt wurde (Konzept & Umsetzung: Michael Dörflinger, Hochschule Furtwangen, Studiengang MIB, WS 2017/2018). Dieses Repo portiert das komplette, in der Abgabe unfertig gebliebene Spiel nach HTML5 Canvas + Vanilla JavaScript — lauffähig direkt im Browser, ohne Flash Player, ohne Build-Schritt, bereit für GitHub Pages.

Spielen

Ausgangslage

Das Original-Archiv (Endabgabe/) enthielt:

  • 35 AS3-Klassen (Source/Classes/) — viele davon nur Rümpfe ohne Verhalten (siehe unten)
  • eine ninja-fight.fla mit vier fertig gestalteten Levels (Level 1–4)
  • Original-Sounddateien, ein Konzept-PDF und ein "Known Bugs"-PDF, in dem der Entwickler selbst dokumentiert hat, welche Systeme nicht fertig wurden
  • drei praktisch identische Kopien desselben Projekts (Build/, Source/, Projekt/) — nur eine davon wurde als Quelle verwendet

Das Spiel war laut eigener Aussage nicht fertiggestellt. Diese Portierung repariert die im Known-Bugs-Dokument aufgeführten Probleme, statt sie unverändert zu übernehmen.

Was aus dem Original übernommen wurde

Asset Herkunft Verwendung
Level-Layouts (Level 1–4) aus LIBRARY/Level/Level1..4.xml der FLA extrahiert (DOMSymbolInstance-Positionen) 1:1 in assets/js/levels.js — exakt dieselbe Platzierung von Boden, Brücken, Leitern, Wasser, Feuer, Messern wie im Original
Sprachdaten (Englisch/Deutsch) ExternalData/strings.json, unverändert assets/js/strings.js
Sounds Sounds/*.mp3, unverändert assets/sounds/
Menü-Hintergrund eingebettetes PNG aus der FLA-Bibliothek (GUI/GUIComponent/background.png) assets/img/background.png
Charakter-Animationen (Held + 4 Gegnertypen) echte gerenderte Frames aus einem SWF-Decompiler (JPEXS FFDec) assets/img/sprites/{hero,blue,green,red,white}.png
Level-Elemente & Items (Floor, Bridge, Ladder, Knives, Flame, Sword, Shuriken, Heart) ebenfalls aus dem Decompiler-Export assets/img/sprites/tiles.png

Von der Farbpalette zu echten Sprites

In einer früheren Fassung dieser Portierung gab es keinen Zugriff auf die eigentlichen Grafikdaten — nur auf die FLA-Quelldatei, in der die Figuren als verschachtelte Vektor-Bodyparts ohne fertig gerenderte Bilder vorlagen. Die Lösung damals: die echten Fill-Farben aus den Shape-Definitionen extrahieren und eine eigene, prozedural gezeichnete Strichfigur einfärben.

Mit einem Decompiler-Export der SWF (decompiled.zip, per JPEXS FFDec) änderte sich das grundlegend: FFDec rendert für jede DefineSprite (= jedes MovieClip-Symbol) jeden einzelnen Frame als fertiges PNG. Für den Helden allein ergab das 465 Einzelbilder — jede Pose jeder Animation, pixelgenau wie im Original. Die Frame-Grenzen pro Animationszustand (Idle/Walk/Jump/Hit/Kick/Throw/Die/SwordHit) wurden aus den Frame-Labels in LIBRARY/Character/Hero/Hero.xml ausgelesen (z. B. index="57" duration="51" name="Jump") — identisch für alle vier Gegnertypen.

Verarbeitungspipeline (siehe Build-Historie, nicht Teil des Repos):

  1. Pro Figur (Hero, Blue, Green, Red, White) und pro Zustand 8 Frames gleichmäßig aus dem jeweiligen Original-Bereich abgetastet (465 Frames pro Figur wären für ein kleines Web-Spiel unnötig groß — 8 pro Zustand reichen für eine flüssige Wirkung).
  2. Jeder Frame auf ein einheitliches 160×150-Fenster zugeschnitten (deckt alle beobachteten Posen sicher ab).
  3. Alle Frames einer Figur zu einem Sprite-Sheet zusammengefügt (8 Spalten × 8 Zeilen, eine Zeile pro Zustand) — ein Bild pro Figur statt Hunderter Einzeldateien, deutlich weniger HTTP-Requests.
  4. Level-Elemente und Items (die meist nur 1 Frame haben, außer Flame mit 37 Animationsframes) ebenso in ein gemeinsames tiles.png gepackt.

Ergebnis: 5 Charakter-Sheets (je 150–200 KB) + 1 Kachel-Sheet (15 KB) statt einer selbst gezeichneten Näherung — die Figuren sehen jetzt exakt so aus wie im Original, inklusive des tatsächlichen Kopfbands, der Maskenfarbe und der Statur.

Die "2"-Varianten aus dem Original (Idle2, Walk2, … — vermutlich gespiegelte Versionen für die andere Blickrichtung) wurden bewusst nicht übernommen: SwordHit existierte im Original ohnehin nur in einer Richtung, daher wird hier durchgängig ein einfacher horizontaler Canvas-Flip (ctx.scale(facing, 1)) für die Blickrichtung verwendet — konsistent für alle Zustände und mit halb so vielen Bilddaten.

Architektur

/
├── index.html                  alle Bildschirme (Menü, Spiel, Pause, …)
├── assets/
│   ├── css/style.css            gemeinsames Stylesheet
│   ├── js/
│   │   ├── levels.js             extrahierte Original-Leveldaten
│   │   ├── strings.js            Original-Sprachdaten (EN/DE)
│   │   ├── spritedata.js         Sprite-Sheet-Manifest (welcher Frame liegt wo)
│   │   ├── render.js             Konstanten, Level-Aufbau, Sound, Sprite-Zeichnen
│   │   ├── entities.js           Hero, Enemy, PowerUp, Projectile
│   │   ├── gamemanager.js        Spiellogik, Level-Aufbau, Game-Loop
│   │   ├── ui.js                 Menüs, Einstellungen, Highscore
│   │   └── main.js               Boot
│   ├── sounds/*.mp3               Original-Audiodateien
│   └── img/
│       ├── background.png         Original-Hintergrundbild
│       └── sprites/
│           ├── hero.png, blue.png, green.png, red.png, white.png
│           │    (je 64 echte Original-Frames: 8 Zustände × 8 Posen)
│           └── tiles.png          Level-Elemente & Items (inkl. animiertes Feuer)
└── README.md

Die Klassenaufteilung folgt bewusst dem Original: Hero/Enemy entsprechen HeroController/*Controller.as, GameManager entspricht GameManager.as + Main.as, ui.js entspricht GUIController.as + den Menü-Klassen.

Behobene Fehler (aus "Ninja Fight_KnownBugs.pdf")

Das Original-Dokument listet 14 bekannte Probleme. Alle spielrelevanten wurden für diese Portierung behoben:

# Problem im Original Fix in dieser Portierung
1a Pause-Menü: btnResume.addEventListener(CLICK, Main.resumeGame()) ruft die Funktion sofort auf, statt eine Referenz zu übergeben — Resume funktioniert nie ui.js: echte Funktionsreferenz () => game.resumeGame()
1b ESC-Taste zum Pausieren war auskommentiert (//pauseGame();) in gamemanager.js aktiv verdrahtet
1c Shuriken-Wurfanimation lief, aber es wurde nie ein Projektil erzeugt Projectile-Klasse in entities.js, die sich durch den Raum bewegt und bei Treffer/Bildschirmrand verschwindet
1d Leiter-Klettern war nicht implementiert (keine Prüfung, ob der Spieler gerade eine Leiter berührt) Hero.update(): Leiterzone wird geprüft, W/S bewegen die Figur vertikal
1e Sprung funktionierte nicht zuverlässig (Bodenkollision widersprach der Sprungbewegung) vollständig neue, geschwindigkeitsbasierte Sprungphysik (siehe unten)
2 Keine Schadenskollision zwischen Held und Gegnern (nur Kommentare im Code, nie umgesetzt) echte Angriffs-Hitboxen für Hit/Kick/Schwert beim Helden, Kontakt-/Fernangriffe bei Gegnern
7 Highscore-Liste ließ sich nach dem Laden nicht scrollen entfällt durch natives HTML-Scrolling (overflow-y: auto)
12 Gegner-Verhalten war nie implementiert (*Controller.as sind leere Rümpfe) — keine Bewegung, keine Landung auf Plattformen einfache Patrouillen-KI mit derselben Schwerkraft-/Landungs-Logik wie beim Helden, plus Angriffe je nach Gegnertyp
14 Spielabsturz beim Tod durch Feuer, weil das Aufräumen mitten in der Kollisionsschleife des Helden selbst passierte Aufräumen wird per setTimeout() auf den nächsten Frame verschoben (siehe onHeroDeath())

Zusätzlich behoben, weil beim Portieren aufgefallen: In AnimationController.as lautete die Bedingung zum Sperren einer laufenden Animation currentAnimation != "Jump" || currentAnimation != "Jump2" || ... — eine Kette von !=-Vergleichen mit || ist immer wahr (ein String kann nicht gleichzeitig zwei verschiedene Werte NICHT sein), wodurch die Sperre nie griff. In dieser Portierung sperrt Hero.setState() korrekt: eine laufende Angriffs-/Sprunganimation kann nicht durch Bewegung unterbrochen werden (mit Tests verifiziert, siehe unten).

Original-Farbwelt bestätigt

Das Spiel spielt tagsüber vor einer freundlichen Wald-/Berg-Kulisse — nicht nachts, wie eine frühere Fassung dieser Portierung versehentlich annahm. Laut den Original-Leveldaten (Level1..4.xml) ist die GUI/GUIComponent/Background-Grafik (dasselbe helle Hintergrundbild wie im Hauptmenü) das erste Element in jedem einzelnen Level. Mit den jetzt verfügbaren Original-Sprites ist das ohnehin hinfällig — Boden, Wasser, Brücke, Leiter und Gefahren sehen exakt wie im Original aus, nicht nur farblich angenähert.

Nachträgliche Korrekturen (nach visuellem Feedback)

Problem Ursache Fix
Ganzes Spielfeld wirkte abgedunkelt, wie durch ein Overlay Die Basis-Regel .screen setzte einen dunklen Verlauf als Hintergrund für alle Bildschirme, auch #screen-game — der während des Spiels direkt über dem Canvas liegt #screen-game { background: none; } mit hoher ID-Spezifität, damit es nicht mit der .with-bg-Regel der Menüs kollidiert
Herz-Pickup wirkte überdimensioniert PowerUp-Icons wurden in Original-Sprite-Größe (35×30px) statt herunterskaliert gezeichnet einheitlicher Skalierungsfaktor 0.55 für Heart/Sword/Shuriken-Pickups
Flammen saßen zu tief/wirkten zu klein Flame.y in den Original-Leveldaten entspricht der Bodenhöhe (Basis der Flamme), nicht ihrer Oberkante — wurde als Top-Left-Position missverstanden Flamme wird jetzt mit der Unterkante an y verankert (wächst nach oben) und um Faktor 1.3 vergrößert
Keine erkennbare Kletter-Animation auf der Leiter Das Original hat keine eigene Climb-Animation (laut KnownBugs war das Klettern nie vollständig fertig) ersatzweise werden die Jump-Frames in Dauerschleife verwendet — optisch die am ehesten passende vorhandene Pose
Gegner wirkten wie eingefroren Patrouillenradius war mit ±70px winzig, Spawn-Positionen konnten sich gegenseitig überlappen Patrouillenradius auf ±220px erhöht, Spawn-Positionen gleichmäßig über die Levelbreite verteilt, außerdem gelegentliches Klettern und Springen während der Patrouille ergänzt

Weitere Ergänzungen (zweite Feedback-Runde)

Wunsch/Problem Umsetzung
Shuriken-Anzahl im HUD sichtbar machen neues HUD-Feld #hud-weapons zeigt Shuriken-Anzahl (✦) und ob ein Schwert getragen wird (🗡)
Lebensanzeige über Gegnern drawHealthBar() in render.js — schmaler grün/orange/rot abnehmender Balken über jedem Gegnerkopf
Leiter: am oberen/unteren Ende nicht normal weiterlaufen können Kletterlogik komplett neu geschrieben: Klettern greift nur, solange ausschließlich hoch/runter gedrückt wird — sobald links/rechts gedrückt wird, wird die Leiter sofort verlassen und normale Bewegung/Schwerkraft greift wieder
Keine Kletter-Sprites Das Original hat keine eigene Climb-Animation (nie fertiggestellt) — ersatzweise laufen die Jump-Frames in Dauerschleife während des Kletterns
Schwert nicht sichtbar, wenn getragen (nur bei Angriff) Im Original zeigt nur die SwordHit-Animation selbst das Schwert. Für Idle/Walk/Jump wird jetzt ein kleines Schwert-Sprite schräg auf dem Rücken ergänzt (showSheathed in drawNinja()), solange hasSword gesetzt ist und keine Angriffsanimation läuft — gilt für Helden und Gegner gleichermaßen
Gegner sollen Items einsammeln können PowerUp.update() prüft jetzt alle Charaktere (Held und alle lebenden Gegner) auf Kollision, nicht mehr nur den Helden — wer zuerst da ist, bekommt das Item. Gegner haben dafür eine eigene collectPowerUp()-Methode (Heart heilt, Sword/Shuriken schalten die jeweilige Fähigkeit frei)

Dritte Feedback-Runde: 10 Level + weitere Fixes

Wunsch/Problem Umsetzung
Lebensbalken saß im Kopf statt darüber Sprite-Anchor genauer vermessen (Kopf-Oberkante liegt bei y − 61px, nicht bei der Trefferbox-Höhe) — Balken jetzt bei y − 70px, klar über dem Kopf
Level endete nicht früher, wenn kein Gegner mehr da war Zwei Ursachen behoben: (1) Gegner, die von der Karte fallen, gelten jetzt als besiegt (onEnemyKilled() wird ausgelöst); (2) zusätzliche Sicherheit — fällt ein Gegner länger als 3s ununterbrochen (z. B. in eine nicht erreichbare Vertiefung), gilt er ebenfalls als besiegt
Schwert/Shuriken gingen bei Levelwechsel verloren Echter Bug gefunden: cleanUpLevel() setzte this.hero = null, bevor der Waffenstatus für die Übernahme ins nächste Level ausgelesen wurde — die Werte waren dadurch immer schon weg. Jetzt wird der Status vor dem Aufräumen gesichert
Nur 4 Level 6 neue Level ergänzt (jetzt 10 insgesamt). Level 5–10 sind neu von Hand entworfen (keine Original-FLA-Daten dafür vorhanden), mit Feuer, Leitern, Stacheln und Wassergräben, jeweils vollständig begehbarem Boden (automatisiert geprüft). Ab Level 5 sind die Gegnertypen gemischt (zufällig aus Blue/Green/Red/White gewählt), Level 1–4 bleiben bei ihrem Original-Einzeltyp. Gegneranzahl pro Level unterschiedlich: 5, 6, 5, 7, 6, 8

Vierte Feedback-Runde: Sieg-Bildschirm, KI, Kampfwerte

Wunsch/Problem Umsetzung
Nach Level 10 kam Game Over statt einer Sieg-Anzeige neuer #screen-victory-Bildschirm ("🏆 You Won!") mit eigenem Highscore-Eintrag, ausgelöst über GameManager.winGame() statt endGame(), wenn alle 10 Level ohne Tod abgeschlossen wurden
Gegner fielen zu oft von Plattformen Enemy.hasSupportAhead() prüft jetzt vor jeder Bewegung, ob vor der Figur noch Boden oder eine Leiter ist — ist das nicht der Fall, wird umgedreht statt weiterzulaufen. Zusätzlich: der zufällige Sprung wird nur noch ausgelöst, wenn auch vor dem Sprung Boden erkannt wird
Dabei gefunden: Leiter-Kollisionsbox reichte weit unter das eigentliche Bodenniveau mergeLadderColumns() addierte fälschlich sowohl die Kachelhöhe als auch eine komplette zusätzliche Kachelhöhe auf das untere Ende — die Leiter reichte dadurch bis zu 48px unter den Boden. Wer die Leiter am unteren Ende verließ, konnte dadurch unter die eigentliche Landefläche fallen. Behoben; alle 10 Level zusätzlich mit einem eigenen Test über 60 simulierte Sekunden pro Level verifiziert (0% Sturz-Tode)
Schwert war dauerhaft, sollte zeitlich begrenzt sein 5 Sekunden ab Aufnahme (SWORD_PICKUP_DURATION), danach automatisch wieder verloren — gilt für Held und gestohlene Schwerter bei Gegnern. Gegner mit angeborener Schwertfähigkeit (Red/White) behalten sie dauerhaft, das ist ihre Grundausstattung, keine Zeitbegrenzung
Alle Angriffe machten gleich viel Schaden gemeinsame Schadenstabelle: Schlag = 1, Tritt = 2, Shuriken = 5, Schwert = 10 — gilt symmetrisch für Held und Gegner
Alle Gegner hatten gleich viel HP (3, "nach 3 Tritten KO") HP jetzt an den Ursprungslevel gekoppelt, nicht an das aktuelle Level: Blue (Level 1) = 10 HP, Green (Level 2) = 20 HP, Red (Level 3) = 30 HP, White (Level 4) = 50 HP — bleibt auch in den gemischten Leveln 5–10 so
Gegner aus Level 1 konnten nur "Hit" alle Gegner wählen jetzt zufällig zwischen Schlag und Tritt im Nahkampf; Shuriken/Schwert bleiben exklusiv an die jeweilige Spezialfähigkeit gebunden (Blue: keine, Green: Shuriken, Red: Schwert, White: beides)

Fünfte Feedback-Runde: Feinjustierung

Wunsch Umsetzung
Schwert 30s statt 5s haltbar, Timer läuft erst ab dem Aufheben SWORD_PICKUP_DURATION von 5 auf 30 erhöht. Ein am Boden liegendes Schwert hatte ohnehin nie ein Ablaufdatum — der Timer startet ausschließlich beim tatsächlichen Aufheben
Gegner ohne Spezialfähigkeit sollen gestohlene Items trotzdem benutzen können war bereits korrekt umgesetzt (Angriffslogik prüft die veränderliche hasSword/hasShuriken-Eigenschaft, nicht die feste def.canSword/canShuriken) — mit eigenem Test verifiziert
Lebenspunkte des Helden pro Level unterschiedlich, aufaddierend statt zurückgesetzt this.lifeEnergy += 10 * levelNum statt = 10 * levelNum — Level 1 gibt +10, Level 2 +20, … Level 10 +100, jeweils zum Rest aus dem vorigen Level addiert. Bei einem echten Neustart (startGame()) wird auf 0 zurückgesetzt, damit die Formel wieder sauber bei 10 beginnt
Schwert/Shuriken müssen ins nächste Level übertragen werden war für hasSword/hasShuriken/shurikenCount bereits umgesetzt — ergänzt um den verbleibenden swordTimer (der 30-Sekunden-Countdown läuft jetzt nahtlos über Levelgrenzen hinweg weiter, statt beim Levelwechsel neu zu starten)

Sechste Feedback-Runde: Gegner leiden genauso wie der Held

Wunsch Umsetzung
Herz heilt auch Gegner war bereits umgesetzt (Enemy.collectPowerUp("Heart")) — mit Test bestätigt
Feuer/Stacheln verletzen auch Gegner neue Enemy.checkHazards(), spiegelbildlich zu Hero.checkHazards(): Feuer zieht 1 HP pro Kontakt (mit kurzer Abklingzeit), Stacheln 5 HP — Gegner sterben dadurch nicht sofort, sondern verlieren nur Lebenspunkte, wie gewünscht
Friendly Fire zwischen Gegnern (Schlag/Tritt/Schwert/Shuriken) Enemy.hitNearbyEnemies() prüft bei jedem Nahkampftreffer zusätzlich andere Gegner im Wirkungsbereich; Projectile wurde umgebaut, um die tatsächliche Werfer-Instanz zu kennen (statt nur "hero"/"enemy" als Text) und trifft jetzt jeden außer dem Werfer selbst — ein Shuriken kann dadurch versehentlich einen anderen Gegner treffen
Zeit abgelaufen + noch Gegner übrig = Niederlage vorher wurde ein Zeitablauf immer als "Level geschafft, weiter" gewertet, egal wie viele Gegner noch lebten. Jetzt: sind bei Zeit-Ablauf noch Gegner am Leben, ist das Spiel vorbei (Game Over) statt automatisch ins nächste Level zu wechseln

Siebte Feedback-Runde: Leiter-Schwerkraft korrigiert

Problem Fix
Beim Klettern rutschte man herunter, sobald man hoch/runter losließ, obwohl man noch mitten auf der Leiter stand Derselbe Fehler wie im Begleit-Tutorial (Kapitel 9): die Schwerkraft wurde nur aufgehoben, solange aktiv hoch/runter gedrückt wurde. Jetzt gilt: solange man sich in der Leiterzone befindet (und nicht seitlich aussteigt), ist die Schwerkraft grundsätzlich aufgehoben — man hängt an einer Sprosse fest, auch ohne eine Taste zu drücken, und bewegt sich nur, wenn man aktiv hoch oder runter drückt. Mit eigenem Test verifiziert (hero.vy bleibt 0, Position bleibt exakt gleich, solange man in der Zone ist und keine Taste drückt)

Notwendige Anpassungen für GitHub Pages

  • Server-Highscore → localStorage. Das Original schickte Highscores per URLLoader an einen PHP-Server auf dem Hochschul-Webspace (saveScore.php/highscore.php). GitHub Pages kann kein PHP ausführen — die Highscore-Liste wird deshalb lokal im Browser gespeichert (localStorage). Das Prinzip (Name, Punkte, Level speichern und sortiert anzeigen) bleibt identisch, nur ist die Liste jetzt pro Gerät statt global.
  • Sprung-/Bewegungsphysik neu geschrieben. Statt die fehlerhafte Original-Physik (deltaY += 0.125, kollidierende Prüfschleifen) zu reparieren, wurde eine saubere, geschwindigkeitsbasierte Physik verwendet (konstante Erdanziehung, Sprunggeschwindigkeit, Landungserkennung über Plattform-Kacheln) — dasselbe Muster wie in den beiden vorigen Repos dieser Reihe.

Testen

Da dieses Spiel mit echten Fehlerbehebungen wirbt, wurden die kritischen Systeme mit einem headless Node-Test verifiziert (Sprung, Leiter, Schuriken-Treffer, Nahkampf-Treffer, Pause/Resume-Einfrieren, Level-Fortschritt über alle 4 Level, Tod ohne Absturz, Highscore-Speicherung). Alle Tests laufen grün. Der Testcode ist nicht Teil des Repos (reines Entwicklungswerkzeug), aber die Ergebnisse sind in den Commit-Notizen nachvollziehbar.

Steuerung

Taste Aktion
W/, S/ Leiter hoch/runter
A/, D/ Laufen
Leertaste Springen
R Schlagen
F Treten
E Schwert (wenn eingesammelt)
Q Shuriken (wenn eingesammelt)
ESC Pause

Lokal ansehen

python3 -m http.server 8000
# dann: http://localhost:8000

(Ein lokaler Server ist nicht strikt nötig, da diese Version ohne fetch()-Aufrufe auskommt — funktioniert auch direkt per file://.)

Deployment über GitHub Pages

  1. Repo auf GitHub anlegen (z. B. NinjaFight) und diesen gesamten Ordner hineinpushen — index.html muss im Root-Verzeichnis liegen.
  2. Im Repo zu Settings → Pages gehen.
  3. Unter "Build and deployment" → Source "GitHub Actions" auswählen.
  4. GitHub schlägt daraufhin die Workflow-Vorlage "Static HTML" vor — diese auswählen und committen (erzeugt automatisch .github/workflows/static.yml).
  5. Warten, bis der Workflow unter Actions grün durchläuft (Build + Deploy).
  6. Die Seite ist danach unter https://<dein-username>.github.io/<repo-name>/ erreichbar.

Falls der Deploy-Schritt mit "Deployment failed, try again later" fehlschlägt, obwohl der Build erfolgreich war: einmal Re-run all jobs im fehlgeschlagenen Workflow-Run ausführen — das behebt laut den GitHub-eigenen Diskussionen dazu die meisten Fälle, da es sich meist um ein kurzzeitiges Problem auf GitHubs Seite handelt, nicht um einen Fehler in eurem Repo.

Quelle

Original-Konzept und ActionScript-3-Umsetzung: Michael Dörflinger, Hochschule Furtwangen (Studiengang MIB), Vorlesung "Spieleentwicklung 2D", WS 2017/2018. Diese Portierung ersetzt Animate/Flash vollständig durch HTML5 Canvas + Vanilla JS und behebt die im Original dokumentierten Fehler.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages