Home > aen's Blog

Multiple Layers, Part 1

smee, build 13

This build receives minimal support for layers! They're not very useful yet, since you can't plot anything to them, but you can add them and then view the map rendering changes as you reorder them.

User Interface Improvements

From the image above, you can see I've spiffed up the the UI a little bit. If the current zoom factor is small enough for the entire map to fit in the viewport, it is centered.

One oddity I've yet to resolve (they're starting to pile up, aren't they) is the fact that the viewport scroll pane shrinks to a tiny size when zooming to any size that is larger than the visible viewport space when compiled with JDK 1.5. Yet with 1.6 the scroll pane with "properly" grow to fill the entire visible area. I experimented with setting the sizes manually, but it ended up being more trouble than it was worth. I'll just have to research it out better and figure out what the dealio!

The hatched backgrounds mimic RPG Maker. I dunno why, but it looks classy!

Layer Manager

The layer manager is of course in the upper right hand corner. If you load up tile.smee-map, you can hit the A key to add a layer. The viewport will promptly become gray when you do. This happens because I currently render all layers solid, making no allowances for empty tiles in any of the upper layers. For demonstration purposes, I have left it this way.

To reorder the layers, hold down the right mouse button over a layer name, then drag upward or downward. If you drag Layer 0 all the way to the bottom, it becomes visible again-- it emerges from underneath the other layers.

The cool thing about how this works is it uses a new custom event and event listener!

The MapLayerEvent Class

The MapLayerEvent class stores information about various types of layer manipulations: adding, removing, and reordering layers. It takes the layer and any relevant information about the event as arguments to its constructor.

When a new layer is added, was_added is set to true. When a layer is removed, was_removed is set to true. When layers are reordered, prev_index indicates where the layer was and curr_index indicates where the layer was moved.

I stuffed all of this information into a single class instead of several separate event classes for simplicity's sake. If I end up adding several more types of layer operations, it will be time to refactor into more finely-grained event classes.

The MapLayerEventListener Interface

The MapLayerEventListener interface has hooks for all three of the above operations: mapLayerOrderChanged(), mapLayerAdded(), mapLayerRemoved(). When a new map is loaded, an instance of the new MapLayerManager class subscribes to these events like so:

map.addMapLayerEventListener(layer_manager);

The MapLayerManager class implements MapLayerEventListener directly, which is why it is passed as the event listener argument.

The Map class was updated to include adding and removal of layer event listeners. There are three methods that currently fire layer events:

  1. add_layer() calls fireMapLayerAdded().
  2. remove_layer() calls fireMapLayerRemoved().
  3. swap_layers() calls fireMapLayerOrderChanged()

Flow of Events

Here's how the events flow during dragging in the layer manager:

  1. In the mapLayerOrderChanged() method of MapLayerManager, I call repaint() on the viewport.
  2. Then, in the code that allows the user to drag layers around, I make a call to a new method I created in the Map class: swap_layers().
  3. As you drag layers around in the layer manager, it swaps the positions of adjacent list items. I created swap_layers() to do the same for map layers. Each time the layer manager swaps list items, I make a corresponding call to swap_layers() to keep the map in sync with the list.

    If you remember the discussion on MVC, this is exactly how a controller, in this case a layer manager, should work: it receives input from the user, which it then uses to affect the underlying data model, in this case a map. The data model then dutifully fires off any relevant events.

  4. When swap_layers() is called, it fires of a new map layer event via fireMapLayerOrderChanged().
  5. Coincidentally, since the MapLayerManager class implements the MapLayerEventListener interface, and since it is subscribed to map layer events, the call chain comes full circle and execution finds its way into the mapLayerOrderChanged() method, which refreshes the viewport.

Slick, no!

Multiple Useful Layers

Tomorrow I'll be adding the ability to plot to the currently selected layer. The map renderer will also be updated to properly account for transparent tiles in all upper layers. And finally, I'll put a bit more effort into importing tiles, perhaps by integrating with Stripper.

Gosh, it'll be swell!