a technical blog

Arkham3D Post Mortem

with 3 comments

BGFH00LCYAAmSf6For last month’s One Game A Month competition I have been writing Arkham3D. I am not very happy with the end result even though I could be happy…. Let me explain.

The game is by far the most complete I have been developing lately (especially in that timeframe) but due to the time-constraints and other topics, I was not able to give it the final polish to make it as great as it could have become. For one thing, I discovered last minute that my asset-loader is broken and randomly is not able to load all textures and sounds correctly. I found that dropbox (where the game is hosted) seems to deny to many simulations connections and silently just blocks/kills them (the Chrome network tab just shows them as “pending” connections). Anyway, I was not able to give it the time to find and really fix this bug. That’s why the game randomly fails to load. You can see it from the comments.

Another thing I discovered last minute was related to the randomly generated levels; which was the most fun part to work at and definitely something I will spend more time on in the future. Sometimes the random level generation breaks; no idea why; also because I currently have no time to fix it.

So, even though “One Game a month” is a blessing for me to get something done, due to the time constraints it leads me to publish a game at the end of the month that is not the quality I wanted it to be. I know from other fellow participants that polish a game after the month and only release it then. I respect that and would love I could do so too, but the next, more interesting project, always seems to be waiting already. I guess this is just another level of “not getting things finished”.

Anyway, I would like to reflect on the game a little as I am quiet pleased with some core decisions:

  • Doing art yourself takes a lot of time. That’s why this time I decided to get the art done by someone else. I decided on a sprite-pack from Oryx Design Lab. The pixel-art-style is exactly what I personally would like to be able to do myself: minimal number of pixels, but maximum character. I had to modify the sprites slightly to get some movement and walls and floors I had to create myself. So, it was a good balance between doing some art but not wasting too much of time.
  • I used some sound packs from for the sounds of sword, pistol, enemies and footsteps and it gives some great depth to the game. I espacially like the sound of the pistol. Just amazing what a poor-mans pixely pistol-sprite and a great sound can create. I will definitively spend more time on this in the future. Also the atmospheric background sound track helps a lot. Thanks to DST for the great sounds.
  • I have been spending a lot of time on generating random levels as the theme for this month was “rogue-like” and randomness/replayablity is one major aspect defining this genre. I am quiet happy with my basic approach as it is very extenable and if i would have had more time, the levels would look much better then they do right now. I will describe it in a separate section below in case you are interested.
  • As the level generation is quiet brute-force and thus takes 2-3 seconds for a large world, I decided to use web workers for the first time so the browser does not lock up. If you don’t know it; it basically introduced a poor-man’s multi-threading concept to Javascript. One major limitation is that you only have messaging via strings. I.e. if you want to pass objects, you have to convert them in a JSON-string send it over and then “eval” it gain. For my purposes though, it fitted perfectly as the output of the world generator is a JSON-file as also generated by the Tiled editor.
    I also liked the include directives a lot: A web worker thread is basically spawned with a JS-file that should be executed. You don’t have a HTML-file with a lot of javascript included  Thus, you need another way to import dependent javascript files. The include directly similar to C/C++ is introduced for this. I really like it and would have hoped that it is taken over to regular javascript on the main thread as well…
  • I switched editors from Jetbrains Webstorm to Sublime Text 2. A really nice and responsive editor that works on Windows and Mac alike.

Random World Generation

The random world generation for Arkham3D uses quiet some brute force, but it turned to work just fine. If you look at the world from above, it is basically just a 2D-array of tiles with certain properties: walls, floors, hedges. And on some tiles, there are entities like monters or collectables (keys, health, ..).

The random world generation takes as an input just the width and height of the generated world and a seed for the random number generator to be able to reproduce the randomness. At some point the idea was to be able to share these seeds between friends. I.e. after you complete a world in a good time, you can mail a link to the game to a friend (containing the seed), and the friend can play the exact same level and challenge the time. This works as the randomly generated level is the same due to using the same seed. I might come back to this concept for some future game.

The algorithm for generating a random world of defined width and height is then as follows:

  • Randomly place dungeons/rooms of random size (obviously there is some min/max size) as far to the top and left of the map as possible. It has to be made sure the rooms do not overlay. If a room cannot be placed in the world as there is not enough space left, the room size is decreased one unit at a time and it tried again to place the room. Once no more room can be placed, the loop terminates
  • We now have a world full of disconected/closed rooms. The algorithm now looks for neighboring rooms and connects them by a portal. The connectivity information is stored in a graph structure.
  • After the previous step, we have a heavily connected undirected graph describing the connectivity between room. As each room connects to each neighboring rooms (if possible and there is no portal that obstructs the creation of another portal) and thus the world is not very challenging to explore. So, the next step is to run a breadth-first-search on the graph to create a spanning tree. I.e. portals unnecessary to connect the world have been removed. The world now is still connected but there is no two ways to get from a to b. Much more like a maze to discover now.
  • Now, we have a connected world/maze but the rooms all look alike. That’s why I defined certain “room-natures”. I.e. what floor and wall texture can be used together to form a room. These natures are now randomly applied to the rooms. So, one room is red-brick, one is gray brick, etc.
  • Additionally, to not just have empty rectangular rooms, we apply certain templates to the rooms randomly. I.e. if a room has a certain size, randomly make it have a seperating wall or centered inner room; or place some hedge-tiles randomly in the room. This allows for more interesting room designs and more place for monsters to hide.
  • We have treasures to hide. Ideally we want them to be locked behind a door that first has to be opened by a key. What the algorithm does is it randomly places the treasures in the leafs nodes of the spanning tree (the player has to discover the world before finding the treasures) and randomly walk up the spanning tree to the root. At some point place a door. Mark all child-nodes as looked by this door. So, if we place another door or key, we take a subtree that is not locked up yet. After all doors are placed, randomly place the keys in the area that is not locked up by a door. (could have made it more interesting by having chaines; i.e. a door unlocks an area with another key but I did not bother for now; worlds a are confusing enough).
  • Now, only the monsters and collectables are missing. Randomly place them in the rooms based on a cost metric. I.e. a room has a certain hardness and monters have a certain difficulty. So, place random monsters in a room until the maximum difficulty for that room is reached. E.g. 3 bats or 1 blue ghost. The difficulty increases with the player getting deeper the tree.
  • As a last step, we place the player spawn at the root of the spanning tree.

A randomly generated world with the overlaid spanning tree in white.

It was really interesting to use randomness to generate levels and not have to bother too much with level design more this game jam. You get instant replayablity if the generator is doing a good job.


I am not sure if I will be able to keep up with the pace of One Game a Month and not have something half-polished at the end of the month. Either I have to take it as what it is (a training and learning experience) or I have to set my goals lower. A full 2 1/2D game like Arkham3D is definitely nothing I will be trying to pull of in one month again.

Also, I will be focusing on development for the Oculus Rift in the next months and thus might not have time to churn out another game. It will be more of a “Research in VR” period.

Written by 38leinad

April 7, 2013 at 12:16 pm

Posted in postmortem

Tagged with

3 Responses

Subscribe to comments with RSS.

  1. Hi Daniel,
    im Oculus Rift Forum steht, dass Du in Paderborn wohnst?
    Ich studiere hier an der Uni Informatik und würde mir gern mal den Oculus Rift Developer Kit anschauen. Wäre das möglich?

    Bernd Messidat

    August 8, 2013 at 9:28 am

    • Hi, Klaro. Ich habe es gerade an einen kollegen ausgeliehen und frag ihn morgen mal ob er sich losreisen kann. Melde mich.

      Gruesse, Daniel

      Sent from Samsung Mobile.


      August 8, 2013 at 9:08 pm

      • Fein, danke!!

        Bernd Messidat

        August 9, 2013 at 6:54 am

Any thoughts?

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: