RL-Engine for JavaScript – ClojureScript

Skoro udało nam się przeportować RL-Engine do CLR to czemu nie zabrać się za JavaScript?

Ponownie, w teorii – wszystko co musimy zrobić to wziąć gotowy kod i przepuścić przez kompilator ClojureScript.

ClojureScript

Sam ClojureScript jest kompilatorem języka Clojure.
Tylko, że w inaczej niż w oryginale nie wypluwa kodu JVM tylko kod JavaScript.
Dodatkowo stara się nim pluć tak, aby wygenerowany Js był kompatybilny z Google Closure (Optymalizator).

Obecnie najnowszą wersją jest 1.8.51.

Kompilacja za pomocą ClojureScript

Tutaj będziemy musieli troszeczkę bardziej podłubać niż przy porcie do CLR.

Ominiemy sobie większość problemów z edytowaniem istniejącego projektu – stworzymy nowy.

lein new cljs-start rl-engine

Leiningen przygotuje nam podstawowy projekt ClojureScripta ze wszystkimi narzędziami jakie będą nam potrzebne.
Jedyne co poza tym potrzebujemy zrobić to pobrać PhantomJs.

PhantomJs jest “symulatorem” przeglądarki.

Symulatorem – ponieważ nie renderuje żadnego obrazu tylko umożliwia uruchamianie javascriptu w środowiskach bez-przeglądarkowych (np. konsola).

Ok, poza tym musimy dodać sobie naszego upiora do PATH. (inaczej leiningen nie będzie w stanie go znaleść)

Idąc dalej zaktualizujemy wszystkie zależności:

profiles.clj

:dev [:shared
       {:dependencies [[ring "1.4.0"]
                       [compojure "1.5.0"]
                       [enlive "1.1.6"]]
        :plugins [[com.cemerick/austin "0.1.6"]]

project.clj

:source-paths ["src/clj" "src/cljs" "src/cljc"]
:dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/clojurescript "1.8.51"]]
:plugins [[lein-cljsbuild "1.1.3"]]
{:builds {...
          {:source-paths ["src/cljc"
                          "src/cljs"]
...}

W pliku projektu (project.clj) pierwszą, szczególnie ważną, zmianą jest upgrade samego clojure do wersji od 1.7 wzwyż.

W wersji 1.7 pojawił się bardzo wyczekiwany feature: Reader Conditionals

W dużym skrócie polega on na tym, że możliwe teraz jest definiowanie makr zależnie od platformy.

Np.:

#?(:clj     Double/NaN
   :cljs    js/NaN
   :default nil)

Zależnie od docelowej platformy dostaniemy różny wynik.

Przypomina to trochę komendy pre-procesora dla C++ czy C#.

Następnie musimy zmienić rozszerzenia plików z .clj na .cljc – czyli z originalnego clojure na clojure ze wsparciem reader conditionals.

RL-Engine w JavaScript

Teraz dodamy trochę kodu odpowiedzialnego za renderowanie lochów wygenerowanych przez nasz silnik:

(defn draw-dungeon
  [height width]
  (let [dungeon ((rl-engine.dungeon-generator/get-generator "bsp") height width)
        div (.getElementById js/document "dungeon")]
    (do
      (set! (.-innerHTML div) "")
      (.appendChild div (.createElement js/document "TABLE"))
      (let [table (.-firstChild div)
            build-cell (fn [content row-element]
                         (let [cell (.appendChild row-element (.createElement js/document "TD"))]
                           (set! (.-innerHTML cell) content)))
            build-row (fn [content]
                        (let [row (.appendChild table (.createElement js/document "TR"))]
                          (doall (map #(build-cell % row) content))))]
        (doall (map build-row dungeon))
        nil))))

Pozostało nam tylko skompilować:

lein cljsbuild once

I odpalić w przeglądarce (index.html wygenerował nam template cljs-start w .\dev-resources\public)

7f8e40c7a0cb0eccde73f9c0ae31f45d
Render w przeglądarce

Nice!

Wszystko ładnie śmiga.

Wymagało to trochę więcej roboty niż przy porcie do CLR – jednakże, efekt jak najbardziej opłacalny.

Bonus

ClojureScript sam w sobie jest już całkiem dojrzałą technologią i ma solidnie rozwinięty wachlarz narzędzi.

Od synchronizacji kodu źródłowego z przeglądarką, poprzez minifikacje, przekierowania po REPL z kontekstem przeglądarki.

Ale jedno z ciekawszych (chociaż działa tylko z Chrome) narzędzi, jakie mnie zaskoczyło, to mapowanie wygenerowanego kodu w Js na nasz kod w Clojure:

Source map
Source map

A po co nam takie mapowania?

A no dzięki nim możemy debugować nie kod JavaScriptowy tylko kod w Clojure!

Sweet…

Świetna robota ClojureScript Team!

Be First to Comment

A penny for your thoughts