Jach's personal blog

(Largely containing a mind-dump to myselves: past, present, and future)
Current favorite quote: "Supposedly smart people are weirdly ignorant of Bayes' Rule." William B Vogt, 2010

Clojure and jMonkeyEngine Tutorial 2

Another simple translation to Clojure code. There are some repetitions that are popping up that we might want to abstract into functions or macros, but so far it's fairly pedestrian.

As a philosophical side note, defining a new function in Java takes effort. It's no wonder it's not done as much. You have to pick a class to put it in, you have to decide its class-scoping, you have to specify its argument and return types, any thrown exceptions, decide if it should be part of an interface somewhere... In practice, code changes. There's nothing wrong with type checking at compile time, but that's only useful when you're reasonably sure the code being checked isn't going to fundamentally change.

Anyway. Here's the code for tutorial 2:

(ns jme_clj.core
(:import [com.jme3 app.SimpleApplication

(def desktop-cfg (.getResource (.getContextClassLoader (Thread/currentThread))

(def assetManager (JmeSystem/newAssetManager desktop-cfg))

(def ^:dynamic *app-settings* (doto (AppSettings. true)
(.setFullscreen false)
(.setTitle "jme_clj")))

(def app (proxy [SimpleApplication] []
(simpleInitApp []
(org.lwjgl.input.Mouse/setGrabbed false)
(let [b1 (Box. (Vector3f. 1 -1 1) 1 1 1)
blue (Geometry. "Box" b1)
mat1 (Material. assetManager
b2 (Box. (Vector3f. 1 3 1) 1 1 1)
red (Geometry. "Box" b2)
mat2 (Material. assetManager
pivot (Node. "pivot")]
(.setColor mat1 "Color" ColorRGBA/Blue)
(.setMaterial blue mat1)
(.setColor mat2 "Color" ColorRGBA/Red)
(.setMaterial red mat2)
(.attachChild (.getRootNode this) pivot)
(doto pivot (.attachChild blue) (.attachChild red))
(.rotate pivot 0.4 0.4 0)))))

(defn -main [& args]
(doto app
(.setShowSettings false)
(.setPauseOnLostFocus false)
(.setSettings *app-settings*)

One thing that's never been fully explained to me: what is the obsession game programmers have with floating point values? Yes they are twice as small and the extra precision of doubles isn't always needed, but the verbosity of the code to have little f's everywhere doesn't seem worth it for such a micro optimization. Ah well.

The interesting thing in the tut 2 is the introduction of a pivot node. So you make two boxes, each with a position and radius in each dimension. If these two boxes are related intimately, for example a player character and a hat, it makes sense to put them together in a single node and make your transformations on that node which will then affect all the sub-nodes at once. In this case, rotating both boxes to be at the same angle.

Let's look at how a Clojure developer might play with what we've learned at this point. I still consider myself a newb in both Clojure and JME, but REPL-driven development alleviates that considerably. Instead of a "write, compile, run, be disappointed, start over" development cycle, I write, evaluate, see what happens immediately without having to run everything else again, and either rewrite what I just wrote and reevaluate just that one expression, or I continue on. I get live feedback in the development process and it lets me explore while I'm programming.

I made a quick (well, 12 mins) video playing around with this example. Don't watch it all, just skip around. (Especially near the end.) I don't typically talk when I'm coding, so I'm unpracticed with this type of video. Feel free to mute the audio if you don't want to hear me mumbling about what I'm doing. I tried to edit my voice so that it's at least somewhat bearable.

There's a lot I didn't show in the video, like moving cubes around dynamically and so on. The point is, you can build iteratively and save off what you liked to further build from. If you have an API reference handy, it's a great way to explore the API and see what's possible. If you use an editor like Eclipse (or setup your vim to do the same thing) you can even have tab completion for method calls. But changing things dynamically and on the fly is part of what makes Clojure, Clojure, and if you've never done so before as a developer you're missing out.

On to 3

Posted on 2012-05-14 by Jach

Tags: clojure, jMonkeyEngine, programming


Trackback URL:

Back to the top

Back to the first comment

Comment using the form below

(Only if you want to be notified of further responses, never displayed.)

Your Comment:

LaTeX allowed in comments, use $$\$\$...\$\$$$ to wrap inline and $$[math]...[/math]$$ to wrap blocks.