# Smooth animation on a persistently refreshing canvas

Yo everyone!

I have been working on an Isometric Tile Game Engine in HTML5/Canvas for a little while now and I have a complete working game. Earlier today I looked back over my code and thought: "hmm, let's try to get this animated smoothly..."

And since then, that is all I have tried to do.

The problem
I would like the character to actually "slide" from tile to tile - but the canvas redrawing doesn't allow this - does anyone have any ideas....? Code and fiddle below...

Fiddle with it! http://jsfiddle.net/neuroflux/n7VAu/

<html>
<title>tileEngine - Isometric</title>
<style type="text/css">
* { margin: 0px; padding: 0px; font-family: arial, helvetica, sans-serif; font-size: 12px; cursor: default; }
</style>
<script type="text/javascript">
var map = Array( //land
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
);
var tileDict = Array("http://www.wikiword.co.uk/release-candidate/canvas/tileEngine/land.png");
var charDict = Array("http://www.wikiword.co.uk/release-candidate/canvas/tileEngine/mario.png");
var objectDict = Array("http://www.wikiword.co.uk/release-candidate/canvas/tileEngine/rock.png"); //last is one more
var objectImg = new Array();

var charImg = new Array();
var tileImg = new Array();

var ymouse;
var xmouse;
var eventUpdate = 0;

var playerX = 0;
var playerY = 0;

for(var i=0;i<tileDict.length;i++){
tileImg[i] = new Image();
tileImg[i].src = tileDict[i];
}
}
i = 0;
for(var i=0;i<charDict.length;i++){
charImg[i] = new Image();
charImg[i].src = charDict[i];
}
}
i = 0;
for(var i=0;i<objectDict.length;i++){
objectImg[i] = new Image();
objectImg[i].src = objectDict[i];
}
}
}

function checkKeycode(event) { //key pressed
var keycode;
if(event == null) {
keyCode = window.event.keyCode;
} else {
keyCode = event.keyCode;
}
switch(keyCode) {
case 38: //left
if(!map[playerX-1][playerY][1] > 0){
playerX--;
}
break;
case 40: //right
if(!map[playerX+1][playerY][1]  > 0){
playerX++;
}
break;
case 39: //up
if(!map[playerX][playerY-1][1]  > 0){
playerY--;
}
break;
case 37: //down
if(!map[playerX][playerY+1][1]  > 0){
playerY++;
}
break;
default:
break;
}
}

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

function drawMap(){ //draw the map (in intervals)
var tileH = 25;
var tileW = 50;
mapX = 80;
mapY = 10;
for(i=0;i<map.length;i++){
for(j=0;j<map[i].length;j++){
var drawTile= map[i][j][0];
var xpos = (i-j)*tileH + mapX*4.5;
var ypos = (i+j)*tileH/2+ mapY*3.0;
ctx.drawImage(tileImg[drawTile],xpos,ypos);

if(i == playerX && j == playerY){
you = ctx.drawImage(charImg[0],xpos,ypos-(charImg[0].height/2));
}
}
}
}

function init(){ //initialise the main functions and even handlers
ctx = document.getElementById('main').getContext('2d');
document.onkeydown = checkKeycode;
}

function gameUpdate() { //update the game, clear canvas etc
ctx.clearRect(0,0,904,460);
ctx.fillStyle = "rgba(255, 255, 255, 1.0)"; //assign color
drawMap();
}
</script>
<canvas id="main" width="904" height="465">
<h1 style="color: white; font-size: 24px;">I'll be damned, there be no HTML5 &amp; canvas support on this 'ere electronic machine!<sub>This game, jus' plain ol' won't work!</sub></h1>
</canvas>
</body>
</html>


You can animate your character by interpolating from the initial position to the target position.

You would have to update the position of the player inside your gameUpdate method, before calling drawMap();. To avoid strange movements, you'll have to lock/ignore player input for that period of time. This can be done easily by setting a flag isAnimating to true and simply ignoring key-input when isAnimating == true.

If your game gets more fleshed out, your game code will increase. If that's the case it's probably a good idea to separate your code into classes (I know, not so easy with JavaScript) and also implement a proper game-loop: Tips for writing the main game loop?

• Thank you for the response, is there any chance you could edit my JSFiddle example with an idea of which direction to go in? I'm so confused. May 9 '11 at 10:53
• There you go: jsfiddle.net/n7VAu/2 Although this doesn't incorporate any best practices.. for example the movement calculation shouldn't be in the draw method.. but it's just an example. May 9 '11 at 11:32

I think it would be best if your tiles are on a separate canvas. This would make what you want really easy - you just clear the top canvas on each frame, while the background remains. As bummzack said, you might have to lock controls during the animation.

Otherwise you could clear only the origin and destination tiles and redraw them on each frame, but this seems like more (unnecessary) work.

Btw using the arrow keys on a 45 degree isometric map makes controls very difficult for me. Better use some of the letter keys, as they are laid out in a 'hex' pattern, making diagonal movements more intuitive.

• Hi RecencyEffect. :) No need to worry about late replies to old questions. It's perfectly acceptable here. Oct 11 '12 at 13:39