I wrote earlier about wanting to have certain sprites (enemies, locked doors, etc), have state that is persistent in a number of levels – screen-local (respawning when leaving/entering a room), map-local (enemies don’t respawn tiill you leave a dungeon and re-enter), and global (bosses stay dead, unlocked doors stay open!)
I kind of figured out a way to deal with this. I never sucked it up to figure out some stuff with the wonderful DAME exporter (an awesome map editor for flixel/flashpunk/etc games) and its lua exporter business…but then I did, sort of, at least enough to make it kind of do what I wanted.
Every map I make is split into grids, because that’s how I want the camera to work. I also only want to load sprites on a certain grid. Currently when I export the sprites from DAME, they end up all under one XML <level> node. This is fine, but on a big map when I want to load certain sprites for a grid, I’d have to calculate every single object’s grid x and y coordinates!! Although that would probably work fine, that’s not as nice. So, I did the following:
After embedding the XML from DAME into my game, I iterate through its <level> nodes (remember, level nodes just correspond to contiguous “areas” in my game – perhaps a floor of a dungeon, someone’s house, etc.). I create a new XML (one that will be serialized/saved and later checked to determine state of a room) object, and create a new <level> node. For every object (enemy, door, etc) in the <level> node of the DAME XML, I convert its x and y coordinates into grid-coordinates, so [0,screen_width) -> 0, [screen_width,2*screenwidth) -> 1, etc. I check if I’ve already added a <grid> node to my new <level> node, if so, I append the object to the grid node, otherwise, make a new grid node, append it to the level node, and append the object to the grid node.
Conceptually I guess this wasn’t too unwieldy, but it took a little time to figure out the syntax and what not.
This DAME-generated XML:
<root> <level name="Dungeon_Test"> <Slime x="96" y="32" p="1" alive="true" /> <Slime x="128" y="32" p="1" alive="true" /> <Slime x="80" y="32" p="1" alive="true" /> <Slime x="176" y="224" p="1" alive="true" /> <Slime x="224" y="368" p="1" alive="true" /> <Slime x="80" y="256" p="1" alive="true" /> <Slime x="16" y="336" p="1" alive="true" /> <Slime x="32" y="352" p="1" alive="true" /> </level> <level name="Area_X"> <Slime x="80" y="80" p="1" alive="true" /> <Slime x="0." y="32" p="1" alive="true" /> <Slime x="16" y="112" p="1" alive="true" /> <Slime x="48" y="32" p="1" alive="true" /> <Slime x="48" y="80" p="1" alive="true" /> </level> </root>
is transformed to this XML (which will end up being serialized and used in the game):
<root> <level name="Dungeon_Test"> <grid grid_x="0" grid_y="0"> <Slime x="96" y="32" p="1" alive="true"/> <Slime x="128" y="32" p="1" alive="true"/> <Slime x="80" y="32" p="1" alive="true"/> </grid> <grid grid_x="1" grid_y="1"> <Slime x="176" y="224" p="1" alive="true"/> </grid> <grid grid_x="1" grid_y="2"> <Slime x="224" y="368" p="1" alive="true"/> </grid> <grid grid_x="0" grid_y="1"> <Slime x="80" y="256" p="1" alive="true"/> </grid> <grid grid_x="0" grid_y="2"> <Slime x="16" y="336" p="1" alive="true"/> <Slime x="32" y="352" p="1" alive="true"/> </grid> </level> <level name="Area_X"> <grid grid_x="0" grid_y="0"> <Slime x="80" y="80" p="1" alive="true"/> <Slime x="0." y="32" p="1" alive="true"/> <Slime x="16" y="112" p="1" alive="true"/> <Slime x="48" y="32" p="1" alive="true"/> <Slime x="48" y="80" p="1" alive="true"/> </grid> </level> </root>
By this chunk of AS3 code (FYI, there are some static thingies from me playing around calling this function from elsewhere, also saveXML needs to be referenced globally)
[Embed (source = '../xml/Seen.xml', mimeType = "application/octet-stream")] public static const EmbedXML:Class; public static var embedXML:XML = new XML(new EmbedXML); public static var saveXML:XML = <root> </root>; public static function embed2saveXML():void { var savelevel:XML; //Will be a level node for our saveXML tree var o:XML; //Generic object XML var level:XML; //For each "level" (house/dungeon/whatever) var grid:XML; //Represents a "grid" var grid_exists:Boolean; // Set to true when an existing grid is found // while iterating through some level's grids var name:String; var x:int; var grid_x:int; var y:int; var grid_y:int; //Every level corresponds to a floor of a dungeon, //a house, the world map, etc. for each (level in embedXML.level) { savelevel = <level/>; savelevel.@name = level.@name; // These are all the DAME objects. for each (o in level.*) { //convert XY into Grid X,Y. x = parseInt(o.@x); y = parseInt(o.@y) + HEADER_HEIGHT; grid_x = x / Registry.SCREEN_WIDTH_IN_PIXELS; grid_y = y / Registry.SCREEN_HEIGHT_IN_PIXELS; // Append the object "o" to the grid node if it already exists. grid_exists = false; for each (grid in savelevel.grid) { if (grid.@grid_x == grid_x.toString() && grid.@grid_y == grid_y.toString()) { grid.appendChild(o); grid_exists = true; break; } } // Otherwise, create a new grid node and append "o" to it. if (!grid_exists) { grid = <grid/> grid.@grid_x = grid_x.toString(); grid.@grid_y = grid_y.toString(); grid.appendChild(o); savelevel.appendChild(grid); } } //Finally, append the transformed level node to the serialized XML. saveXML.appendChild(savelevel); } }
Is there a better way to do this? Probably! But this works…it seems. Of course, more attributes will have to be added and what not to deal with state, but this is a good base for that.