我有一个名为Player
的对象:
var Player = function(_id) {
var time = new Date().getTime();
this.act = function() {
if (new Date().getTime()-time>=5000)
this.destroy();
}
}
当我创建这个玩家对象时,我想在5秒后destroy
这个玩家。
现在我可以看到两种方法来解决这个问题:
如果我决定使用act方法(使用setInterval
),它可能看起来像:
Players = {};
for(var i=0;i<5;i++)
Players[i] = new Player(i);
setInterval(
function() {
for(var i=0;i<5;i++)
Players[i].act();
},
1
);
如果我决定使用setTimeout
,例如:
var Player = function() {
setTimeout(
function() {
this.destroy();
},
5000
)
}
使用方法#2意味着我不需要跟踪时间,也不需要特殊ID。
然而,假设我有200名玩家,它会产生200个计时器,那不会效率低下吗?
但是,如果我使用方法#1,它将一直运行,计时器将保持每毫秒运行,我觉得效率也很低。
因此,就效率而言(包括精度也很好),哪种方法更好?
我的最终代码如下:
function Player(_id) {
this.enqueue(_id);
}
Player.prototype = {
queue: [],
constructor: Player,
enqueue: function (_id) {
this.id = _id;
var length = this.queue.push({
expires: new Date().getTime() + 5000,
instance: this
});
if (length === 1) this.dequeue();
},
dequeue: function () {
var player = this.queue[0];
var delay = player.expires - new Date().getTime();
setTimeout(this.act.bind(player.instance,player.id), delay);
},
act: function () {
console.log(this.id);
this.queue.shift();
if (this.queue.length)
this.dequeue();
}
};
如果我在chrome中进入开发人员的控制台并输入Player(4)
,然后等待2秒并输入Player(3)
,我会得到4,然后是两秒钟之后3。
按预期工作,通过查看代码,我只使用一个setTimeout。
答案 0 :(得分:3)
这两个世界的优点怎么样?您将按顺序创建Player
个对象。因此,它们将以与创建时相同的顺序超时。记住这一点,你一次只需要一个setTimeout
(对于Player
对象,接下来会超时)。这是如何实现它:
function Player() {
this.enqueue();
}
Player.prototype = {
queue: [],
constructor: Player,
enqueue: function () {
var length = this.queue.push({
expires: new Date().getTime() + 5000,
instance: this
});
if (length === 1) this.dequeue();
},
dequeue: function () {
var player = this.queue[0];
var delay = player.expires - new Date().getTime();
setTimeout(this.act.bind(player.instance), delay);
},
act: function () {
this.destroy();
this.queue.shift();
if (this.queue.length)
this.dequeue();
}
};
现在,每次创建Player
的实例时,它都会将其添加到队列(enqueue
)。如果队列中只有一个实例,则队列开始移动(dequeue
)。超时后,实例被销毁并且队列移位。如果队列中仍有实例,则队列继续移动。
这样,只有一个setTimeout
一次有效,下一个在第一个结束后开始。
答案 1 :(得分:2)
使用setTimer
(或setInterval
)不会为每个SE生成任何计时器,但会将事件放在已解析的事件队列中。
但是,是的,有很多这样的事件很明显,时间会变得越来越不准确,并使浏览器忙于尝试清空这些事件和其他事件(如绘制事件)的队列。
你可以做的是拥有一个包含自己状态的自我容器玩家对象。仅举例来说(考虑这个伪代码,因为我还没有测试过它):
function Player(name) {
var status = 0, //0=alive, 1=dying, 2=dead
timeOfDeath,
threshold = 5000; //ms, time of dying
/// get status of player
this.getStatus = function() {return status}
/// kill player
this.kill = function() {
if (status === 0) {
status = 1;
timeOfDeath = new Date().getTime();
}
}
function animateDeath(progress) { /* something here */ }
///MAIN, update player. This will be driven by a
///common loop
this.update = function() {
switch(status) {
case 0:
/// do whatever the player do when alive
break;
case 1:
///player is dying, animate death
///diff determines progress of death
var diff = new Date().getTime() - timeOfDeath;
animateDeath(diff);
if (diff > threshold) status = 2;
break;
}
}
}
现在我们有一个玩家对象,我们可以从一个公共循环更新:
var p1 = new Player('Bob');
var p2 = new Player('Lee');
...
function loop() {
if (p1.getStatus < 2) p1.update();
if (p2.getStatus < 2) p2.update();
...
requestAnimationFrame(loop);
}
如果你现在通过调用p1.kill()
来杀死一个玩家,这将由对象本身自动处理,并且当状态改变时,对象不会更新。
当然,这是一个非常基本的例子,只是展示了它的原理。
您需要考虑优化,例如将玩家对象放入数组,而不是迭代并从游戏中的其他组件中移除死亡玩家,如果可能的话使用原型(对象比数组慢,所以如果玩家信息可以放在一个简单的数组中,并使外部函数与对象中的函数相同,这将表现得更好......)等等。
不确定您是否可以同时开车,需要创建&#34;群组&#34;等等,但这种格式太宽泛了。
使用requestAnimationFrame(rAF)可以将更新同步到监视器的VBLANK,使动画更加流畅。由于它是一种更低级别的机制,它也能更好,更快地工作。