Tuesday, March 23, 2010

AS3 Dynamic Terrain




Ah the joys of Flash. Recently I needed to come up with a way of dynamically generating a realistic looking landscape for an isometric tile based game I am working on. After a bit of investigation into techniques that people have used I eventually successfully integrated an effective terrain generating algorithm in to the afore-mentioned game.

But how was this done? Once again through the beauty of BitmapData and it's widely ignored PerlinNoise method.

The beauty of this is that you are able to utilize PerlinNoise to generate what is called a "heightmap" which is then applied to the isometric tiles.

Firstly lets generate the perlin noise that we will use to generate a height map... 


var pmap:BitmapData = new BitmapData(400, 400);
var _seed:uint = Math.round(Math.random()*100);
pmap.perlinNoise(400,400, 6, _seed, true, false, 1, true);


This will generate a 400x400 BitmapData object that when rendered will look like the Photoshop "Clouds" filter (see image below). The beauty of Perlin Noise is that it produces gradual peaks and troughs which can be used to generate natural looking variants in terrain height.

In this example, we will be applying these values to a 7x7 isometric grid. Let's initialize where we will be storing this data:

// Set the grid size
var grid_width:uint = new uint(7);

// Initialize the heightmap
var heightmap:Array = new Array();


for (var i:uint=0; i < grid_width; i++) {
 
     heightmap[i] = new Array();
for (var j:uint=0; j < grid_width; j++) {
heightmap[i][j] = new uint();
}
}


Using the perlin map we've generated, let's divide the BitmapData object in to a 7x7 grid and examine the top-left pixel in each segment. We will then report the brightness of it to the heightmap array. (Darker pixels report a lower value, whilst brighter pixels report a higher value.)

We will also want to separately store the darkest pixel value for level correction in a later step...


var pixelPoint:Point = new Point();
var darkest_pixel:Number = 1;

//Divide the map in to a 7x7 grid and take data at each interval
for (i=0; i < grid_width; i++) {
 

    for (j=0; j < grid_width; j++) {
pixelPoint.x = Math.round((i/grid_width) * pmap.width)+1;
pixelPoint.y = Math.round((j/grid_width) * pmap.height)+1;
heightmap[i][j] = pmap.getPixel(pixelPoint.x, pixelPoint.y);
        heightmap[i][j] /= 0xffffff;

if (heightmap[i][j] < darkest_pixel) {
darkest_pixel = heightmap[i][j];
}
}
}


Now we will have a heightmap populated with pixel brightness data from the perlin noise. Not to mention a separate variable which contain the darkest pixel value. We will now use this lower limit value to stretch the heightmap values in to range between 0 and 1...

var brightest_pixel:Number = 0;

//Adjust values to a min of 0

for (i=0; i < grid_width; i++) {
for (j=0; j < grid_width; j++) {
heightmap[i][j] -= darkest_pixel;

        if (heightmap[i][j] > brightest_pixel) {
            brightest_pixel = heightmap[i][j];
        }
}
}

//Adjust values to highest value of 1
for (i=0; i < grid_width; i++) {



for (j=0; j < grid_width; j++) {
        heightmap[i][j] /= brightest_pixel;
    }
}


Now that we have a fully formed heightmap, we can apply it to change the height of the relevant cell in the isometric grid to produce natural looking terrain! (The following code assumes you have already got a flat isometric 7x7 grid on the stage called myTile[row#][column#])


var height_range:Number = 5;
for (i=0; i < grid_width; i++) {

for (j=0; j < grid_width; j++) {
  myTile[i][j].y -= heightmap[i][j]*height_range;
    }
}






Nice little effect, eh?

Monday, March 22, 2010

If statement syntax shortcut


Many times have I come across a situation where I have an extremely simple "if statement" that takes up 5 or so lines of code and I've though to myself "I really wish I could express this code in a single line without losing a great deal of readability"

Well look no more... Actionscript 3.0 code such as this:

if (myVar >= 10) {
trace("foo1");
} else {
trace("foo2");
}

can now easily be compressed in to a single line utilizing an alternate method of syntax such as this:

(myVar >= 10) ? trace("foo1") : trace("foo2");

Nice huh? Probably not as easy to read - but for those of you with large class files, or if you are slightly OCD when it comes to white-space conservation as I am, it's a perfect method of syntax compression.

Blogistential Angst


I have resisted the temptation for a while now, but I can resist no more.

Yes my friend, I am giving in to the need to add my own voice to the legion of many others... but for what purpose apart from the desire to stroke my own ego ever so slightly?

Well, hopefully this new blog will help to educate those of you on the same journey as I am - not to mention document most of the new cool stuff that I come across on said journey.

I regularly visit blogs of various nature, however most of them have one theme in common. Tips, tricks, methods and projects relating to Actionscript 3 and my own personal lord and saviour - Flash Professional (praise be to him).

For a while now I've fought the desire to start up a blog of my own to cover the same kind of content because I didn't want to come across as preachy or vain in an area that I have no formal qualifications in.

So why give in to temptation now? It is simple. My work ethic includes a procedure. I get to my desk at 9am every work day... and I do not leave it to go home until I feel that I have learnt something new or have expanded my knowledge of Flash and Actionscript.

I have been working as a professional Flash developer on a full-time basis now for close to 2 years and prior to that, as a contractor after-hours for close to 6 years. In that time I have worked on quite a few websites and web-applications. But what I am most recognized for are my flash games - amongst them is a little horror point-n-click series called Exmortis.

As you can imagine over the years I have acquired a reasonably in depth knowledge of Flash and Actionscript... and yet I still have so much more to learn. As a result, I primarily consider this blog as a way of documenting what I learn for my easy future reference and maybe - just maybe someone might stumble upon these words and also take something out of it to help build upon their own skill set.

I must also point out that I am not formally trained, nor have I a great depth of knowledge when it comes to Object Oriented Programming (OOP) methodologies - so I may not always be right nor will I always be putting forward the best/most efficient method of accomplishing something... so I always welcome comments and feedback on the content within this blog.

So without further adieu... let the fracas begin!

Cheers,
Ben Leffler