Cloning Zelda, part 2: Tile thing
Last time, I talked about how I had started working on a clone of the Legend of Zelda in a pure-JavaScript aesthetic. When I started researching and brainstorming solutions to the challenge of hit detection, not to mention the game's overall memory footprint, I realized I had a problem. My first stab at the game used a single, huge (4096x1408) PNG image (downloaded from here, forgot to mention that last time) as its map, and I kept track of an offset to the current screen's position on the huge image, which the main program loop would repaint on the canvas every single game tick! Eeek!
Needless to say, upon reflection, that looked like a really bad idea. So I went back to the drawing board, and came up with several design considerations:
- Only repaint the background image when it changes (not every game tick!)
- Use two canvas layers to separate background from sprites (so player and enemy movement won't trigger a background redraw)
- Use a "tile" approach to limit image size in memory
So, armed with my trusty copy of Paint Shop Pro 7, I started slicing up the map. What I discovered was that the game overworld uses a surprisingly sparse number of tiles to do its job... in fact, these 79 tiles cover all the bases! As I said before, the full map of Hyrule is 4096 pixels wide by 1408 pixels high. We can reproduce the whole thing by using a 2-dimenional array of 256x88 tiles, 16 pixels square. I chose to go one step further, subdividing the map into individual screens, and each screen into its component tiles. This gave me a 3-level map structure:
- 1 map of 4096x1408 pixels
- = a 16x8 matrix of map screens,
- each of which = a 16x11 matrix of 16x16 tiles
Now came the long & tedious part: encoding the map as a data structure! I let the above structure inform my design decisions, creating a Map class with: a current property to contain an x/y pair representing the currently visible screen within the map, and a 16x8 two-dimensional array containing MapScreen objects. Each MapScreen object knows its (x,y) position within the larger map, and contains a 16x11 array of cells, each an (x,y) coordinate pair representing that cell's source tile in the global "tiles" image. Map and MapScreen both contain draw() methods that take an HTML Canvas 2d drawing context as a parameter, and which take care of drawing themselves when requested. The code for both Map and MapScreen is here: map.js, and the data file is here: mapData.js
Once the code for the new map was done, there was still the matter of encoding the map data in a way the Map/MapScreen classes could use. I started out hand-coding the map data myself, but this soon proved to be the gateway to madness, and I decided that I needed to build a map editing tool. This would be useful, I reasoned, not only in terms of saving me time now, but also because it would allowing for custom map creation in the future (assuming I live long enough to get this beast fully running!).
At any rate, after a few days of spare-time coding, I came up with this Mapper tool. Two caveats, though: 1, the user interface is pretty terrible, since I built this for myself, not for the average user, and 2, performance is pretty sluggish. The large map on the editor page is drawn in the same way as the map in the game itself, only repeated (16x8) 128 times in rapid succession, once for each individual screen. A good CPU helps a lot, and so does having a good browser: Google Chrome runs it about 10 times faster than Firefox 3.0 (which complains with the "unresponsive script" dialog box on my home PC... but will finish soon thereafter if you click "continue"). That said, the latest nightly build of Firefox 3.1, aka Shiretoko, is quite a bit better... probably owing to its revamped TraceMonkey JavaScript engine). Code for the mapper tool is here: mapper.js
Anyway, after a few days of spare-time work and referring often to the original overworld map image, I've finished encoding the map, and the new version of the game is here. Basically the same functionality as from Part 1, but with a much smaller footprint. I've also started to implement collision detection, which the tiled map makes quite a bit simpler, but considering how long it took me to encode the map tile data, that task kind of took over this step of the process.**
Next time, we're going to finish implementing collision detection, and introduce the missing Armos statues (hint: they're not part of the current map for a reason)!
* Footnote: Some of you may have no doubt noticed that there is quite a bit of duplication in the map tiles I chose to use, as many tile designs are represented three different times in three different colors (brown, green, and gray). Some of you may even point out that I could have accomplished this same thing with a smaller tile palette, if I implemented the idea of multiple palettes at the same time (the downloadable Zelda clone Zelda Classic does it this way, for example). Well, yeah, but I didn't want to even think of the performance hit I would have to take to do image re-coloring in JavaScript/canvas, so intentionally avoided that road. Maybe I'll try it out later on when Canvas has had a chance to mature a bit more in the modern browsers.
** Footnote 2: You may notice some blurring and visual artifacts in this new version of the game map; these appear to be due to the fact that I'm scaling the game up to 2x its original size, so I'm going to have to experiment with resizing it differently, or keeping it in its original 256x240 size. As with the Mapper performance, Chrome seems to handle this much better than Firefox, but a new solution is probably in order.
Labels: clone, JavaScript, Zelda
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home