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

7. 优雅的使用外部代码

在前面的章节中,我们谈到了使用项目中他人写的函数,我们采用的是一边改一边重构的方式,使之更符合整个项目。那我们使用引擎层的代码有什么注意的吗?

使用引擎层的代码,我们要尽量使用局部缓存缓存住接口或者值。举个例子:

let levelSlider = Core.createBitmapByName("slider_png");

levelSlider.x = 0;

if(XXX){

levelSlider.x += 6;

}

if(XXX){

levelSlider.x += 8;

}

if(XXX){

levelSlider.x -= 2;

}

上面的代码里面对levelSlider.x 这种操作,我们没办法直接知道这个赋值行为也就是levelSlider.x= 底层是不是做了很多操作。所以我们需要先把值缓存住,等计算完成后再一次赋值过去。

修改后的代码为:

let levelSlider = Core.createBitmapByName("slider_png");

let nSliderX = 0;

if(XXX){

nSliderX += 6;

}

if(XXX){

nSliderX += 8;

}

if(XXX){

nSliderX -= 2;

}

levelSlider.x -= nSliderX;

对于引擎代码,我们应该尽量保持警惕,尤其是在每帧都执行的函数中,很容易产生大量的gc或者是消耗大量的CPU。我们的一个原则就是要尽可能少去直接和引擎进行交互,而是更多的进行局部缓存,把战场拉回到更加通用的逻辑里面。对值的设置,或者是获取都是可以进行这样的优化。

我们再来看一个例子:

我们想对一个窗口内的一个ui调整层级。我们有2个方式可以完成:

1. this.addChildAt(oImgBottom, nIndex);

2. this. setChildIndex (oImgBottom, nIndex);

1,2 两个方式都可以实现调整ui层级的作用,但是我们推荐是使用第二种,因为第二种的写法字面表明的含义在这个地方更加符合我们想要做的事情。第一种写法是重新添加对象,第二种写法是设置对象的索引层级。很明显第二个写法更加清晰。很多情况下,我们在使用第三方引擎的时候,比如unity、egret、laya等引擎都会遇到一些超乎预料的未知bug,这个过程常常被程序员称为踩坑过程。有些同事会经常遇到,有些同事就完全不会遇到。我们尽可能的使用容易理解,清晰的接口,这样我们就可以尽量避开引擎本身的各种坑。

再看一个例子:在某个引擎的粒子系统内, 有2个成员变量:

emitterX 对应的注释:表示粒子出现点X坐标

emitterXVariance 对应的注释:表示粒子初始坐标 x 差值

两个变量貌似都可以影响粒子出现点的坐标,那我们在第一次写代码的时候应该选用字面意思更为容易理解的变量,也就是emitterX。不需要也没必要去搞清楚这2个变量的区别,在需求不满足或者与预期不符的时候再去弄懂即可。这可以节约我们在引擎使用上面的不必要时间成本。

前面我们讲了我们在使用底层代码的时候应该尽量使用简单的部分,也尽可能少的调用它们来实现自己的目的。当底层代码提供了不同的接口或者参数来做类似的事情的时候,我们也应该尽量选取里面简单的部分。在白鹭引擎里面,有个接口是这样的:

hitTestPoint(x: number, y: number, shapeFlag?: boolean): boolean;

这个接口它提供了一个shapeFlag参数用于指定是使用像素检测或者是边框检测。在我们的项目中,也许传递不同的参数不会有什么影响,但是我们应该优先选择边框检测。选择边框检测的原因是因为边框检测的复杂度比像素检测低,像素检测依赖于更加底层的接口,而边框检测是个通用的逻辑技术,也底层无关。曾经有个白鹭项目里面使用像素检测,后面跑在微信小游戏上的该项目突然就无法正常工作了。微信的bug影响了我们上层的逻辑bug,这也告诫了我们应该优先使用简单的接口。

选择简单的接口/参数以及尽量少的使用/调用底层接口就是我们所谓的正确的代码使用方式。