Introduction & TL;DR
In January I decided to start learning ClojureScript by implementing some 2d platform game mechanics. After
a little while I had a number of game mechanics and level editor features I
wanted to experiment with. By mid January I decided to make and release a
complete browser game with an integrated level editor.
This post contains some of my initial notes, screenshots and
comments on the process.
If you don't want to read a wall of text
I suggest a quick scroll down to the screenshot section.
Or you can play the game!
Game(Play) Features & Requirements
- 2d platform game with word puzzle element.
- Move from box to box to spell out a word or phrase. Boxes can have various
actions/effects in addition to letters (
a-z, space).
- Seamless integrated level editor. More below.
- Vanilla Clojurescript with as few libraries as possible. Probably just
for rendering
HTML. Write the game engine myself.
- Gravity and rotation as a central element to navigation and puzzle
design.
- Movement/controls must feel satisfying. It should be funny if you are
yeeted into the abyss. But it must be possible to master the
controls, so controls, physics & collision detection have to be consistent and
precise.
- Limited number of game mechanics that interact well. I.e. the first
version must feel like a complete game. All components must have a reason
for being there that has to do with game play.
- Variable jump height, and possibly wall jumps.
Editor Features & Requirements
- The result will probably be a level editor engine with a functioning game
attached to it. And I suspect I will enjoy building things in the editor more
than the average person.
- Snapping grid in editor. For emphasis on aligning boxes in levels. And to
reduce encoded/compressed world size. And because it feels nice to use.
- Editor has to be nearly seamless, easily accessible, and pleasant to use.
- Since there is no global world registry, make local configuration and;
- exportable/importable data/levels.
[incomplete]
Technical Observations & Limitations
- Only
CSS or SVG graphical elements; as few
elements as possible. Prefer simple things such as a CSS
border or gradient as opposed to an SVG.
- Use
DIV elements for building the game world, and for the
editor. Use browser mouse events for interaction with e.g. the level
editor.
- Add visual feedback slowly, and make it limited.
It will take some time to know what the game should
feel like. The visual feedback will help with this.
- Encode & compress entire world in
URL. Make
URLs shareable. (Hash level data for integrity and id.)
- Extending levels and level editor with new mechanisms should be possible
without breaking old URLs. Changing the encoding scheme in the future
should also be possible.
- Only use axis aligned rectangles in physics engine.
- Limit world size to fit on screen.
[*]
- No backend. I.e. no central score registry etc.
[*]
- No mobile support. No gamepad support. I didn't research this, but it
seems like it might be tricky to do well in the browser.
[*]
- No sound. I'm unfamiliar with most things sound. It would be very
interesting to me to try to add generative sounds to this in the future.
[*]
Progress & Screenshots
Some of the screenshots have dates that indicate when they were made. But I have
included screenshots here as much for their aesthetic as their informational value. Here we go.
readme.png
Quick list of possible box mechanics I wrote down when I started. Only a few made it into the finally game, but there are several more things I may want to try in the future.
20250107-120450.png
One of the first screenshots that show a basic, working platform mechanics. the Asterisk can be moved, and it has a jetpack of sorts.
20250108-234711.png
Boxes have colors (and id's) to distinguish events in the log.
20250114-184839.png
Initially the boxes were supposed to be black, and the board a black square. I quickly started experimenting with different backgrounds. Here you also see the initial/working title and the first logo attempt.
Some of the box labels have icons in them. The initial icons (you can see backspace and an arrow glyph in the screenshot) are from Fira Code. Which is the font used in the rest of the final game.
reticulated.png
The custom icons in the game are SVGs. Vector paths were drawn in Inkscape. Above is a screenshot of the cljs function that generates the SVGs on demand.
20250118-230851.png
More stuff ends up in the top bar. It changes quickly is I start to need more UI elements.
This is one of the first times when a level is fully playable and it is possible to reach a win state.
20250128-174137.png
Early parts of the level editor are visible. You can see tentative scaling handles and borders that indicate box type.
20250201-142324.png
Spawn point (+) is added to the board. Page background is black (or dark) from now on. We see borders on box labels. Borders are hard to distinguish, and they don't look that nice in many cases. Eventually I move on to use textures instead.
20250203-204513.png
More work on level editor. Here the box property selectors are visible in the bottom. The final version only has three different box properties. But here there are four as we seen in the bottom.
The left property selector is set to Visible. Earlier versions of the game had hidden boxes that would only display the outline when you were on them. Hidden boxes were removed from the final game.
The spiritual successor being the box that kills you instantly. Which serves a (vaguely) similar purpose in indicating that some area of the map is off limits.
20250205-203440.png
Early experimentation with different color style for edit mode. Eventually the board will have a dark (technically transparent, so you see the dark page background) background in play mode. And a light edit mode background.
20250214-194554.png
The first win modal was tied to the board, and its rotation. The final version is a little more user friendly. You can see it in later screenshots.
The level selector has been added to the top bar.
20250215-155719.png
Running some performance tests on a level with a recursive loop. There is no particular emphasis on performance optimization in the code. It runs pretty well considering the simplicity of the engine.
20250417-154355.png
Earlier version of the design with initial experiments with texture on the gravity modifier boxes (cyan, gray).
20250420-142443.png
This is close to the final design, but the top bar is less elaborate and the background is still white. Also the boxes still have border to indicate type in addition to the color code. In the final version boxes have texture and color.
User Interface & Local Storage
I decided to add some color and key binding configuration. The config is
stored in local storage along with level data and player statistics.
20250420-213400.png
An earlier version of the final config page. We see some of the EDN encoded data in local storage.
20250421-095503.png
Broken key binding config page.
20250422-113902.png
Broken color config. In the game you can configure the primary colors used to distingush box type (ice, low gravity) in addition to the box texture. The same (configurable) primary colors are also used in the page design.
Looking at the Final Result
Here are some screenshots of the final result.
final.png
Final design of the game showing one of the first levels (Box Types).
Notice the transparent textures for different gravity modifiers in the upper left (invert gravity) and right (low gravity) corners.
yay.png
Modal with icons and statistics for winning a level. The level was finished in 48.65 seconds, in 104 steps (touching 104 boxes in sequence). while tilting the world 0 times (manually).
texture.png
Close up of the textures on normal boxes (white), gravity modifiers and the page background (top half). The avatar is resting on the underside of the box because it is inside the inverse+half gravity area.
oh-no.png
Oh no ... You died in 1.83 seconds after touching one (1) box.
Summary
In summary I think this post is long enough at this point, but I hope you try the
game!
Another post with some more technical details about the level encoding is coming in the not too distant future.