HTML5画布游戏,多图层问题

3
我正在创建我的第一个 HTML5 游戏。这是一款类似于《战争部长/传奇之战》的游戏。它是一款基于等角瓦片的 MMORTS 游戏。现在,当我构建游戏时,我试图考虑资源管理和速度;然而,我遇到了一个问题。
我有一个名为 renderMap 的方法,它遵循以下步骤:
  1. renderMap 加载当前地图块。地图块目前被定义为 6x6 的网格。
  2. renderMap 循环遍历每个单独的瓦片,从升序 X、Y 到降序 X、Y,并将该瓦片绘制到等角网格上。在将瓦片绘制到屏幕上的同时,它还会检查是否在该瓦片上有建筑,并在瓦片上方绘制该建筑。
现在,因为对于地图块中的每个单独瓦片,renderMap 必须调用 drawImage 约 36 次,如果该函数每帧都被调用,它真的会变得非常耗费资源。所以,我让 renderMap 绘制到内存画布上,然后每帧绘制到主画布上。这样,36 次调用 drawImage 就减少到了每帧只有 1 次。
而且,我只需要在瓦片或建筑物以任何方式更新时才需要调用 renderMap。例如,如果加载了一个新的地图块并且我们需要加载新的等角地图。
但是,如果我想让这些建筑物或瓦片拥有动画呢?
例如,我向游戏中添加了一个名为 oil well 的建筑物。如果油井位于玩家客户端上加载和显示的 6x6 块中的任何一个瓦片上,它将迫使游戏不得不调用 renderMap 更多次,以渲染该单个建筑物的新图像(因为动画)。所以,我最终会渲染额外的 35 个瓦片,只为了更新那 1 个建筑物。
我该怎么办?最好是不要包含动画吗?
这就是游戏的样子,这可能有助于解决问题的背景:
(抱歉图片很杂乱...我正在进行一些测试)

你可以使用包含所有动画的PNG文件。 - user2490157
我能对此怎么做呢?最好是不包含动画吗?因为你使用了画布(canvas),所以你必须重新绘制每一帧。每次添加一个漂亮的特效时,你将会有越来越多的计算任务,而且由于JavaScript在计算方面表现得很差,这个方法会失败。我的建议是,不要使用画布来实现这个。我相信纯HTML会在长期内更具可扩展性。然后,使用PNG是一个好主意。 - user4466350
1个回答

1
创建一个包含所有必要图像以动画显示“石油井”图块的精灵表。
然后,您可以从该精灵表中提取精灵,并将呈现的地图覆盖在单个油井图块上。
然后,您只需一次呈现大型地图。您将多次呈现仅该图块(比重新呈现整个地图以动画显示一个图块更有效)。您可以使用第二个重叠的画布来获得最佳性能。您甚至可以使用 CSS 定位将其移动到目标图块上的重叠画布的大小。
示例代码和演示:

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>


是的,这就是我想做的。重新渲染一个特定的瓦片。但在等距地图中这是不可能的。比如说,如果我为城市添加了一个动画精灵表。如果你看一下我提供的示例图像,在地图顶部最靠近油井旁边的城市。现在,想象一下,如果我只更新那个瓦片来更新城市精灵表图像。它会出现错误,并将城市绘制在油井上方,使城市重叠在油井上方——这看起来会非常奇怪。(希望我解释得足够清楚) - sieg
@sieg:我在我的回答中添加了一个演示...你的油田位于[20,24]似乎着火了! - markE
1
谢谢!爆炸效果很棒! - sieg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接