游戏逻辑思想
上QQ阅读APP看书,第一时间看更新

13. 阻塞与非阻塞

首先,我们这边所说的阻塞非阻塞指的是加载文件。在很多的api中,我们可以看到一个接口经常有2种形态:

readFile

readFileSync

或者unity里面加载资源的接口:

Resource.Load

Resource.LoadSync

我们用js写文件的时候,很容易建立一个认知。当我们想对文件进行有序的操作的时候,我们用阻塞的方式。如果我们不需要任何的时序或者只使用到很少的时序,那么我们就可以用非阻塞的形式。

在游戏里面,经常会被建议不要用阻塞的方式。因为阻塞行为一旦发生,它是可能卡住整个界面的,使得游戏无法做出任何的响应。那我们是不是就不用阻塞了?我们先来看一看阻塞的优点。阻塞相对非阻塞而言,它的速度是比较快的。它可以在一些场景中进行应用:

在游戏的启动加载过程中,为了缩短玩家的等待时间。我们大量使用阻塞,只在单个文件加载后更新一下界面,接着就继续阻塞加载了。

在大部分游戏引擎里面,加载界面资源我们常用的是非阻塞。但是当我们加载界面资源后,如果实例化这个对象一定是耗时的或者说卡的。那么这个时候我们一般会先把界面的外框加载出来,再加载里面的内容,这个时候我们就可以换用阻塞了。当存在某些逻辑是不得不卡住的或者说耗时严重的,而且也无法通过线程或者其他方式优化的时候,我们先阻塞加载静态的背景,再进行一系列耗时的操作,包含阻塞加载资源,对象的实例化等。

这2个案例告诉了我们它的应用场景,在效率至上的地方,以及在耗时已经超出的地方。因为如果一定要卡(耗时),那么就在一瞬间铺个静态背景卡个够。这边还可以举个例子就是场景切换后,需要加载大量的玩家对象。这些对象在加载的时候因为数量,也因为其他不确定的因素,可能是会导致CPU的高峰。那么我们就在切换场景的加载页面出现之前,先同步把它们都加载完。这样,进入场景后就不会再遇到它们导致的卡顿。

对于非阻塞,通常伴随着事件完成的回调处理。比如说加载一个文件,我们会在加载完成后回调到我们注册的函数。对于代码而言,阻塞的代码写起来是比较舒服的,不用各种回调嵌套,因为回调一旦多了,会引发“回调地域”的灾难,这在js里面有专门的介绍。我们可以通过协程来解决,模拟一个同步的写法。