The jadening

Bolting Scalatra on to the existing hexmap project turned out to be fairly easy, though I had the advantage of having an existing minimal project already (the nascent random-album-cover project).  My idea for this project is to have nearly all the server transactions handled through Ajax rather than form posts and server-side templating, but while I was getting the directory structure up and running I took the opportunity to translate the existing HTML that I developed for the hexmap into Jade.

My initial impulse, actually, was to use Mustache, and avoid rewriting the HTML altogether.  However, I found the layout documentation for scalate Mustache to be basically incomprehensible, and if I’m understanding it correctly, it requires you to insert extra stuff into your layout file and still not remove anything from your content file, which doesn’t exactly seem like it’s heading in the right direction as far as templating system design.  Besides, using Jade lets me pretend that I live in a universe where SGML-derived syntaxes never became something that almost every working programmer would have to deal with almost every day.

The downside of this is that I’m no longer able to monkey with the client-side code without a running jetty instance, but I think that should be OK for the near future.  The upside is that the code for index.html has shrank down to a svelte 36 lines.

At any rate, I’ve got the client code retrieving and painting a map from the server now.  My server-side map database currently looks like this:

  get("/map/new.json") {
    contentType = "application/json"
    """{ "map" : [["blue", "brown", "yellow"],
                  ["lightgreen", "yellow", "blue"],
                  ["grey", "grey", "darkgreen"]] }"""
  }

It’s a start. I’ve also pulled in the Lift-JSON libraries, which look to be useful. The next step is to implement a few different map stores, hook them up to the client, and test the output. After that, I need to figure out a way to populate actually interesting map data in a persistent store (presumably Mongo). The idea which keeps nagging at the back of my mind is that the existing code could probably be used as the start of a dynamic map editor, but I’m not positive I want to head down that route.

Javascript hexmap display

hexagon map

After a good bit of head-scratching I’ve managed to make some progress on what I assumed was going to be one of the more difficult aspects of the hex-map project: actually displaying the hexes in a browser.  This also turned out to be a good opportunity to finally dig into jQuery a little more deeply, too.  I’ve come up with a rough idea of how to proceed from here, too; I’ll have the page make Ajax calls out to Scala to retrieve map details in JSON and it should be pretty easy to dynamically construct the hex grids from there.

Github, probably wisely, doesn’t display checked-in HTML directly in the browser, but I’ll attach a screenshot here; interested parties can check out the code on github under src/main/webapp.  Currently the page generates a new random map on every page refresh, with each map being a mixture of roughly 2/3 desert tiles and 1/3 ocean tiles.  The data structure is a simple 2-dimensional array:

 var map = [["desert", "ocean", "desert"],
            ["desert", "ocean", "desert"],
            ["desert", "desert", "ocean"]]

Obviously for a full game implementation the elements would be complex JSON structures, not strings, but this suffices for now.  To tile the hexes together, I’m simply laying them on top of one another via CSS absolute positioning; each image is a rectangle (actually, a square) with the diagonal edges cut out of the sides.  I was a little worried this would make it cumbersome to attach jQuery event handlers to the right places, but I currently have a handler that displays the (x,y) coordinates of a hex when a user mouses over one and it seems very usable, if not perfect around the edges of hexes.

The code I’m using to position the tiles looks roughly like this (note: some cleanup is still pending in the actual code; what follows is the gist but see the code itself for details).  Here tile is a string from the previous array, and x and y are row / column numbers.

function placeTile(tile, x, y) {
  var tile$ = $("." + tile, "#templates").children().clone();

  tile$.hexMapPosition(x, y).appendTo($('#hexmap'));
}

This code clones images based on the tile name from the map data structure. The images themselves are stored in a hidden div:

<div id="templates" class="hidden">
  <div class="tiles desert">
    <img src="images/tiles/desert.png" width="72" height="72"/>
  </div>
  <div class="tiles ocean">
    <img src="images/tiles/ocean.png" width="72" height="72"/>
  </div>
</div>

It then positions them via a jQuery extension, hexMapPosition(), which examines the image’s height and width and then calculates an absolute position for the top left pixel.

  (function($) {
    $.fn.hexMapPosition = function(row, column) {
      var tile_width = this.attr("width");
      var tile_height = this.attr("height");

      // Haven't done the math to check these but they work
      var y_offset = tile_height / 2;
      var x_offset = tile_width / 4;

      var xpos = row * (tile_width - x_offset));
      var ypos = column * tile_height);

      if (row % 2 == 0) {
        ypos -= y_offset;    // Every other row, offset down
      }

      return this.css({"position": "absolute", "top": ypos, "left": xpos});
    }
  })(jQuery);

This isn’t perfect, but it’s a good place to start. Some problems I’ve got with it:

  • The hexagon pngs are ones I’ve borrowed from the Battle for Wesnoth project, and are a bit irregular (literally – the top edges are shorter than those on the sides). This does seem to make the math easy, though. I should note here that the PNGs are published under the GPLv2, though to be honest, I have no clue how the GPLv2 could possibly apply to images.
  • I’m a little worried that overlaying further PNGs on top of these ones (for map highlighting, roads, player markers, etc) will interfere with the mouseover events, but I’ll need to experiment.
  • I had a lot of trouble trying to get little borders to appear between hexes by monkeying with the positioning values above.

Edit: having recently discovered jsfiddle, I uploaded a fiddle of this there. I’ve also manage to clean up the code so that it is currently in much better shape on jsfiddle than it is in github, but the github version will be changing and it will be nice to keep a clean version of the basic idea around.