Wednesday, September 1, 2010

Creating a simple tile system in AS3: Part 2

Today we create our character and introduce movement!

Firstly, I'd like to say that in these tile tutorials, I write all the code on the first frame of the timeline. I do not use classes, because the goal is to explain how everything works, the logic behind tiles. But when you understand the basics, you, of course, will be able to create your own ultimate engine, using classes and possibly other external files (XML for tile maps, perhaps).

Right, let's start off from where we finished last time. We have a piece of code that creates our map... now we need to make a character and be able to move him around with arrow keys. Tile collision might sound unusual and complicated, but it really isn't.

First we need to create a MovieClip, give it a class name of hero. Inside it, draw a square (32x32 pixels) with coordinates 0,0. This way, we have the upper left corner of the square in the center of 'hero' MovieClip. Draw your character in the square (the square is just there to show you the borders of your drawing area), it's a top-down-view tile engine, so your character will be turned around when he goes in different directions. Make sure his face is facing down.

Now convert your drawing into another MovieClip (with an instance name of gfx), and registration point in the center, because that's the point we will be turning our character around.

So now you have a MovieClip (with a class name 'hero') in library, that contains another MovieClip (with an instance of 'gfx') that has its borders' upper left corner in the center of 'hero' MC.


Let's set up some variables and spawn our hero.

var heroSpawnX=4
var heroSpawnY=2

var character:hero = new hero();
addChild(character)
character.x=heroSpawnX * tileSide
character.y=heroSpawnY * tileSide

var heroX=heroSpawnX
var heroY=heroSpawnY

The first two variables, heroSpawnX and heroSpawnY, are the tile coordinates, where our character will spawn on start.

The next line creates a hero class object, called character. We add it to the stage, set its coordinates. HeroX and heroY are the current coordinates of our character. It will change every time we move our hero.

The movement function is all that is left! Create a KeyboardEvent.KEY_DOWN listener and the function, that performs actions when a certain key is pressed:

stage.addEventListener(KeyboardEvent.KEY_DOWN, movement);

function movement(event:KeyboardEvent){
 if(event.keyCode==40 && myMap[heroY+1][heroX]==0){
  character.gfx.rotation=0
  character.y+=tileSide
  heroY++
  }
 if(event.keyCode==38 && myMap[heroY-1][heroX]==0){
  character.gfx.rotation=180
  character.y-=tileSide
  heroY--
  }
 if(event.keyCode==39 && myMap[heroY][heroX+1]==0){
  character.gfx.rotation=270
  character.x+=tileSide
  heroX++
  }
 if(event.keyCode==37 && myMap[heroY][heroX-1]==0){
  character.gfx.rotation=90
  character.x-=tileSide
  heroX--
  }
 }

There are 4 blocks, for 4 arrow keys. I will explain one:

if(event.keyCode==40 && myMap[heroY+1][heroX]==0){ - if down arrow key is pressed, and if the tile below our hero is grass, perform action. We check the tile below by using the myMap array and our current hero coordinates.
character.gfx.rotation=0 - rotate according to direction.
character.y+=tileSide - move the character.
heroY++ - update variable.

And you're done!


The full code:

// Create map

var mapWidth = 10;
var mapHeight = 10;
var tileSide = 32;
var totalTiles = mapWidth * mapHeight;
var myMap:Array = [
   [1,1,1,1,1,1,1,1,1,1],
   [1,0,0,0,0,0,0,0,0,1],
   [1,0,0,0,0,0,0,0,0,1],
   [1,0,0,0,0,0,0,0,0,1],
   [1,0,0,0,0,0,1,1,0,1],
   [1,0,0,0,0,0,1,0,0,1],
   [1,0,0,1,0,0,1,0,0,1],
   [1,0,0,1,1,1,1,0,0,1],
   [1,0,0,0,0,0,0,0,0,1],
   [1,1,1,1,1,1,1,1,1,1]
   ];

for (var i:int=0; i<mapHeight; i++)
{
 for (var u:int=0; u<mapWidth; u++){
  var cell:MovieClip = new tile();
  cell.gotoAndStop(myMap[i][u]+1);
  cell.x=tileSide*u
  cell.y=tileSide*i
  addChild(cell);
 }
}

// Hero management

var heroSpawnX=4
var heroSpawnY=2

var character:hero = new hero();
addChild(character)
character.x=heroSpawnX * tileSide
character.y=heroSpawnY * tileSide

var heroX=heroSpawnX
var heroY=heroSpawnY

// Basic movement

stage.addEventListener(KeyboardEvent.KEY_DOWN, movement);

function movement(event:KeyboardEvent){
 if(event.keyCode==40 && myMap[heroY+1][heroX]==0){
  character.gfx.rotation=0
  character.y+=tileSide
  heroY++
  }
 if(event.keyCode==38 && myMap[heroY-1][heroX]==0){
  character.gfx.rotation=180
  character.y-=tileSide
  heroY--
  }
 if(event.keyCode==39 && myMap[heroY][heroX+1]==0){
  character.gfx.rotation=270
  character.x+=tileSide
  heroX++
  }
 if(event.keyCode==37 && myMap[heroY][heroX-1]==0){
  character.gfx.rotation=90
  character.x-=tileSide
  heroX--
  }
 }

Remember, that this is a very simple tile tutorial, the movement is dodgy, but the goal was to explain how tiles work. This system can be used to create many different tile-based games, such as tetris. With more tweaking, this engine can be used to create RPGs. Right now, it's simply the base for new buildings to be built on it.

Thanks for reading!

Related:

Creating a simple tile system in AS3: Part 1

5 comments:

Anonymous said...

Very nice tutorial, just what i was looking for! :)

I am having a problem though. When i test the movie and press one of the arrow keys i get this error:

TypeError: Error #1010: A term is undefined and has no properties.
at NewGame_fla::MainTimeline/movement()

I can't seem to figure out what's wrong, it all looks like it should be working. I copied the code from your post and made a movieclip with name hero, exported it for actionscript as hero, and drew another movieclip inside called gfx. Help please :)

Kirill Poletaev said...

Are you sure you have a 'gfx' movieclip inside your hero?... Which line does the debugger say is faulty?

Anonymous said...

Awesome tutorial.

I have a little question about the following string:
var character:hero = new hero();

Can you explain why did you write a type ":hero" but not ":MovieClip" as you did above ( var cell:MovieClip = new tile();)? As I understand, in that particular case, MovieClip and tile/hero classes are the same. So there is no difference between them. Am I right?

Thanks for the answer!

PS Greetings from Ukraine =)!

Kirill Poletaev said...

You can do :MovieClip as well, it will work the same.

Anonymous said...

muy bueno!!

Post a Comment