我正在构建一个浏览器游戏,其中包含玩家周围环境的迷你地图。我需要跟踪其他玩家的位置,并在有人移动时更新此迷你地图。我在NodeJS和CouchDB中实现它。我的设计如下:
我有一个包含所有不断变化的游戏数据的数据库。在这个数据库中,我有一个文档,其中包含二维数组中的基本地图数据,网格上的每个方块都由此数组中的元素表示。由于我可以让大量不同的用户同时在地图上移动,我需要一些方法来确保我没有读取其他人正在写的内容(如果有大量用户正在阅读,我可能会收到错误的信息。写入单个文件)。我决定在这个数据库中有单独的文档,它们代表各个方块,每个文档都有那个正方形“播放”的播放器和与该方块相关的其他一些数据。实质上,地图文档仅用作方形文档的查找表。这使我能够在不必重写整个地图文档的情况下更改单个文档,从而解决了同时读写的问题。
我的问题是,我需要获取一个迷你地图供用户用作参考。这个迷你地图将包含周围广场的文件。由于我同时需要所有这些,我想我会从数据库中获取所有9个方块并将其返回到单个ajax响应中。我的问题是减少了阻塞IO的数量。现在,我有一个嵌套循环,从数据库中请求我需要的方块。以下是我的代码(传递位置和地图):
var miniMap = new Array();
var keyMap = new Object();
var numDone = 0;
for(i = position.y - 1, y = 0; i < position.y + 1 && i < map.length; i++, y++){
miniMap[i] = new Array();
for(v = position.x - 1, x = 0; v < position.x + 1 && v < map.length; v++, x++){
keyMap[map[i][v].id] = {'x': x, 'y': y};
gameDB.getDoc(map[i][v].id, function(er, doc){
var tPos = keyMap[doc._id];
miniMap[tPos.y][tPos.x] = doc;
numDone++;
});
}
}
我的问题是,getDoc是非阻塞的,所以我不知道它何时将方形数据设置为miniMap。我想做这样的事情:
while(numDone < 9){
sleep(10);
}
callback(miniMap);
这将让我等到CouchDB完成获取所有数据,但JavaScript没有睡眠功能。我找到的最接近的是setTimeout,但这也是非阻塞的,我永远不会100%确定我为超时选择的时间足以让CouchDB完成我的数据。
所以基本上我想要一个条件,测试它,然后如果条件为假则再次将它返回到事件堆栈。我想到的唯一解决方案是使用递归的setTimeout回调来执行类似这样的操作:
function testCallback(data, condition, callback){
if(data < condition){
setTimeout(function(){
testCallback(data, condition, callback);
}, 10);
}else{
callback(data);
}
}
这看起来非常可怕......有没有更好的方法可以做到这一点?我应该放弃这种方法并强制进行多次ajax调用以获取更新数据吗?我应该采取阻止措施吗?
答案 0 :(得分:5)
只需使用其他回调:
function getMiniMap(....., callback) { // supply the callback that sends the data
var miniMap = new Array();
var keyMap = new Object();
var numDone = 0;
...
numDone++;
if (numDone === 9) { // as soon as everything has been collected...
callback(miniMap); // ... call the send callback and supply it the miniMap
}
});
}
}
}
哦,你的数据库模型真的不好,我对你的游戏知之甚少,但除非这需要在多个Node进程上运行,否则最好保留Map等。在JS数组中,只有在服务器需要保存状态时才写入数据库。
哦,你也可以用匿名函数调用替换你的keyMap
:
(function(x, y) {
gameDB.getDoc(map[i][v].id, function(er, doc){
var tPos = keyMap[doc._id];
miniMap[tPos.y][tPos.x] = doc;
numDone++;
});
})(x, y); // pass in a copy of x and y
答案 1 :(得分:1)
鉴于我们正在谈论事件编辑系统,我认为如果你可以在numDone == 9时发出一个加载完成事件并从你的主文件中捕获它并从那里继续它将会更清晰。
即使游戏需要跨多个节点运行,您也可以将整个地图放在redis中。