HTML5 Canvas game, multiple layers problem

I am creating one of my first HTML5 games. It's a game like War War / War of Legends. It is an isometric tile based MMORTS action game. Now, when I build a game, I try to keep resource management and speed in mind; however I ran into a problem.

I have a method called renderMap

which follows these steps

  • renderMap

    loads the current map fragment. Currently, the map tile is defined as a 6x6 grid.
  • renderMap

    crosses each individual tile from ASCENDING X, Y to DESCENDING X, Y and draws that tile on an isometric grid. When drawing a tile on the screen, it also checks to see if a building is on that tile and draws that building on top of the tile.

Now, since it renderMap

has to call drawImage

about 36 times for every single chunk that is part of a chunk, it really starts to become a resource swamp if the function were to be called every frame. So, I made a renderMap

draw to an in-memory canvas that accesses the main canvas every frame. Thus, 36 calls drawImage

are reduced to one frame.

And the only time I need to call renderMap

is if the tiles or buildings are updated anyway. For example, if a new piece is loaded and we need to load a new isometric map.

However, what if I want these buildings or tiles to have animation?

For example, I add a building with a name to the game oil well

. If the oil well is on any of the tiles in a 6x6 chunk that is loaded into the client player and rendered, this will cause the game to be called renderMap

so many times to render that one new building (because of the animation). So I end up selecting 35 additional tiles to update this 1 building.

What can I do about this? Is it best not to include animation?

This is what the game looks like, so it might help in adding context to the problem:

(sorry the picture is cluttered ... I was doing some testing)

image of game

+3


source to share


1 answer


Create a sprite containing all the images needed to animate your oil well.

Then you can pull the sprites out of that sprite and overlay your rendered map onto this single oil plate.

Then you render the large map once. You only render this tile a few times (more efficient than re-rendering the entire map to animate one tile). You can use a second overlaid canvas for better performance. You can even make an overlaid canvas that is just the size of a single sprite and use CSS positioning to move across the target plate.



Sample code and demo:

var canvasT=document.getElementById("canvasTop");
var ctx=canvasT.getContext("2d");
var canvasB=document.getElementById("canvasBottom");
var ctxB=canvasB.getContext("2d");
var cw,ch;

var nextTime=0;
var duration=1000/60*3;
//
var spritesheetWidth=256;
var spritesheetHeight=256;
var spriteCols=4;
var spriteRows=4;
var spriteCount=spriteRows*spriteCols;
var spritePosition=0;
var spriteWidth=spritesheetWidth/spriteCols;
var spriteHeight=spritesheetHeight/spriteRows;
var fireX=265;
var fireY=100;
//
var imgCount=2;
var fire=new Image();
fire.onload=start;
fire.src='https://dl.dropboxusercontent.com/u/139992952/stackoverflow/explodeSprite.png'
var map=new Image();
map.onload=start;
map.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/isometric1.png";
function start(){
  if(--imgCount>0){return;}
  cw=canvasB.width=canvasT.width=map.width;
  ch=canvasB.height=canvasT.height=map.height;
  canvasB.width=map.width;
  canvasB.height=map.height;
  ctxB.drawImage(map,0,0);

  animate();
}


function animate(time) {
  if(time<nextTime){requestAnimationFrame(animate);return;}

  nextTime=time+duration;

  var row=parseInt(spritePosition/spriteCols);
  var col=spritePosition-row*spriteCols;
  var spX=col*spriteWidth;
  var spY=row*spriteHeight;

  // Drawing code goes here
  ctx.clearRect(0,0,cw,ch);
  ctx.drawImage(fire,
                spX,spY,spriteWidth,spriteHeight,
                fireX,fireY,spriteWidth,spriteHeight);

  spritePosition++;
  if(spritePosition>spriteCount-1){
    spritePosition=0;
  }

  requestAnimationFrame(animate);
}
      

body{ background-color: ivory; }
#wrapper{
  position:relative;
  width:1095px;
  height:655px;

}
#canvasTop,#canvasBottom{
  position:absolute; top:0px; left:0px;
  border:1px solid green;
  width:100%;
  height:100%;
}
#canvasTop{
  border:1px solid red;
}
      

<div id="wrapper">
  <canvas id="canvasBottom"></canvas>
  <canvas id="canvasTop"></canvas>
</div>
      

Run codeHide result


+1


source







All Articles