Be a Supporter!

As3: Share Level Code

  • 2,512 Views
  • 4 Replies
New Topic Respond to this Topic
Glaiel-Gamer
Glaiel-Gamer
  • Member since: Dec. 28, 2004
  • Offline.
Forum Stats
Member
Level 28
Game Developer
As3: Share Level Code Sep. 23rd, 2009 @ 08:07 PM Reply

AS3: Main

THIS ASSUMES YOU ALREADY HAVE A BASIC LEVEL EDITOR AND A WAY TO CONSTRUCT YOUR LEVEL FROM AN ARRAY.

Typically your level will be stored as a 2D array of tiles, with some object in each slot representing how to reconstruct your level. Do not directly store MovieClips in the array, this is not the level, just the data for the level.

i.e.

map = [[0, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 1],
[0, 1, 1, 1, 1, 1]]

Your map can be anything you want, store numbers, strings, ints, more arrays, it doesn't matter.

EXPORT LEVEL CODE STRING

var raw:ByteArray = new ByteArray();
raw.writeObject(map);
raw.compress();
levelstring = BAtoString(raw);

Simple right? You write the entire map object to the byte array. Flash encodes everything about it so it can be reconstructed exactly as you wrote it when decoding. Then you just compress it, which will turn any level with a ton of similar tiles into a short code. I tried, a 100x100 level was converted into about a 250 character code. BAtoString (ByteArray to String) will be covered later.

IMPORT LEVEL CODE STRING

var raw:ByteArray = new ByteArray();
raw = StringtoBA(levelcstring);
raw.uncompress();
map = raw.readObject();

StringtoBA (String to ByteArray) will be covered later
This is the exact inverse of the code above. We read in the string, convert to a bytearray, decompress, and then read in the map. You dont even have to do anything special! map will be reconstructed exactly as you left it when encoding.

UTILITIES: THE MEATY PART
ByteArray is fun enough, 4 lines of code for export, 4 lines of code for import, it couldn't be that easy, could it?

Not quite, but the good part is the required functions are very simple and easy to understand. Here's the conversion functions:

function BAtoString(a:ByteArray):String{
	var s:String = "";
	a.position = 0;
	while(a.bytesAvailable){
		var b:uint = a.readByte();
		s += ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'][b%16];
		b=b>>4;
		s += ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'][b%16];
	}
	a.position = 0;
	return s;
}

This will take a byte array, convert it to a string, and return that string. There is a toString function on the bytearray, but it's not useful here because of the large amounts of non printing characters and other funkiness that will make your string really hard to copy and paste and import. So I'm rolling my own which will basically represent each byte as 2 hex characters. One hex character (4 bits) can be represented as any of the following characters: 0123456789abcdef

while(a.bytesAvailable){
var b:uint = a.readByte();

this will read every byte in the string. A byte is 8 bits, and i'm gonna represent them like this:
[01010101]

There's no byte type in flash so we pack it into an unsigned integer (32 bits). The rest of the bits will just be 0, like so:

[00000000 00000000 00000000 qwertyui]

There's no function for reading 4 bits at a time so we need to convert the top 4 bits and the bottom 4 bits into hex characters separately.

['0','1','2','3','4','5','6','7','8','9'
,'a','b','c','d','e','f'][b%16];

Looks complicated, but let me break it down:
['0','1','2','3','4','5','6','7','8','9'
,'a','b','c','d','e','f']
is an array

[b%16];
is an index into the array

b%16 will read the bottom 4 bits of the byte and return a number 0 to 15 which acts as an index into the array. Come to think of it this is functionally equivalent to b&15 (bitwise and [byte]&[00001111]), so use whatever you wish. Bottom 4 bits converted to hex, added to the string, we're good.

b=b>>4;
will now shift the byte over 4 bits to the right, so

[qwertyui] >> 4 = [0000qwer]

Now the top 4 bits are the bottom 4 bits so we just do the same thing as above to extract them and convert to a character.

At the end we reset the bytearray's read position to 0 and return the string.

Now we move on to the same function, but in reverse:

function StringtoBA(s:String):ByteArray{
	var a:ByteArray = new ByteArray();
	for(var i:int = 0; i<s.length; i+=2){
		var b:uint = (hint(s.charAt(i))) + (hint(s.charAt(i+1))<<4);
		a.writeByte(b);
	}
	a.position = 0;
	return a;
}

This time we're reading a string and converting it to a byte array. It's a little easier, but again we have to read 2 characters at a time, so the for loop there has +=2 instead of ++ in the 3rd field. Also, this WILL NOT handle line breaks or spaces gracefully, but it shouldn't be too hard for you to process the string to remove all non hex characters before calling this function.

Anyway, here's the meat that reads 2 characters, converts into a byte, and writes it into the bytearray:

var b:uint = (hint(s.charAt(i))) + (hint(s.charAt(i+1))<<4);
a.writeByte(b);

lets break it down:

var b:uint = (CHAR1) + ((CHAR2)<<4);

char1 will be 4 bits, char2 will be 4 bits, so

char1: [0000asdf]
char2: [0000ghjk]

char2 << 4 shifts the byte over 4 bits to the left so it's now [ghjk0000]

we add together:

[0000asdf] +
[ghjk0000]
----------------
[ghjkasdf]

and we have our byte, which we write to the bytearray.

but it's not just a raw character lookup, we need to look up the character in the string then convert it to an int

hint stands for "hex character to integer", will be covered below.

s.charAt(i) is one character, s.charAt(i+1) is the next character. We convert them to ints with hint and now we have out CHAR1 and CHAR2 which we can plug into the equation above.

And finally, hint:

function hint(s:String):uint {
	switch(s){
		case "0": return 0;
		case "1": return 1;
		case "2": return 2;
		case "3": return 3;
		case "4": return 4;
		case "5": return 5;
		case "6": return 6;
		case "7": return 7;
		case "8": return 8;
		case "9": return 9;
		case "a": return 10;
		case "b": return 11;
		case "c": return 12;
		case "d": return 13;
		case "e": return 14;
		case "f": return 15;
	}
	trace("invalid character");
	return 16;
}

There most likely is a shorter way to do this by converting characters to their key codes and subtracting, but since abcdef and 0123456789 are in a different range there'd still have to be a comparison and well this is a lot easier to visualize.

We just use a switch statement to easily and return the int we want.

And there we go. This will work with any object you want thanks to ByteArray magic, and compressing it will make it very difficult for someone to modify the code and get a glitchy level. Flash will throw errors if it has a problem decompressing the level code, so I recommend you wrap the decode function in a try...catch statement so you can correctly say when the level couldn't be reconstructed from the code.

matrix5565
matrix5565
  • Member since: Feb. 28, 2006
  • Offline.
Forum Stats
Member
Level 16
Game Developer
Response to As3: Share Level Code Sep. 23rd, 2009 @ 08:39 PM Reply

Thanks Glaiel, level sharing is quite a powerful community tool in Flash games. It's good to find such a detailed explanation of a import/export system.

Matt-Porter
Matt-Porter
  • Member since: Aug. 9, 2004
  • Offline.
Forum Stats
Member
Level 41
Game Developer
Response to As3: Share Level Code Sep. 23rd, 2009 @ 11:29 PM Reply

I've made a grid based editor myself, but never used byte arrays. I'll have to check this out in a closer look later, nice work, glad to see AS3 Main coming together.

Glaiel-Gamer
Glaiel-Gamer
  • Member since: Dec. 28, 2004
  • Offline.
Forum Stats
Member
Level 28
Game Developer
Response to As3: Share Level Code Sep. 24th, 2009 @ 01:40 AM Reply

As an exercise this is an encoded 2D array of integers:

87ad3e49664e4d764616061648346202f0f24896 8ffffffff141d98b12c5f030c940be6cd6b1a9e1 8902e67c161d0805ec8826c0e1b181887d0c881a 181909600af9ff268e10a0ef78487cf343a034f3 9810d85c308b363946d083c108b43090010056fc 32fe

if you decode correctly you should be easily able to see the 2D array.

liam
liam
  • Member since: Dec. 11, 2004
  • Offline.
Forum Stats
Member
Level 22
Blank Slate
Response to As3: Share Level Code Sep. 24th, 2009 @ 11:16 AM Reply

function hint(s:String):uint {
 return uint("0x"+s);
}

And you could easily check if it's an invalid character by converting it to the char code and just checking it's not over the char code of f :p I'm sure that'd work.


Sup, bitches :)

BBS Signature