Screen transitions, legend of zelda gbc gamesPosted: 2012-03-10
So, a favorite set of games of mine has always been the legend of zelda GBC games (link’s awakening, oracle of seasons, oracle of ages – yes, LA was on the Game boy originally but they’re similar in gameplay mechanics/graphics)
The game uses 16×16 tiles, and every room is 10 by 8 tiles. Pretty amazing if you ask me. Dungeons and the map are laid out in a grid of these 10×8 tile rooms. It’s quite elegant how they did this and managed to well, make it work on something so tiny. I’m pretty impressed!
I was interested in using a similar type of map system in my next game, and got thinking about what it would take to do in Flixel. Quite a fun problem to think about. Let’s see. These are my initial thoughts and attacks at the problem, maybe you’ll learn something too.
-Touching the edges of a map must push you a tile forward, but move the camera view an entire screen in the direction of travel.
-Enemies and events need to be able to be triggered. Unlocked doors must stay open, bosses defeated stay dead, etc. That is, each screen must be able to maintain state for some period of time
-Enemies/bullets can’t travel from one screen to the next.
Tackling this problem one step at a time:
1. Making the map appear. Using tilesets, just draw this in a map editor. For simplicity let’s say I’m going to use one tileset for my map tiles. Then I export this gigantic map as a CSV (comma separated values – like “1,2,3,4,4,4,4”) , which with a quick calculation probably wouldn’t be more than a couple hundred KB (assuming I have over 100 tiles, my screens are 8×10 tiles, and I have like a 1000 screens, which is probably overcounting by a bit). Not a memory problem, in any case. We can pick which of the tiles we want to assign certain properties. E.g., some tiles act as holes, some as spikes that do damage – we can either replace these tiles with sprites at draw-time, change their properties to be one way, etc.
A few caveats with this. Drawing the whole map is obviously a horrible idea, we don’t want to have to deal with that being redrawn constantly, and in any case it’s just inefficient. So, we’ll want to only show one part at a time, and maybe have neighboring grids in memory for easy drawing, which leads to
2. Traversing the map.
Ahead of time, we know that our screens can be represented as a grid of points – you might start at screen (4,2). Now, luckily, our screens are equal sized *and* grids! So, it’s just a matter of some array index trickery and we can pull out grid-sized chunks from our whole-map CSV. We might have small 80-member CSVs (10×8) of mappings to tiles, one for the current screen, and one for up, down, left, and right (if they exist). So, we’d create tilemaps out of these CSVs and have them drawn. When we want to move screens, we just push our character a tile forward, and move the camera an entire screen in whatever direction. Watch a video of zelda to see how this works. It visually works quite well and you can’t really tell that your character has moved completely across the screen!
Once you’re in the new map, of course you will trash those other tilemaps from memory, and find your new neighbor tilemaps, and load those into memory.
It’s kind of like your traversing a building, but you only really can see parts at a time and process that visually. If we had to process every thing in the building at once, even though we’re not there, that would be tiring!
3. Dealing with sprite generation.
In a screen , we’ll have enemies, one-way doors, locked doors, NPCs, etc. I’m not sure 100% how I want to do this. I could create a tilemap that maps tiles to certain enemies. That is how I did pills/notes/doors/entrances in my previous game. That might be annoying on my end because I would have to hand-code this mapping. But it wouldn’t be TOO annoying. However, I would end up with an enemy/sprite whatever CSV map the same size as my map-map. The other idea is exporting data to XML and parsing that instead, but maybe that would take up more space in the XML file. Not something to worry too much over, though. In some way, there will be a way to map sprites to tiles, etc.
4. But what if we unlock a door? NOW WHAT? OR KILL A BOSS?
There’s a few ways to go about it. My favorite at the moment would be to store a dictionary into the save file for our “state” – it would have two keys, an “area’ and “subarea” key. E.g., “Dungeon 1, screen (2,3)”. With in it is just an array of objects. When you enter a screen, this array is generated from anything that holds state – a boss that may be alive at first, but dead forever later, a teleporter that is activated only past a certain event in the game. The state of tehse objects is saved as necessary, so if the boss is dead then I’d leave some data in my array saying that the boss should be dead.
How will this be made to work? Well, there are different kinds of sprites. There are sprites that don’t really have state – say a turnstile object, or a one way door. These things are *Always* where they are on the map, no matter what! Thus we don’t label them as stateful. A boss, on the other hand, will be “stateful” – is it dead? or alive?
So, we just take our array of numbers that maps to certain sprites. I’ll have to hard code whether a sprite is stateful, but if it’s not, we stick it on the screen. Otherwise, we look into our saved data for the specific screen’s “state”. If it doesn’t exist, this means we have visited the screen for the first time, and so everything should have default behavior ( a door should be locked, the boss should be there, etc.) If the entry in our large dictionary IS there, we use the state that we stored (maybe one door is open, a wall is broken,…)
This very well might be what I go with, it seems like it would work quite nicely. I shouldn’t need to store more than a few things per screen, anyways, so storage shouldn’t be an issue.
5. Enemies shouldn’t be able to move between screens! Or maybe they should for scary effects!
Not too hard. Just set bounds on where it can move based on what screen it spawns on. Maybe set “max/min x/y” value when you create the enemy object ,and don’t let it move outside. Likewise with bullets!
So that lets us create dungeons, bosses, and some basic state. There is, of course, the question of how to handle global state (what dungeons are open? What events have I triggered? Etc), but that comes more into play with the actual game, I’d say.
An elegant way of handling events would be what to think about next, I think…
annnnd it kind of works, screen moving. I have a LOT to think about still, though,.