3

我有一个名为Player

var Player = function(_id) {
    var time = new Date().getTime();

    this.act = function() {
        if (new Date().getTime()-time>=5000)
            this.destroy();
    }
}

当我创建这个播放器对象时,我想destroy在 5 秒后创建这个播放器。

现在我可以看到两种方法来解决这个问题:

  1. 如果我决定使用 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
    );
    
  2. 如果我决定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。

4

2 回答 2

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处于活动状态,下一个在第一个结束后开始。

于 2013-07-12T03:16:46.537 回答
2

使用setTimer(or 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()这将由对象本身自动处理,并且随着状态的变化,对象将不会被更新。

当然,这是一个非常基本的例子,只是展示了它的原理。

您需要考虑优化,例如将玩家对象放入数组中,而不是迭代并从游戏中的其他组件中删除死玩家,如果可能的话使用原型(对象比数组慢,所以如果可以放置玩家信息而是在一个简单的数组中,并让外部函数与对象中的函数执行相同的操作,这将执行得更好..) 等等。

不确定您是否可以同时驱动所有内容并需要创建“组”等,但这对于这种格式来说太宽泛了。

使用requestAnimationFrame (rAF) 可以让您同步更新到监视器的 VBLANK,从而使动画更加流畅。由于它是一种更底层的机制,因此它也可以更好更快地工作。

于 2013-07-12T03:01:11.310 回答