# Canvas tile grid, hover effects, single tilesheet, etc

I'm currently in the process of building both the client and server side of an HTML5, canvas, and WebSocket game.

This is what I have thus far for the client: http://jsfiddle.net/dDmTf/20/

### Current obstacles

1. The hover effect has no idea what to put back after the mouse leaves. Currently it's just drawing a "void" tile, but I can't figure out how to redraw a single tile without redrawing the whole map.
2. How would I go about storing multiple layers within the map variable? I was considering just using a multi-dimensional array for each layer (similar to what you see as the current array), and just iterating through it, but is that really an efficient way of doing it?
3. Currently the "borders" encroach on one another. I'm not sure what I'm doing wrong? If you hover over and off of the bordering "tree" tiles, you'll notice how it removes part of the neighboring tiles border. This shouldn't need to happen.
4. Outside of the map, what would be an elegant way of adding Players to the render scene? I've seen a few places that recommend you use multiple canvas's, would you recommend that since they'll probably be moving much more? In addition to that, how can I confine player movement to the grid, similar to how in the pokemon gameboy games, when you tap the left arrow, you always move one "space" that direction. It's not a halfway type deal.
5. Camera. What would be a good way about adding a "camera" to the equation, so the viewport can pan around a larger map area?
6. Obstacles. In the newer version of the map, you'll notice an empty obstacles array. Assuming players get added well, what do you think would be good for adding movement obstacles?

### Side note

The tile sheet being used for the jsfiddle display is only for development. I'll be replacing it as things progress in the engine.

If you guys have any pointers for my JavaScript, feel free. As I'm more or less learning advanced usage as I go, I'm sure I'm doing plenty of things wrong.

Note: I will continue to update this post as the engine improves, but updating the jsfiddle link and updating the obstacles list by striking things that have been solved, or adding additions.

### Edit

Updated link from http://jsfiddle.net/dDmTf/7/ to http://jsfiddle.net/dDmTf/17/ - this handles the "hover" problem, and it's been striked out.

### Edit

Updated link from http://jsfiddle.net/dDmTf/17/ to http://jsfiddle.net/dDmTf/19/ - fixes the out of bounds issue with the tiles

### Edit

Updated link from http://jsfiddle.net/dDmTf/19/ to http://jsfiddle.net/dDmTf/20/ - adds multiple layers to the equation

• You shouldn't be editing more and more questions to the same post. Ideally each post should have one clear question. You should revise it to contain only information on the original question, give it a clearer title regarding that question, mark it as answered, and then ask the rest in a separate question. There's even a limit to how many times you can edit your question before it gets turned into community wiki. – David Gouveia Apr 8 '12 at 9:20
• In other words, revise the question so that it focuses mostly on the hovering problem (title and content), and leave the rest for separate and more focused posts. – David Gouveia Apr 8 '12 at 9:39
• @DavidGouveia Sorry, I'm new to stackexchange. I'll move my new questions to a new post for now on :) – syntaqx Apr 8 '12 at 10:46
• You should also upvote and mark as an answer using the icons on the leftvside ofvthe posts. Well just read the FAQ :-) – David Gouveia Apr 8 '12 at 21:25

I wouldn't call it an elegant solution or anything but at least it succeeds in solving the hovering problem without having to redraw the entire map. It redraws a 3x3 radius around the last mouse position instead, since that's the amount of tiles affected by your hover rectangle. Here's the new fiddle and below are the changes I made to the original one.

1. Extracted the inner part of your renderMap method to a separate method:

Renderer.prototype.renderTileAt = function(map, tilesheet, i, j) {
var row = i * this.tilesize;
var col = j * this.tilesize;
if(map && (i in map) && (j in map[i])) {
this.drawTile(tilesheet, col, row, map[i][j][0], map[i][j][1]);
} else {
this.drawCellRect('#333', col, row);
}
}

Renderer.prototype.renderMap = function(map, tilesheet) {
for(var i = 0; i < (this.getHeight() / this.tilesize); i++)
for(var j = 0; j < (this.getWidth() / this.tilesize); j++)
this.renderTileAt(map, tilesheet, i, j);
}

2. Since the red border "eats" into neighbour tiles I draw a 3x3 radius around the last mouse position instead:

if((lastX !== null && lastX != x) || (lastY !== null && lastY != y)) {
var lastI = lastY / renderer.tilesize;
var lastJ = lastX / renderer.tilesize;
for(var i=lastI-1; i<=lastI+1; ++i)
for(var j=lastJ-1; j<=lastJ+1; ++j)
renderer.renderTileAt(map, sprite, i, j);
}


As for adding layers, yes just add a third dimension to your map array with how many layers you need.

Edit 1

Another alternative that would simplify the hover problem would be to draw the hover rectangle a little smaller so that it doesn't eat into the neighbours. For instance:

Renderer.prototype.drawHoverRect = function(color, x, y) {
this.context.save();
this.context.lineWidth = 2;
this.context.strokeStyle = color;
this.context.translate(x, y);
this.context.strokeRect(1, 1, this.tilesize-2, this.tilesize-2);
this.context.restore();
}


Also, make drawCellRect call clearRect at the top of the method. With both of those changes, instead of drawing the 3x3 region above, a single call is enough:

renderer.renderTileAt(map, sprite, lastY / renderer.tilesize, lastX / renderer.tilesize);


Updated fiddle here.

Edit 2

Yet another improvement, this time to the entire mouse move event handler (assuming the changes above are already in place):

var lastX = 0;
var lastY = 0;

var x = (e.pageX / renderer.tilesize) | 0;
var y = (e.pageY / renderer.tilesize) | 0;
if(lastX == x && lastY == y) return;
renderer.renderTileAt(map, sprite, lastY, lastX);
renderer.drawHoverRect('red', x * renderer.tilesize, y * renderer.tilesize);
lastX = x;
lastY = y;
})


It uses a trick found here to cast to int. I also got rid of those null checks and replaced them with a default value that does not do much harm. Once again, updated fiddle here.

• Awesome :) I appreciate the help on that. I've noticed one problem though, which you might already be aware of. The outer-most cells in the map (the trees) have border encroachment from their neighbor cells. An example of this, is if you hover over and then off-hover the tree cell, you'll see the border area is very different from the cells next to it. Thoughts? – syntaqx Apr 8 '12 at 4:12
• @user121730 If I even understood what you mean, then it's because your drawCellRect method which uses a line thickness of 2 ends up drawing of the tile's bounds. When another tile is drawn on top of it, the extra portion that was outside of the tile's bounds gets cropped. So simply don't draw outside of the tile's bounds. There are many possible solutions, such as drawing the grid like this instead. – David Gouveia Apr 8 '12 at 5:12
• @user121730 Or this. And on the previous comment I meant "ends up drawing outside of the tile's bounds" but only noticed the typo after 5 minutes. – David Gouveia Apr 8 '12 at 5:18
• @user121730 Also, I don't see what's your problem with multiple layers. It's exactly as you had considered. It's just a 3 dimensional array. It could either be map[layer][x][y] or map[x][y][layer]. The first one would be the easiest. Just do var mapData = new Array() and then mapData[0] = /* what you already have */, mapData[1] = /* second layer */ and so on. – David Gouveia Apr 8 '12 at 5:31
• Fixed the issue with the tiles, should've seen that one :P Updated link in the primary post. @David Gouveia: That just seems like it would be a lot of writes to the canvas? Is there really not a better way to do it? – syntaqx Apr 8 '12 at 5:38