View Full Version : A tile engine that loads arrays from external .txt and/or tiles from external .swf
goku87
11-14-2007, 07:09 AM
okay, so i have a nice little tile map generator that was slighly modified from out of society's stuff, but not by much. like two things are different lol. but its a good little generator.
anyway, how could i go about loading an array from an external text file? ive googled it, and the things i found were kinda helpful, but i couldnt quite understand the different code snippets i found, so i didnt know how to modify them to make them work for me.
also, i have some movie clips in the library which i use for the tiles. i have the different clips to organize the different tiles into sets like outdoors, indoors, caves, etc. i was wondering if it were possible to do the same thing with an external swf as i do with the MCs.
anyway, here is my almost not modified map generator.
// ### code start
// no scaling of the flashmovie...
fscommand ( "allowscale", false );
// constants of the tiles width and height
tileW = 32;
tileH = 32;
depth = 0;
// our map-array
testMap = [ [ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9,10,11,12],
[13,14,15,16] ];
// the mapdrawing-function
function buildMap (map, tileSetSource) {
var mapWidth = map[0].length;
var mapHeight = map.length;
for (var i = 0; i < mapHeight; ++i) {
for (var j = 0; j < mapWidth; ++j) {
this.attachMovie(tileSetSource, "t_"+i+"_"+j, ++d);
this["t_"+i+"_"+j]._x = (j*tileW);
this["t_"+i+"_"+j]._y = (i*tileH);
this["t_"+i+"_"+j].gotoAndStop(map[i][j]+1);
}
}
}
// calls the mapdrawing-function with the argument testMap
buildMap (testMap, "wasteland");
// ### code end//
-- a note on the variables to help others help me --
map - is the array with the tileset information (obviously)
tileSetSource - the tile set swf.
Greg SS
11-15-2007, 05:02 PM
If you're loading tiles from external source, its easier to copy everything and stick it in an array so you can access it better. Don't group it into different arrays, just stick it into one large array and have a tile list printout ready for cross referencing... The last game I made uses the strip method, where I load an external BMP with the tiles arranged like a long film strip. I then slice the strip apart, and place the BitmapData into MovieClip tiles.
I don't know how big your map is going to be, and the size limit of flash String class (other programming languages can handle up to 4 gig of text data), but the easiest way is to parse it out of the file. Have the file contain 2 variables, width and data, make the data in CSV format (comma separated value), and slice the hell out of it into an array. using the width information, rebuild the 2D data using a for-loop.
var mapWidth:Number = 80;
var textData:Array;
var tileMap:Array = new Array();
var lastY:Number = 0;
tileMap[0] = new Array();
for(var i=0; i<textData.length; i++){
var x:Number = i%mapWidth;
var y:Number = Math.floor(i/mapWidth);
if(y > lastY){
tileMap[y] = new Array();
}
tileMap[y][x] = parseInt(textData[i]);
}
TJones
11-21-2007, 08:18 PM
Or you could use XML. Then the size of your maps/levels becomes less of an issue. Cache what you need, then step in and out of the nodes you want. My engine supports 100 levels with 100 rooms per level and 144 tiles per room not including the assets inside each room. I have no performance problems and I just index, reference and or clone the nodes I need. The principle is similar to multi index arrays... Kind of like a box in a box in a box...
So you could have something as simple as this:
<room>
<row>
<tile />
<tile />
<tile />
</row>
<row>
<tile />
<tile />
<tile />
</row>
<row>
<tile />
<tile />
<tile />
</row>
</room>
Now you import that in using the XML object, get the first child of the object (the room node) and place in in an XMLNode variable and you can traverse it the exact same way:
var map:XMLNode = xmlObj.firstChild;
Then in your loop you access the nodes like this:
//inside nested loop...
map.childNodes[z].childNodes[x]
So then you get fancy and setup ID's for the tiles you want to lay down.
<!-- sample xml tile node -->
<tile id="groundTile" />
I know you are wanting to use external .swfs, but for example, say you have a symbol in the library named groundTile and it is set to export with that name, then you would attach it to the stage or target clip a so:
// code inside nested loop function
var tileClip:MovieClip = target.attachMovie(map.childNodes[z].childNodes[x].attributes.id, "tile_"+x+z, target.getNextHighestDepth());
// perform placement and other functions to the
// tileClip as needed...
You can take that even further and set other properties about the tile in the XML as well and then parse it like color values, walkable, friction, static, etc.... It really becomes endless the possibilities...
TJ
;)
goku87
11-22-2007, 12:57 AM
If you're loading tiles from external source, its easier to copy everything and stick it in an array so you can access it better. Don't group it into different arrays, just stick it into one large array and have a tile list printout ready for cross referencing... The last game I made uses the strip method, where I load an external BMP with the tiles arranged like a long film strip. I then slice the strip apart, and place the BitmapData into MovieClip tiles.
I don't know how big your map is going to be, and the size limit of flash String class (other programming languages can handle up to 4 gig of text data), but the easiest way is to parse it out of the file. Have the file contain 2 variables, width and data, make the data in CSV format (comma separated value), and slice the hell out of it into an array. using the width information, rebuild the 2D data using a for-loop.
var mapWidth:Number = 80;
var textData:Array;
var tileMap:Array = new Array();
var lastY:Number = 0;
tileMap[0] = new Array();
for(var i=0; i<textData.length; i++){
var x:Number = i%mapWidth;
var y:Number = Math.floor(i/mapWidth);
if(y > lastY){
tileMap[y] = new Array();
}
tileMap[y][x] = parseInt(textData[i]);
}
hey thanks for all the helpful information, quick question since i feel stupid now and couldnt figure it out, how do i load the .csv into the array? :confused:
goku87
11-22-2007, 01:18 AM
Or you could use XML. Then the size of your maps/levels becomes less of an issue. Cache what you need, then step in and out of the nodes you want. My engine supports 100 levels with 100 rooms per level and 144 tiles per room not including the assets inside each room. I have no performance problems and I just index, reference and or clone the nodes I need. The principle is similar to multi index arrays... Kind of like a box in a box in a box...
So you could have something as simple as this:
<room>
<row>
<tile />
<tile />
<tile />
</row>
<row>
<tile />
<tile />
<tile />
</row>
<row>
<tile />
<tile />
<tile />
</row>
</room>
Now you import that in using the XML object, get the first child of the object (the room node) and place in in an XMLNode variable and you can traverse it the exact same way:
var map:XMLNode = xmlObj.firstChild;
Then in your loop you access the nodes like this:
//inside nested loop...
map.childNodes[z].childNodes[x]
So then you get fancy and setup ID's for the tiles you want to lay down.
<!-- sample xml tile node -->
<tile id="groundTile" />
I know you are wanting to use external .swfs, but for example, say you have a symbol in the library named groundTile and it is set to export with that name, then you would attach it to the stage or target clip a so:
// code inside nested loop function
var tileClip:MovieClip = target.attachMovie(map.childNodes[z].childNodes[x].attributes.id, "tile_"+x+z, target.getNextHighestDepth());
// perform placement and other functions to the
// tileClip as needed...
You can take that even further and set other properties about the tile in the XML as well and then parse it like color values, walkable, friction, static, etc.... It really becomes endless the possibilities...
TJ
;)
wow thanks for all the help! i was thinking about staying away from .xml files because the whole task of trying to get flash to read them was so daunting to me because i could never quite figure out how to do it. as for the external .swf that was just a "what if" kinda deal, but its not needed.
so now more questions to satisfy my ever curious mind:
you stated about setting things like walkable and friction on tiles. how could i go about doing this? and would i also be able to set which "tile set" would be used for each map? also, if i were to have multiple "floors," if you will, could i store the different data from a single xml file into separate arrays? and how would i go about doing this?
and one more crazy question:
can i have like a master .xml file tell actionscript what other .xml map files to load?
sorry for all the questions, but you seem to know what you're doing and i think picking someone's brain is a great way to learn new things. thanks a lot! :)
TJones
11-22-2007, 05:54 AM
sorry for all the questions, but you seem to know what you're doing and i think picking someone's brain is a great way to learn new things. thanks a lot!
Well goku, get ready for XML 1.0.1.
If you don't already have these books, I recommend you purchase them or get a Safari online account where you can access them online:
'ActionScript for Flash MX the Definitive Guide' by Colin Moock
- and -
Essential ActionScript 2.0' by Colin Moock.
I've read most of the books out there but Moock does one of the best jobs of balancing theory with principle and application of knowledge. He is also easy to follow when he explains things.
So, what you want to do is start a new file that you are going to use for learning XML. Also you will want to create an XML file in the same directory as your new .fla. Use the following code and use a text editor (like note pad) and paste the following into the doc:
<room name="MyFirstRoom" >
<row id="0">
<wall symbol="Wall_1" edge="1" />
<wall symbol="Wall_1" edge="1" />
<wall symbol="Wall_1" edge="1" />
<wall symbol="Wall_1" edge="1" />
<wall symbol="Wall_1" edge="1" />
</row>
<row id="1">
<wall walkable="0" edge="1" />
<tile symbol="Tile_1" />
<tile symbol="Tile_1" />
<tile symbol="Tile_1" />
<wall walkable="0" edge="1" />
</row>
<row id="2">
<wall walkable="0" edge="1" />
<tile symbol="Tile_1" />
<tile symbol="Tile_1" />
<tile symbol="Tile_1" />
<wall walkable="0" edge="1" />
</row>
<row id="3">
<wall walkable="0" edge="1" />
<tile symbol="Tile_1" />
<tile symbol="Tile_1" />
<tile symbol="Tile_1" />
<wall walkable="0" edge="1" />
</row>
<row id="4">
<wall symbol="Wall_1" edge="1" />
<wall symbol="Wall_1" edge="1" />
<wall symbol="Wall_1" edge="1" />
<wall symbol="Wall_1" edge="1" />
<wall symbol="Wall_1" edge="1" />
</row>
</room>
Save the file as MyFirstRoom.xml.
inside your crispy clean new .fla, place this code:
// Import the delegate class so we can reference the main timeline
// from within the onLoad event scope
import mx.utils.Delegate;
// create xml object and set properties
var myXmlObject:XML = new XML()
myXmlObject.ignoreWhite = true;
// set the onload event to create a delegate object. This is
// how you reference your onLoadEvent function.
myXmlObject.onLoad = Delegate.create(this, onLoadEvent);
// onLoadEvent function
function onLoadEvent(success:Boolean):Void
{
if (success)
{
trace(myXmlObject.firstChild.toString());
}
}
// load the xml doc to get the process started
myXmlObject.load("MyFirstRoom.xml");
Now, there is something important you need to know about the onLoad event. You need the Delegate class to access the correct timeline/class where the onLoadEvent function is written. Read this link and understand what he is telling you. He is using an external class for his example, but I have just converted it to the main timeline, therefore the principle is the same and the main or root time line is our pseudo class.
http://www.kirupa.com/web/xml/XMLspecificIssues3.htm
Now when you run the .fla, you will see the xml traced in the output window.
You are now ready to move on to the next step. Comment out the trace command in the onLoadEvent and replace with this:
buildGrid(myXmlObject.firstChild);
Now, just below your onLoadEvent function, add a new function:
function buildGrid(xNode:XMLNode):Void
{
var ylen:Number = xNode.childNodes.length;
var xlen:Number = xNode.firstChild.childNodes.length;
for (var y=0; y<ylen; y++)
{
trace("--- start row ---")
for (var x=0; x<xlen; x++)
{
trace(xNode.childNodes[y].childNodes[x])
}
trace("--- end row ---")
}
}
Run your app and you will now see each node trace out along with the start row and end row lines to show you where the inside loop completed and advanced to the next group.
Now lets trace out specifics about the XML. Change your trace command to this:
trace(xNode.childNodes[y].childNodes[x].nodeName)
Now you will see the words "wall" and "tile" trace out which are the node names. (open your xml file and look at it to see. Keep it open for now to refer back to).
So now lets access the symbol attribute:
trace(xNode.childNodes[y].childNodes[x].attributes.symbol)
pretty groovy eh? Now create some library symbols with the symbol names. Make them small squares say 50 x 50 pixels. Use an inkline with a different color so you can see the border. Make one called 'Tile_1' and another called 'Wall_1'. Make them different colors and make sure to set their linkage properties to export on first frame.
Now add this line of code to your build grid function:
var clip:MovieClip = this.attachMovie(xNode.childNodes[y].childNodes[x].attributes.symbol, "s_"+x+y, this.getNextHighestDepth());
clip._x = clip._width * x;
clip._y = clip._height * y;
You should now see a 5 x 5 grid on your screen with the outside surrounding squares (the Wall_1 symbols) a different color from the inner squares (the 'Tile_1' symbols)
Now it's your turn... Experiment and make the XML file longer, add more symbols to the library and names of the symbols to the xml... etc...
Play with this code, understand it front to back, why it is doing what and how to make it produce results that you want. This is your foundation to tile based games. Once you have your head around this, the more advanced stuff will come easy and you will acquire the vision necessary to implement your own ideas.
For reference, here is the final code from frame 1 of the main timeline in your fla:
// Import the delegate class so we can reference the main timeline
// from within the onLoad event scope
import mx.utils.Delegate;
// create xml object and set properties
var myXmlObject:XML = new XML()
myXmlObject.ignoreWhite = true;
// set the onload event to create a delegate object. This is
// how you reference your onLoadEvent function.
myXmlObject.onLoad = Delegate.create(this, onLoadEvent);
// onLoadEvent function
function onLoadEvent(success:Boolean):Void
{
if (success)
{
//trace(myXmlObject.firstChild.toString());
buildGrid(myXmlObject.firstChild);
}
}
function buildGrid(xNode:XMLNode):Void
{
var ylen:Number = xNode.childNodes.length;
var xlen:Number = xNode.firstChild.childNodes.length;
for (var y=0; y<ylen; y++)
{
trace("--- start row ---")
for (var x=0; x<xlen; x++)
{
trace(xNode.childNodes[y].childNodes[x].attributes.symbol)
var clip:MovieClip = this.attachMovie(xNode.childNodes[y].childNodes[x].attributes.symbol, "s_"+x+y, this.getNextHighestDepth());
clip._x = clip._width * x;
clip._y = clip._height * y;
}
trace("--- end row ---")
}
}
// load the xml doc to get the process started
myXmlObject.load("MyFirstRoom.xml");
Basics, basics, basics!!!! I can't stress that enough.
Also, spend a lot of time tracing and looping through XML. Create really deep and complex trees and learn how to access and traverse them.
Also learn how to call recursive functions (functions that call themselves to drill down through XML Nodes). These are very fast and effecient ways to get alot of xml data parsed quickly.
Good grief! I think I just wrote a tutorial! Hope this helps and let me know if you have any questions... Maybe this post should be moved to a tutorials section or new thread... However I am a complete forum idiot, so someone else will need to do it for me LOL! Geesh!
TJ
goku87
11-24-2007, 05:36 AM
Well goku, get ready for XML 1.0.1.
If you don't ... all that information ... However I am a complete forum idiot, so someone else will need to do it for me LOL! Geesh!
TJ
WOW THATS A LOT OF INFORMATION!!!! :D
give me a few days to digest it and play around with it. Once i am sure that i am totally lost and not just overwhelmed with the amount of information ill hit you back with any other questions i may have.
thanks a load dude!
|
vBulletin® v3.8.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.