Canvas Isometric Tutorial – Add a player and throw in some objects Pt2.

Walls and other solid objects

This will be pretty easy to do and apart from a few extra condition checks in your movement code everything else is really just a case of duplicating.

Obviously to begin with we are going to need a wall tile, for this tutorial I decided to go with the orange wall of blockade. Took a while to plan out and design before creating such a tile but here it is if you wish to use it, carefully.

The wall as usual is a .png. The .png canvas height is made to be 50 pixels, double the ground tile height. This means our object can be any height between 0 and 50 pixels and by halving the height attribute of the object we can always line it up with ground tile it is on top of.

For speed and ease I’ll be going back to the original source of tutorial 02 where the map is just directly in the array rather than loaded as XML from a database. Ralph, our playable character has also been added in. It shouldn’t be hard at all though to use a later code version as I said it really is just a case of repeating some steps.

Current source of all the isometric tutorials minus the loading/saving can be seen here:

http://glacialflame.com/tutorials/tiles/06/source.html

Step One – loading our isometric objects

Adding objects is simple. Duplicate the ground tile code and then draw it on top once our ground layer has been drawn. Using a second array for objects means for simplicity all we have to do is look up the array coordinates relating to the players desired position, if there is an entry then that coordinate is impassable.

So duplicate the map array and imagine for just now zero is passable and one is a wall.

var objectMap = Array([0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,1,1,0,0],[0,1,1,0,0,1],[1,0,1,1,0,1],[0,0,0,0,0,0],[1,0,1,0,1,0],[1,1,1,0,0,0]);

Also similar to when you added your playable character, duplicate the image arrays and update the loader. Around the same area as your tileDict and tileImg add:

var objectDict = Array("wall.png");
var objectImg = new Array();

In your loadImg() function add:

	for(var i=0;i<objectDict.length;i++){
		objectImg[i] = new Image();
		objectImg[i].src = objectDict[i];
		objectImg[i].onload = function(){
			loaded++;
		}
	}

in your loadAll() function update your loaded condition to check if all object images have been loaded like so:

if(loaded == tileDict.length + charDict.length + objectDict.length){

All done!

Step Two – Let the user personally reflect over our wall object

In order to draw the objects into our map go the function drawMap() and within the loops add the following, underneath var drawTile = ma… is just fine:

var drawObj = objectMap[i][j];

Now we want objects to be drawn on top of our ground so under our ctx.drawImage(tileImg[drawTile],xpos,ypos) and before our character drawing add the following:

if(drawObj){
ctx.drawImage(objectImg[drawObj-1],xpos,ypos-(objectImg[drawObj-1].height/2));
}

This should explain itself, if drawObj (which is equal to the current drawing coordinate in our array ) is anything above zero then… well… draw it. The reason for drawObj-1 is because our object images start at zero however in our object array we start at 1. Otherwise every 0 in our array would represent a wall.

You should now be able to run Ralph around the map behind and in front of walls.

Step Three – HALT! You can’t walk there Ralph

Finally we update our movement code to check if there is an object or not.

Within our checkKeycode(event) function we can throw in the following around our current movement code:

if(!objectMap[playerX-1][playerY]){
playerX--;
}

The if check makes sure that the desired players coordinates within the objectMap array is false allowing the player to move that direction. This is done for each direction like so:

case 38:
if(!objectMap[playerX-1][playerY]){
playerX--;
}
break;
case 40:
if(!objectMap[playerX+1][playerY]){
playerX++;
}
break;
case 39:
if(!objectMap[playerX][playerY-1]){
playerY--;
}
break;
case 37:
if(!objectMap[playerX][playerY+1]){
playerY++;
}
break;

And that’s it. Ralph can now run about our fields with only the orange walls of blockade preventing his fun.

Full source can be found here:

http://glacialflame.com/tutorials/tiles/06/

As always let me know how you get on!

All the best.

This entry was posted in Canvas, Javascript, Tile World, Tutorial. Bookmark the permalink.

11 Responses to Canvas Isometric Tutorial – Add a player and throw in some objects Pt2.

  1. Ste says:

    Hi,
    Your tutorials are great! Love your work!
    I’ve been using this for a little personal project,
    but there is something that I can’t figure out:
    While hovering over a tile, I would like to make it possible to highlight
    not one tile but two tiles. I changed the lineTo coordinates in drawMap(), but that doesn’t seem to enough.
    Could you help me out?

    Thanks!

    • Iain Hamilton says:

      Hi Ste,

      when you click a tile it currently looks up the array position by matching your x and y mouse coords to that entry. If you edit it to reflect:

      map[xmouse][ymouse]= tile;
      map[xmouse+1][ymouse]= tile;

      It will update both array entrys.

  2. adailton says:

    sorry my english I’m from Brazil

    very good tutorial held with you and am very eager to see how the avatars that walk on the tiles with the click of a mouse.

    • Iain Hamilton says:

      Hi there, we should have a tutorial covering pathfinding up in the next few weeks! Got some smaller stuff to cover before then :) .

  3. First of all, thanks a ton for these guides/tutorials! They are really helpful when trying to learn how to take advantage of the canvas element. Awesome job guys!

    Any chances of more guides covering the particle engine or how things like attacks/arrows are handled? It can probably be handled by the client, but that would make the game vulnerable for cheating, wouldn’t it?

    A few other things which are still a mystery to me, how does AI work? Is this done server-side as well? Also, is it possible to make/render things like stairs and such (having a z-axis of the map)

    Again, thanks a ton for these guys, really awesome! :-)

  4. Niels says:

    Nice tutorial!

    Ralph can still walk/slide/move to undefined map spaces, though. I fixed this by checking whether the map x/y was undefined or not. While working on this, I changed some of the input handling code, too, like so:


    var newX = playerX; // local copies
    var newY = playerY;

    switch(keyCode)
    {
    case 38: newX--; break;
    case 40: newX++; break;
    case 39: newY--; break;
    case 37: newY++; break;
    }

    if(objectMap[newX] && objectMap[newX][newY] !== 1 &&
    map[newX] && map[newX][newY] !== undefined)
    {
    playerX = newX;
    playerY = newY;
    }

    IMO it looks cleaner this way, but that’s just a matter of style I guess.

    Niels.

  5. Timo says:

    Hi, nice tutorials – thank your for that!
    I tried your tuts for understanding isometric games. now i’m working on a project to learn more.
    I tried to build a house with walls. For getting a realistic optic, i made small walls. so they are between the tiles. for example:
    X|XXX
    X|XXX

    i dont want to block a tile, i just want to detect the walls so that the player can move on the tiles around but not through the wall.

    any ideas how to fix my problem?
    And sorry for grammar – i’m german :)

    Timo.

    • Edward Smyth says:

      Hey Timo,

      Hopfully this helps, it is a simple fix for the problem using the example talked about in this tutorial as a base.

      The way I would go about representing the walls rather than just having:
      0 = No wall
      1 = Wall

      I would represent the different walls for each tile as a binary value from (0000) to(1111). These could be either stored in the object array as Hex values (0 to F) or Decimal values (0 to 15).
      [ http://www.ecawa.asn.au/home/jfuller/binary/binary7.htm for details on hex/decimal/binary! ]

      Using these binary representations each bit would correspond to a particular side of the tile as having a wall or not.

      So for example:
      1000 = Top edge of tile is a wall
      0100 = Right edge
      0010 = Bottom Edge
      0001 = Left Edge
      0000 = No wall
      1010 = Top and Bottom edge has wall
      etc….

      This will enable us to store all possible wall combinations for each tile.

      Now for movement checking, Using the example
      given in this tutorial a number of modifications are required.

      The main change is we will need to check both the tile we are leaving as well as the tile we are entering. Also we will need to check the value stored in the object array as a binary value and use a bitwise operation on it to validate if a wall is present.

      Here is the modified code from the tutorial given (I havent actually checked it but hopfully this should work!)


      // Same code as before with extra checks!
      // 0x1 = Wall Left Edge
      // 0x2 = Wall Bottom Edge
      // 0x4 = Wall Right Edge
      // 0x8 = Wall Top Edge

      var current_tile = parseInt(objectMap[playerX][playerY], 16);
      case 38:
      if(
      !(current_tile & 0x1) && // Check current tile left edge for wall
      !(parseInt(objectMap[playerX-1][playerY], 16) & 0x4) // Check tile you are entering right edge for wall
      ){
      playerX--;
      }
      break;
      case 40:
      if(
      !(current_tile & 0x4) && // Right Edge
      !(parseInt(objectMap[playerX+1][playerY], 16) & 0x1) // Left Edge
      ){
      playerX++;
      }
      break;
      case 39:
      if(
      !(current_tile & 0x2) && // Bottom Edge
      !(parseInt(objectMap[playerX][playerY-1], 16) & 0x8) // Top Edge
      ){
      playerY--;
      }
      break;
      case 37:
      if(
      !(current_tile & 0x8) && // Top Edge
      !(parseInt(objectMap[playerX][playerY+1], 16) & 0x2) // Bottom Edge
      ){
      playerY++;
      }
      break;

      I’m not sure if this is the best way but it was what initially jumped to mind when i read your problem hope it helped. If you have any further questions on this or anything else feel free to post again or contact us via e-mail.

      Edward

      (As a small extra note if you use hex representation for the binary values they will need to be stored as strings in the object array)

  6. Alexander says:

    Hello,

    I just love your site! But I have a few problems, since I don’t understand the map array so how does it work: what means every 0 or 1?

    Also I see white lines between tiles, do you have a fix for that?

    I just love the canvas element!

    Here is my (small) contribution:

    var antiCache = Math.random() * 100;
    var map = Array([1,2,2,0,1,1],[1,0,0,1,1,1],[0,0,2,1,1,0],[1,2,2,1,1,1],[1,0,0,0,1,1],[1,0,0,1,1,2],[0,0,1,1,1,2],[1,1,1,1,2,2],[1,2,2,0,1,1],[1,0,0,1,1,1],[0,0,2,1,1,0],[1,2,2,1,1,1],[1,0,0,0,1,1],[1,0,0,1,1,2],[0,0,1,1,1,2],[1,1,1,1,2,2],[1,2,2,0,1,1],[1,0,0,1,1,1],[0,0,2,1,1,0],[1,2,2,1,1,1],[1,0,0,0,1,1],[1,0,0,1,1,2],[0,0,1,1,1,2],[1,1,1,1,2,2],[1,2,2,0,1,1],[1,0,0,1,1,1],[0,0,2,1,1,0],[1,2,2,1,1,1],[1,0,0,0,1,1],[1,0,0,1,1,2],[0,0,1,1,1,2],[1,1,1,1,2,2]);
    var tileDict = Array("images/tiles/water.png?r=" + antiCache,"images/tiles/land.png?r=" + antiCache,"images/tiles/road.png?r=" + antiCache);
    var charDict = Array("images/char/ralph.png?r=" + antiCache);

    It adds a random char to make sure the images don’t get cached when you post new images online. Also I changed the moving. I din’t like the orientation, so here is my new movement code:


    case 38:
    playerY--;
    break;
    case 37:
    playerX--;
    break;
    case 39:
    playerX++;
    break;
    case 40:
    playerY++;
    break;
    default:
    break;

    I hope I have helped someone with this and I hope you can help me with my problem!

    Yours,
    Alexander

    • Iain Hamilton says:

      Hi Alexander, did you manage to get your issue fixed? Sorry there was no reply for such a long period. If you haven’t and you’re still interested feel free to reply with a link to your current issue and I’ll see if I can help fix it.

      Best,

      Iain

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>