我一直在为自己的游戏开发等距游戏引擎。目前,它是一个大型,开放的世界,地图数据是从Node.js服务器动态检索的。
要了解我正在做什么......在大多数情况下,这是一个以瓷砖为基础的世界。因此每个地图都有最大数量的cols,rows(19),每个世界都有col,row(6)的最大地图数量。所以这是一张6x6的世界地图,每张地图包含19x19个图块。每当玩家移动到新的地图/区域时,客户端请求周围地图的3x3矩阵,其中心地图是玩家当前所在的地图。这部分非常优化。
然而,我的问题是找到一种优化画布到画布上的好方法。目前,我没有太多的延迟,但我也有一台快速的电脑,但我担心有时它可能会导致其他人滞后/混乱其他图形的渲染。
基本上,我现在如何使用它是从服务器发回数据的时候,它将每个地图以及它必须呈现的每个列/行的所有平铺图像添加到缓冲区中。游戏循环的每个循环,它基本上将25块瓷砖的一小部分渲染到特定地图的隐藏画布上。当所有请求的地图完成渲染(在几个游戏循环之后)时,摄像机将继续并将这些隐藏的地图合并到3x3矩阵的1个大地图画布中(通过从隐藏的画布切片部分并将它们合并到新的画布)。
理想情况下,我希望整个过程都是异步的。但我一直在调查网络工作者,显然他们不支持画布。有没有人想出一个类似的东西并保持优化的过程?
谢谢!
答案 0 :(得分:0)
这是在每个帧中渲染19x19网格的示例。在每个帧中从右到左从上到下添加新的随机区块。网格以相同的顺序呈现,您可以看到这适用于重叠切片。
我认为保存每个磁贴并创建一个渲染整个网格的功能是最好的。因此,如果玩家在3x3周围区域获得更新,则下载并保留这些图块并重新渲染整个网格。
更新 我提供了一个消除透支和切换的功能。这可能会提高某些人的表现。它从下到上从左到右绘制。这将首先绘制重叠项目,并使用globalCompositeOperation" distination-over"告诉画布在添加新内容时单独留下现有像素。这意味着将像素放在画布上的工作量减少,因为它不会覆盖未使用的像素。
var cols = 19;
var tile_width = 32;
var rows = 19;
var tile_height = 16;
var y_offset = 64;
var h_tw = tile_width / 2;
var h_th = tile_height / 2;
var frames = 0;
var fps = "- fps";
setInterval(function(){
fps = frames + " fps";
frames = 0;
}, 1000);
var can = document.getElementById('tile');
var ctx = can.getContext('2d');
var wcan = document.getElementById('world');
var wctx = wcan.getContext('2d');
wcan.width = cols * tile_width;
wcan.height = rows * tile_height + y_offset;
var tiles = initTiles();
document.getElementById('toggle').addEventListener('click', function() {
if (this.innerHTML == 'renderWorld') {
renderFn = renderWorldNoOverdraw;
this.innerHTML = "renderWorldNoOverdraw";
} else {
renderFn = renderWorld;
this.innerHTML = "renderWorld";
}
});
//renderWorld();
var ani_x = cols;
var ani_y = 0;
var renderFn = renderWorld;
ani();
function initTiles () {
var tiles = [];
for (var y = 0; y < rows; y++) {
var row = [];
for (var x = 0; x < cols; x++) {
var can = document.createElement('canvas');
can.width=tile_width;
can.height=tile_height+y_offset;
row[x]=can;
}
tiles[y] = row;
}
return tiles;
}
function ani() {
var can = tiles[ani_y][--ani_x]
if (ani_x == 0) ani_x = cols, ani_y++;
ani_y %= rows;
var ctx = can.getContext('2d');
randTile(can, ctx);
renderFn();
requestAnimationFrame(ani);
}
// renders from bottom left to right and skips
// drawing over pixels already present.
function renderWorldNoOverdraw() {
frames++;
wctx.clearRect(0,0,wcan.width,wcan.height);
wctx.save();
wctx.globalCompositeOperation = "destination-over";
wctx.translate(0, y_offset);
var x_off = 0;
var y_off = 0;
var y_off2 = 0;
for (var y = rows; y--;) {
x_off = (cols * h_tw)- ((rows-y) * h_tw);
y_off = y * h_th + tile_height;
y_off2 = y_off;
for (var x = 0; x < cols; x++) {
var can = tiles[y][x];
wctx.drawImage(can, x_off, y_off2 + y_offset);
y_off2 -= h_th;
x_off += h_tw;
}
}
wctx.translate(0,-y_offset);
wctx.fillStyle = "#ddaadd";
wctx.fillRect(0,0,wcan.width, wcan.height);
wctx.restore();
wctx.fillStyle= "black";
wctx.fillText(fps, 10, 10);
}
function renderWorld() {
frames++;
wctx.fillStyle = "#CCEEFF";
wctx.fillRect(0, 0, wcan.width, wcan.height);
wctx.save();
wctx.translate(0, y_offset);
var x_off = 0;
var y_off = 0;
var y_off2 = 0;
for (var y = 0; y < rows; y++) {
x_off = (cols * h_tw) + (y * h_tw) - h_tw;
y_off = y * h_th;
y_off2 = y_off;
for (var x = cols; x--;) {
var can = tiles[y][x];
wctx.drawImage(can, x_off, y_off2 - 64);
y_off2 += h_th;
x_off -= h_tw;
}
y_off += h_th;
x_off -= h_tw;
}
wctx.restore();
wctx.fillStyle= "black";
wctx.fillText(fps, 10, 10);
}
function randTile(can, ctx) {
var maxH = can.height - 24;
var ranH = Math.floor(Math.random() * maxH);
var h = Math.max(ranH, 1);
ctx.clearRect(0, 0, can.width, can.height);
ctx.beginPath();
ctx.save();
ctx.translate(0, can.height - 16);
ctx.moveTo(0, 8);
ctx.lineTo(16, 0);
ctx.lineTo(32, 8);
ctx.lineTo(16, 16);
ctx.lineTo(0, 8);
ctx.strokeStyle = "#333333";
ctx.stroke();
// random floor color
var colors = ["#dd9933", "#22aa00", "#66cccc", "#996600"];
ctx.fillStyle = colors[Math.floor(Math.random() * 4)];
ctx.fill();
// random building
if (Math.floor(Math.random() * 8) == 0) {
ctx.beginPath();
ctx.moveTo(8, 8);
ctx.lineTo(8, -h - 4);
ctx.lineTo(16, -h);
ctx.lineTo(16, 12);
ctx.lineTo(8, 8);
ctx.stroke();
ctx.fillStyle = "#333333";
ctx.fill();
ctx.beginPath();
ctx.moveTo(16, 12);
ctx.lineTo(16, -h);
ctx.lineTo(24, -h - 4);
ctx.lineTo(24, 8);
ctx.lineTo(16, 12);
ctx.stroke();
ctx.fillStyle = "#999999";
ctx.fill()
ctx.beginPath();
ctx.moveTo(16, -h);
ctx.lineTo(24, -h - 4);
ctx.lineTo(16, -h - 8);
ctx.lineTo(8, -h - 4);
ctx.moveTo(16, -h);
ctx.stroke();
ctx.fillStyle = "#CCCCCC";
ctx.fill()
}
ctx.restore();
}
&#13;
body {
background-color: #444444;
}
&#13;
<button id="toggle">renderWorld</button><br/>
<canvas id='tile' width="32" height="32" style="display:none"></canvas>
<canvas id="world" width="608" height="368">
</canvas>
&#13;