4

问题是这样的:

在基于 js 和 asm.js 的多人游戏中,我有两个循环。

一个处理实际的游戏时间,例如单位位置、速度和战斗。

另一个将这个世界渲染到画布上供用户查看。

我想要发生的是当处理器/GPU(他们现在在某些机器上做了同样的事情,不能说我对此感到高兴)受到太多的阻碍时,渲染循环应该跳过并因此停止改变帆布。即在延迟派克中冻结游戏屏幕。

同时,剩余的少量处理能力用于成功完成实际的游戏滴答,防止与其他游戏客户端不同步。

(在加载时这是一个类似 RTS 的游戏,因此用户输入而不是所有对象的位置都通过网络发送)。

否则,客户端将不得不被其他客户端踢掉,否则所有客户端都必须暂停让他重新连接和重新同步。即坏坏坏!

一种草率的临时方法可能是使用时间戳并在图形循环在特定时间之前无法完成时终止图形循环。可以通过确定循环堆栈上数据包类型的最大执行时间来做到这一点,如果所有数据包的“执行时间值”在一起太大而无法在时间戳的资源容量内处理,则立即终止循环通过减速测量来指示。地狱,也许这是激进的,但甚至可能在检测到任何减速时跳过终止图形循环,以确保避免不同步。

因此,将一个循环优先于另一个循环(两个处理滴答声)并在检测到资源短缺时跳过第二个循环,以确保第一个循环始终在每个时间范围内完成它的滴答声(此处为每秒 10 个滴答声)。

你们可以告诉我任何可能性或最佳实践方法吗?

编辑:如果这些资源不足以完成两个循环(即如果循环不会在 100 毫秒后完成下一个循环滴答应该已经触发,不要开始/终止图形循环)。

4

2 回答 2

1

一种解决方案是使用网络工作者进行世界更新循环,然后使用普通的 javascript 循环进行渲染。您需要直接从 web worker 来回传递状态,但渲染循环只会利用更新的数据。

一个优点是您仍然可以在主 ui 循环中使用反动的显示代码。

这还有一个优点,即 web worker 可以使用不同的核心,并且对于多个 web worker,您可以使用多个额外的核心

于 2013-10-27T05:08:51.233 回答
0

对于逻辑循环,我会采用setInterval,而对于油漆 - requestAnimationFrame甚至更多 - requestAnimationFrame处的回调也接收时间戳,因此您可以跟踪时间戳并在出现某些缺失时跳过单帧。


处理器能够在渲染动画的同时处理其他任务

这个说法是错误的——处理器只能处理一个任务,requestAnimationFrame实际上不是渲染,它是你的回调——通用 javascript。您可以将其视为setTimeout. 唯一的区别是,它尝试在下一个空闲帧速率的帧上运行回调。这就是为什么它比setTimeout. 因此,对于动画,您必须使用requestAnimationFrame. 关于它的其他好处是,当网页在后台时(打开其他选项卡)。然后回调不会被调用,直到它到达前台。这节省了处理器时间,因为在该回调中没有计算任何内容。

回到你的问题:你现在但是,一次只能处理一个回调,所以如果处理器在特定时间忙于逻辑函数,那么动画循环的回调将不会被触发。在这种情况下,它称为“滞后”。但据我了解,这实际上是所需的行为 - 给逻辑回调函数更多时间。但还有另一面。如果您的动画功能很忙,当逻辑功能的时间被触发时怎么办?在这种情况下,它只会在动画功能结束时触发。没有什么可做的。如果您的动画功能很“重”,则只能尝试将其拆分为 2 帧。一帧 - 为渲染准备一切,第二帧 - 渲染。

但无论如何,你永远不会成为 javascript 中毫秒级的完美间隔或超时。因为它想被调用,直到事件循环不是免费的。要得到这个想法:

var seconds = 0;
setInterval(function(){ seconds++; var x = 10e8; while(--x); }, 1000);

取决于你的 CPU,但 10 秒后,变量“秒”将远小于 10。

还有一件事,如果你真的依赖时间,那么使用 Date.now() 来同步下一个逻辑刻度会更安全:

var setLogicalLoop = (function(){

    var _startedAt,
        _stop,
        _ms;


    function frame(){
        if (_stop === true) 
            return;

        // calculations

        var dt = Date.now() - _startedAt,
            diff = dt % _ms;

        setTimeout(frame, ms - diff);
    };

    return function (callback, ms){

        _startedAt = Date.now();
        _stop = false;

        setTimeout(frame, ms);


        return function(){
            _stop = true;
        };
    };
});


// -> start
var stopLoop = setLogicalLoop(myFunction, ms);
// -> stop
stopLoop();
于 2013-10-25T11:21:47.550 回答