Ars hexica

After spending a while uselessly flailing around trying to get the Gimp to do what I wanted it to do, I wised up and checked out some directions for tile creation on the Wesnoth site, which pointed me in the direction of the open-source SVG editor Inkscape. Inkscape, among other nice features, has a polygon tool which is able to make regular hexagons and the like. It will also export selected objects as PNG files, so with minimal effort I’ve been able to replace the tiles I swiped from Wesnoth with new, boring ones of my own devising. Among other improvements, I’ve got lines around the hexes now, and the hexagons themselves are regular. Encouragingly, I didn’t need to change the javascript at all to deal with the new sizes, since it works by inspecting the img sizes from their attributes directly and basing its math on that.

Along those same lines, I’ve been pretty pleased with how easy it is to separate presentation logic from presentation markup in jQuery.  Apart from switching image sizes, I can see that it would be fairly easy to set up the function which generates random tiles, randomTile(), to dynamically query the DOM tree, find all of the possible tiles, and then pick one at random (currently it uses a static list of possible tile names).  Together with attaching arbitrary custom data to each possible tile, this seems like a promising approach to some sort of game, with a classic game engine / game content separation.  However, Barbarian Prince is not that game, being based on a static game map, and part of the point of this exercise has been to learn Scala, so it’s back to the server side after this.

After finding Inkscape, I spent a while  messing around with different sizes for the hexes in the hexmap.  For a while I was considering using either SVG or HTML5 canvas elements or some such to represent the map, with the idea that it would allow a more easy way to zoom in and out from the map.  I feel like both of those areas represent rather large and interesting rabbit holes, though, and I’ve only just emerged from the jQuery rabbit hole I was in.  Moreover, having distinct tiles seems like a good match for the game as it was written.

The next steps are to head back out to terra incognita and add this stuff to the project:

  • A more beefed-up definition of the hexmap definition (as a JSON, uh, schema).
  • A persistent store on the Scala side which is capable of storing and retrieving this definition (most likely via mongodb, since I’m familiar with it and it has a well-regarded Scala interface).
  • Simple REST service definitions on the Scala side, and, oh yeah, a web server framework to serve them up, most likely with Scalatra.
  • An Ajax call from index.html to get the persisted hexmap from the Scala server and display it.

I will note in passing that it has occurred to me that Barbarian Prince as she is spoke could probably be easily implemented purely through jQuery and HTML5 client-side storage (which I guess seems to mean indexedDB as of February 2012), keeping all game logic and map creation on the client side.  This would certainly reduce network latency for players, as well as the need for highly-skilled developers on server side.  This is an approach that somebody who isn’t trying to get experience in programming Scala should probably pursue.

Advertisements

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.