3
votes

I'm creating one of my first HTML5 games. It's a game sort of like Ministry of War/War of Legends. It's an isometric tile-based MMORTS game. Now, while I'm building the game, I'm trying to keep resource management and speed in mind; however, I've come across an issue.

I have a method called renderMap that follows these steps

  1. renderMap loads the current map chunk. A map chunk is currently defined as 6x6 grid of tiles.
  2. renderMap loops through each individual tile from ASCENDING X,Y to DESCENDING X,Y and draws that tile onto the isometric grid. While drawing the tile to the screen, it will also check if building is on that tile, and will draw that building on top of the tile.

Now, because renderMap has to call drawImage about 36 times for each individual tile that is a part of the chunk, it really begins to become a resource hog if the function were to be called every frame. So, I made renderMap draw to the in-memory canvas, which is drawn to the main canvas every frame. So 36 calls of drawImage is reduced to only 1 every frame.

And the only time I need to call renderMap, is if the tiles or buildings update in anyway. For instance, if a new chunk is loaded and we need to load the new isometric map.

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

For example, I add a building called an oil well to the game. If an oil well is on any of the tiles in the 6x6 chunk that is loaded on the player's client and displayed, it will force the game to have to call renderMap so many more times, in order to render that single building's new image (because animation). So I end up rendering 35 extra tiles just in order to update that 1 building.

What can I do about this? Is just best to not include animations?

Here's what the game looks like, so this might be of help in regards to adding context to the problem:

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

image of game

1
You could use a png with all the animations.user2490157
-> What can I do about this? Is just best to not include animations? > Because you have used canvas, then you must redraw each frame. Becasue you ll get more and more computations to do everytime you add a nice feature, and as JS is very bad to computations, that will fail. My advice, don t use canvas to realize that. I believe plain HTML sould be more scalable over the time. Then yes, using PNG is good idea.mh-cbon

1 Answers

1
votes

Create a spritesheet that contains all the images necessary to animate your "oil well" tile.

Then you can pull the sprites off that spritesheet and overlay your rendered map on that single oil well tile.

Then you're rendering the large map once. You're rendering just that tile multiple times (a more efficient task than re-rendering the entire map just to animate the one tile). You can use a second overlaid canvas for best performance. You could even do an overlaid canvas that's just the size of a single sprite and use CSS positioning to move it over the target tile.

Example code and a 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>