我需要对片段进行排队并在给定时间(每秒一次)执行所有片段。片段实际上是玩家动作,如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 = [];
我确信我的方法很糟糕,但我想澄清一下我想做什么,我想这样的问题并不是那么罕见。任何信息/想法?
答案 0 :(得分:3)
可怕 - eval
是邪恶的。相反,请使用closures:
var queue = [];
queue.push(function() { attack(); });
while (queue.length) {
queue.shift().call();
}
这是一般情况,但在这种情况下可以简化:function() { attack(); }
是一种更复杂,更慢的方式来编写attack
,因此queue.push(attack)
也可以正常工作(如果您在某处可以访问function attack() { ... }
。显然,如果您的函数采用参数(或者更确切地说,如果每个函数采用不同的参数;您可以在call(thisobj, param)
调用中提供统一参数列表),则不能这样做。
编辑以获取其他查询:
在您编写的代码中,这应该已将变量捕获到闭包中;如果更改变量,那么该值将是您执行时获得的值。如果你有undefined
,我猜你以后会在某个地方执行closerEnemy = undefined
。例如,当人们尝试将单击处理程序绑定到循环中的多个元素,并在处理程序中使用循环计数器时,这是常见的错误来源 - 处理程序捕获计数器而不是其值,并且稍后将总是评估到元素的数量(循环结束时剩下的最后一个值)。
要捕获值而不是变量,请使用此技巧:
(function(capturedCloserEnemy) {
queue.push(function() {
attack(capturedCloserEnemy);
});
})(closerEnemy);
(变量的名称不同只是为了便于阅读;它们都可以被命名为相同而且无关紧要,因为阴影。)
答案 1 :(得分:1)
您的方法存在的问题是:
您正在使用eval
您已经听说过使用eval的风险。如果没有,请检查this document from MDN。
您正在使用循环
使用循环时,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);