1

我需要对片段进行排队并在给定的时间(每秒一次)执行它们。这些片段实际上是玩家的动作,例如 jump()、attack()、walk('left') 等。

当用户按下一个键时,我需要将他的操作排队并每秒执行一次所有操作。

现在我的方法,因为没有更好的主意,就是将每个片段添加到一个数组中,然后用 eval() 循环遍历它。这是我的代码:

var queue = [];

// On player or AI action
queue.push('attack()'); // Could be walk('left'), jump() etc.

// On new frame
for(var i=0;i<queue.length;i++){
  eval(queue[i]);
  }
queue = [];

我确定我的方法很糟糕,但我想澄清我想要做什么,我想这样的问题并不少见。任何信息/想法?

4

2 回答 2

3

可怕的 -eval是邪恶的。相反,使用闭包

var queue = [];

queue.push(function() { attack(); });

while (queue.length) {
    queue.shift().call();
}

这是一般情况,但在这种情况下可以简化:function() { attack(); }是一种更复杂且更慢的编写方式attack,因此queue.push(attack)也可以正常工作(如果您在function attack() { ... }可访问的地方定义了 aa)。显然,如果您的函数带有参数(或者更确切地说,如果每个函数带有不同的参数;您可以在call(thisobj, param)调用中提供统一的参数列表),您就不能这样做。

编辑您的附加查询:

在您编写的代码中,这应该已将变量捕获到闭包中;如果您更改变量,则该值将是您在执行它时获得的值。如果你得到了undefined,我猜你closerEnemy = undefined稍后会在某个地方执行。这是一个常见的错误来源,例如,人们试图将点击处理程序绑定到循环中的多个元素,并在处理程序中使用循环计数器 - 处理程序捕获计数器而不是它的值,并且总是稍后评估到元素的数量(循环结束时剩下的最后一个值)。

要捕获值而不是变量,请使用以下技巧:

(function(capturedCloserEnemy) {
  queue.push(function() {
    attack(capturedCloserEnemy);
  });
})(closerEnemy);

(变量命名不同只是为了便于阅读;它们都可以命名相同,这并不重要,因为阴影。)

于 2012-12-20T05:54:43.837 回答
1

您的方法的问题在于:

  1. 您正在使用评估

    您已经听说过使用 eval 的风险。如果没有,请从 MDN 检查此文档

  2. 您正在使用循环

    使用循环时,JavaScript 会尽可能快地完成循环。这将导致 UI 中的阻塞。这也意味着当您以这种方式开始循环时,您的动画将显示为仅在第一个动画处移动、冻结并在队列中的最后一个动画处结束。

作为对这两个问题的补救措施,为什么不将对您打算执行的函数的引用推送到队列中。此外,为了防止 UI 阻塞,请改用计时器:

var queue = [];

//actions as functions
function attack(params){...}
function block(params){...}
function walk(params){...}

//our animation timer
var animationTimer = setInterval(function(){
    //remove the action from the queue and execute
    var nextAction = queue.shift();
    //if we shifted something, execute
    if(nextAction){
        nextAction.call();
    }
},1000);

//to insert into the queue, push the reference of the function instead.
queue.push(attack);
于 2012-12-20T06:00:12.580 回答