More moving

Now we will look how to allow hero walk anywhere on stage, not only between centers of tiles:

Notice how hero changes the graphics when switching directions. For such magic to happen, we need to update the herosheet bitmap with images for each direction:

When hero moves, he picks correct image from the spritesheet. We include different sprite numbers for each key:

keys[Keyboard.UP] = {down:false, dirx:0, diry:-1, sprNum:1};
keys[Keyboard.DOWN] = {down:false, dirx:0, diry:1, sprNum:0};
keys[Keyboard.LEFT] = {down:false, dirx:-1, diry:0, sprNum:3};
keys[Keyboard.RIGHT] = {down:false, dirx:1, diry:0, sprNum:2};

We will add speed value to hero in direction of pressed arrow key and check if the tile where hero wants to walk, is walkable. If it is, we will simply move the hero. If the tile is not walkable (the hard brick wall type), then movement is not possible. But wait, there are problems.

This is perfect collision with wall:

Hero stands next to wall and in next step he would be inside the wall. We cant let it happen, so we wont. No moving, man! But world is not perfect, unlike in previous chapter where hero was able to move only between centers of tiles, it may now be only partially colliding with them:

That requires us to check for collision between hero and wall with every corner points of the hero. If any of the corners (lower left corner in this example), would end up inside the wall, we will stop the hero.

Or if hero is not next to wall yet, but would still go inside the wall if you allow him to step there:

We will have to adjust hero by placing him next to the wall:

"Oh, no!" you might cry, "All this is impossible to do!" Not to worry, nothing is impossible.

Give me my Corners

We dont want parts of our character go inside wall so we have to check collision between hero and the unwalkable object using not one, not even two, but four points. We will use corner points, expecting most of heroes look like rectangles (they DO!).

For the purpose lets make new function called getMyCorners:

function getMyCorners (x:Number, y:Number, ob:Object):Boolean {
    var upY = Math.floor (y / ts);
    var downY = Math.floor ((y + ob.ts - 1) / ts);
    var leftX = Math.floor (x / ts);
    var rightX = Math.floor ((x + ob.ts - 1) / ts);
    var upleft = tiles["t_" + upY + "_" + leftX].walkable;
    var downleft = tiles["t_" + downY + "_" + leftX].walkable;
    var upright = tiles["t_" + upY + "_" + rightX].walkable;
    var downright = tiles["t_" + downY + "_" + rightX].walkable;
    return upleft && downleft && upright && downright;
}

This function accepts 3 arguments: new x/y coordinates of the top-left corner of object on stage and the bravely moving object itself. Wait, we already know x/y position of the object, we have it saved in the hero, you may wonder. Thats true, but we have saved the CURRENT position, here we are dealing with the position the hero WOULD BE if it would move.

First we calculate the tiles where object extends. Its one corner may be on one tile, but its other corners may be all in different tiles so we have to check all of them. By dividing coordinate with the size of tiles gives as tile numbers. Floor is built-in mathemathical function to round the number to lowest integer. For down and right tile we add size of hero before dividing.

Last 4 lines use points we calculated to get the value of walkable property in each tile under the corners. For example upleft corner uses upY and leftX variables. Only if all 4 corners are walkable, can hero walk and function returns true.

Move

Once we know the type of tile each corner of character will be on, we can easily write movement for the char: if all the corners are walkable, then move, else dont move. More work is needed to place the hero right next to wall in case the collision would happen.

Hero must have speed to move so we give him one in prepareGame function:

hero.speed = 3;

The moveObject function will first add speed to coordinates of hero in correct direction and then calculates new tile for hero to stand on:

function moveObject (ob:Object, moveOb:Object) {
    ob.x += moveOb.dirx * ob.speed;
    ob.y += moveOb.diry * ob.speed;
    ob.xtile = Math.floor (ob.x / ts);
    ob.ytile = Math.floor (ob.y / ts);
    if (ob.moveOb != moveOb) {
        ob.sprNum = moveOb.sprNum;
	ob.bmp.bitmapData = getImageFromSheet (ob, ob.sprNum).bitmapData;
	ob.moveOb = moveOb;
    }
    ob.bmp.x = ob.x;
    ob.bmp.y = ob.y;
}

We also check here if hero is using new direction for movement, in such case we need to change image using getImageFromSheet function.

Last, the runGame function will now check for corners before moving:

if (keyOb.down == true) {
  var moveOb:Object = keyOb;
  if (getMyCorners (hero.x + keyOb.dirx * hero.speed,
hero.y + keyOb.diry * hero.speed, hero) == false) {
    if (keyOb.dirx != 0) {
        var dx = (keyOb.dirx + 1) / 2;
        hero.x = hero.xtile * ts + dx * (ts - hero.ts);
    } else {
        var dy = (keyOb.diry + 1) / 2;
        hero.y = hero.ytile * ts + dy * (ts - hero.ts);
    }
    moveOb = {dirx:0, diry:0, sprNum:keyOb.sprNum};
    }
  moveObject (hero, moveOb);
  break;
}

If hero is free to move, moveObject function is simply called using keyOb. However, if hero is about to hit the wall, we first place the hero next to the wall. We do placing in 2 parts, only horisontally (keyOb.dirx != 0) or vertically. The idea is that if hero is moving left (or up) then we have to place him at coordinate of current tile, in case he is moving right (or down), he will end up standing at coordinates with difference bewteen tile and hero added to the current tile. Possible movements in key objects have values of 1 or -1, we convert those to values 0 or 1 using temporary variables dx and dy.

Since we dont align bitmap image into center of tile, we need to place the hero itself there in the buildmap function:

hero.x = hero.xtile * ts + (ts - hero.ts) / 2;
hero.y = hero.ytile * ts + (ts - hero.ts) / 2;


You can download the source fla with all the code and movie set up here.

Next: Animating sprites.

Top