光有图片不知道游戏应该怎么办

我们只实现了小鸟飞行以及障碍粅地图随机生成、绘制和滚动这期我们要完成整个游戏的大体框架,即游戏运行的主体部分而其他的比如计分,重新开始等不会去实現文章最后会讲一些个人心得。

本期内容依旧是在微信小游戏上进行实现的由于内容以及代码都承接以前文章,如果你没有阅读过鈳以从开始。

本文不允许任何形式的转载!

本系列文章不适合以下人群阅读如果你无意点开此文,请对号入座以免浪费你宝贵的时间。

  • 想要学习利用游戏引擎开发游戏的朋友本文不会涉及任何第三方游戏引擎。
  • 不具备面向对象编程经验的朋友本文的语言主要是Javascript(ECMA 2016),如果你不具备JS编程经验倒也无妨但没有面向对象程序语言经验就不好办了。

关注我的微信公众号回复“源代码4”可获得本文示例代码下載地址,谢谢各位了!

为什要加上一个“最基础的”呢因为二维图形碰撞(Collision)一般分为Bounds接触判定(可能会碰撞)和最终碰撞判定(多边形到底是否有接触),比如下图:
首先说下Bounds是什我们可以认为Bounds就是一个区域,通常我们都以一个矩形作为Bounds但有时候可能会是其他形状。就上图而言我们的Bounds是一个矩形,并且是和XY轴平行的这种Bounds我们成为Axis-aligned bounding boxes,简称AABB(下文提到的Bounds都是这个AABB)一般只需要知道这个矩形的左上角和右下角两个点的位置,就能通过计算得知两个Bounds是否接触

通常来说我们称左上角为min,右下角为max意为该Bounds最小的点和最大的点。而我们接下来用的是[left,top],[right,bottom]分别来表示最小点和最大点的坐标

怎判断两个Bounds没有接触呢:
设两个不同的Bounds A和B,如果其中一个的left比另外一个的right大或者它的top仳另一个的bottom大,则我们认为 A没有和B接触反之,判断两个Bounds是否接触的代码如下:


我就问一下你觉得哪段代码好?我选第二个

回到我们嘚游戏中,为了简化我们认为只要树干和小鸟的Bounds接触上,就算碰撞
那小鸟和树干的Bounds怎得到呢。

综上我们知道了什是Bounds,而且注意到偠判断两个Bounds是否接触,必要条件是这两个Bounds必须在同一个坐标系下

如果你对Figure类第0次迭代这篇文章还有印象的话,就应该知道Figure的left和top是相对其父节点的坐标系的所以Figure的bounds不应该这简单地计算!比如一个Figure A的子Figure C和另一个Figure B中的子Figure D进行bounds碰撞测试,如果按照上面的代码得到的bounds来判断的话肯萣是错了坐标系都不同比较个卵。

所以我们说bounds首先是有一个相对性的就我们的游戏而言,要判断小鸟和树桩是否碰撞就应该获得它們相对于顶层Graph的bounds值,然后再进行判断幸运的是小鸟和树桩(应该说是障碍地图)正好是Graph的子节点,所以上述代码正好得到它们相对于Graph的bounds直接判断就好了。

除了相对坐标系的问题Figure的bounds还应该要考虑到自身的旋转和拉伸,这都会影响到Bounds值更幸运的是,我们目前这个游戏中尛鸟和树桩都没有旋转和拉伸如何计算旋转和拉伸后的Bounds我会在以后讲到变换矩阵的时候再说,先提示一下各位免得产生误会

小鸟的Bounds还恏说,就刚才代码即可获得但是TileMap的怎办?它的Bounds是整个地图的区域跟树干没关系呀,这里我们就只能是特例特办了通过TileMap的地图数据来計算地图中所有树干的Bounds。

它是通过地图数据在drawSelf中计算出树桩的位置和类型然后绘制上去。正好我们的树桩bounds计算跟这段代码几乎一样,那我们就可以利用这段代码来计算出树桩bounds了
为了不重复计算,我们可以在生成地图数据的时候就计算出树桩的bounds数据然后在绘制的时候矗接使用即可,无需二次计算所以我更改了TileMap的代码,你可以拿去和以前的比较一下:

请注意!!树桩的Bounds目前并不是相对Graph的,而是相对於其所在地图的这是因为计算bounds的方法只在生成地图的时候被调用,而这时候的地图坐标和需要计算碰撞时候的坐标是不同的所以只能先算出树桩相对地图的bounds,然后再在需要进行碰撞测试的时候加上地图的left和top来获取其相对Graph的bounds

便于测试,我把小鸟和树桩的bounds都绘制成了红色邊框得到如下结果:

注意看,小鸟的bounds实际上有点过大了因为整个小鸟并没有占满整个Figure的区域,所以在测试碰撞的时候我们要可以缩小┅下他的Bounds区域不然小鸟背部根本没接触到树干还判定为碰撞就不好了。比如这大我觉得就挺合适了。

有一种碰撞判定叫做 像素碰撞即查看两个图片数据中是否有不全透明的像素点重合,我觉得好麻烦啊这种判断难道不会很慢吗

最后我们就需要在BirdFlyGame里加入碰撞判定代码,一旦碰撞上游戏就结束:

下面是测试结果一旦小鸟的红色框碰到树干的,游戏就停止了

我们就可以在processGameOver里做一些操作,比如显示飞行距离、时间、重来一次按钮等

如果Figure没有在Graph的显示范围内(即屏幕里面),其实是没必要绘制的
而我们的地图却不是。第一张地图和第②张地图同时往左移动在第二张地图实际上还没有进入到Graph的区域的时候,我们却依然在绘制它并且TileMap类还是一个要绘制大量图片的类,這就让效率降低了不少
如果我们提前判断出Figure不在父节点的区域内的话,就不要绘制它所以我们可以在Figure类的drawChildren方法中进行一次Bounds碰撞判断,發现子Figure没有和父Figure所在显示区域接触就不绘制:

这个解决了整个Figure在父节点区域外会进行绘制的问题。不过TileMap类却要绘制大量树桩而且有很哆树桩都是在显示区域外的,就如上图所示蓝色背景的地图中,有一部分还是在屏幕外的
这个也好解决,我们在TileMap的drawSelf中通过树桩的Bounds来哏Graph的显示区域Bounds做碰撞测试,没有接触的就不绘制:

不要小看只是少画了一些图片性能会提升很多的。再次提醒各位用CanvasRenderingContext2D绘图是很慢的,能改进的地方就改进提升一点是一点,否则一旦把程序搬到移动设备上就会卡得出奇

下图是性能测试的结果,CPU 2 x slowdown大约录值了6秒,不太准

2.取消不必要的碰撞测试

在测试小鸟和树干碰撞的时候,我们的代码如下:

是将两张地图的所有树桩Bounds和小鸟的Bounds进行测试就如上面优化繪制遇到的问题一样,很多树桩位置根本就没进入屏幕这种碰撞测试就是无意义的。
更进一步来说如果地图没有进入到小鸟可能在的位置区域,就没必要进行测试如下图所示:

如果还要细分的话就需要改造之前生成的树桩Bounds数据结构,目前我把所有树桩的Bounds放到了一个数組中可以把它改成一个二维数据结构:

之前存放的数据是这样的:
一维数组,每个数据表示树桩的Bounds(data是之前为了判断树桩类型保留下来的┅个属性)

目前测试方法是遍历一维数组改造后虽然也要遍历一遍,但是由于分成了“列”如果某列第一个不在测试范围,则整列就跳过这里我只提一下,就不改造原来的数据结构了
此外,如果某个树桩没有进入小鸟碰撞区域并且在该区域右侧,那这个树桩往后嘚所有树桩都没有进入该区域不需要测试碰撞:

上面我提到,如果树桩bounds在小鸟右侧并没接触上则跳过剩余的树桩那如果树桩bounds如果在小鳥左侧也没接触上,那该树桩之前的树桩也是没必要测试碰撞的但是我们是遍历一个数组,逐个进行比较且数组里的Bounds数据的left是从左至祐的(从小到大),所以刚才说的那个就不好实现

我们的游戏主体框架就已经全部完成了,即小鸟可以控制飞行地图可能随机生成并能滚轴移动,可以进行碰撞测试等并且我们还进行了一些优化,游戏其余的部分:加入音效和背景音乐游戏结束后计分,游戏开始的堺面游戏结束的界面,这些的工作量其实不比之前做游戏主框架少甚至更多,所以我就不一一讲述了毕竟我很懒。
至此《老脸教你莋游戏》就告一段落了(实际上我想写的是《老脸教你做游戏引擎》后来我想了想,还是要点脸吧)
如果有下期,那应该会是讲质点運动模拟有缘的话我们会再见的。

如果你已经强打精神看到此处说明我们有缘。我怕以后不更新blog没机会写所以在最后我讲讲一些我覺得挺有用的东西。

用过数据库吗没用过没关系。数据库可以建立索引便于快速定位到某个数据。
我们在未优化测试碰撞的方法中采用了全局遍历,即用图形A和所有其他图形进行碰撞测试这是很慢的。如果我们能快速定位到图形A和它附近的图形的话就没必要进行铨局测试了。
所以几乎所有游戏引擎都会对图形进行区域划分就拿目前的游戏为例:

我们可以把整个屏幕划分成n个区域,每当有新的图形加入就计算出该图形的Bounds在哪个区域并把该图形放到区域里图形数组里,如果图形发生变换(移动、缩放、旋转)重新计算图形所在區域并更新,这就相当于数据库里建立索引写段伪代码:


 
 
 

伪代码,不要较真是这个意思就可以了

这样一来,如果我想知道小鸟所在区域有哪些些的图形就可以这样:

比如想要进行碰撞测试,就不再需要全局比较了从小鸟所在区域内取出其他图形和小鸟Bounds进行测试即可。
上面例子里的区域是一个固定网格形状的这种区域划分叫做全局网格(Uniform Grid),是二维中较为简单的区域划分方式二维游戏引擎常用区域划分还有四叉树(Quadtrees)等,三维的就有BSD等不管怎去划分吧,目的就一个快速定位图形,减少不必要的操作我们所讲的这个游戏碰撞測试还是很简单的,如果是复杂的碰撞测试那就会花费很多时间。一帧16毫秒是特别宝贵的不要浪费在一些无谓的操作上。
当然选择哪种划分方式还要看区域更新复不复杂,太复杂了也不行本末倒置了。
下图是我做的刚体运动模拟的网格区域划分测试:

比如你绘制一個图片在canvas上你可能会发现在手机上比PC上模糊(像素化),这是因为devicePixelRatio的值不为1
这个属性值的意思是说,当前显示设备的物理像素分辨率囷CSS像素分辨率的比值可以这理解:一个CSS像素等于多少个物理像素大小,即需要多少个当前设备屏幕像素绘制一个CSS像素
假设devicePixedlRatio为3.5的话,你繪制出来的图片在设备上显示跟实际屏幕像素就差了3.5倍所以看着就模糊了。

而通常我们写代码的时候图形的大小都是根据canvas大小来设定的而且是在canvas内存大小改变之前,所以如果你在代码中一直沿用了为改变内存大小的canvas长宽那就还需要将context放大相应的倍数:


这样就不会出现模糊的情况了。

我们绘制的图片经常都是一张一张的实际上这样不好,尽量把他们都合到一张图片上指定好source bounds然后绘制。
这样做一方面昰为了节约空间另一方面是为了方便加载,另外我在做webgl的时候为了统一绘制,会将多个图片合并到一个texture中但是不管怎做都无法让这些图片铺满一个大的空间的,往往会生成多个texture(这也跟我的算法有关)如果一开始要绘制的图片就是一个大的图片,就不需要来回切换叻一次性就能绘制成功。

如果游戏程序中会频繁生成新的图形对象请建一个对象池,便于复用
好多人说对象池的目的是为了减少创建对象时的开销,个人认为这是一方面另外一方面是为了给GC省时间,太多的对象需要清扫的话GC花的时间很长以至于出现游戏玩着玩着突然卡一下。

这个方法我再web上倒没有发现问题可是在微信小游戏里出现了性能爆降的情况。我觉得是微信小游戏的bug于是在他们论坛提茭了,不过过了很久都无人回复我觉得很有可能是我自己代码烂造成的,于是就默默把提交的bug删了
这是代码片段导入链接,试试在你嘚手机上卡不卡:

context 2d在移动端只适合简单的图形和游戏

我之前做了个打泡泡的微信小游戏不包括背景图片,里面至少要绘制50个泡泡而且還会动,起初在模拟器上运行得还好而且在我的手机上运行也没问题;后来我把这个游戏移植到了web端,一到手机端FPS只有20我忽略了一点,微信小游戏不是运行在浏览器上的跟普通的web页面不是一回事。于是我不得不把整个游戏的绘制部分改成了webgl花了一个月的时候才调试恏。

在此提醒一下各位如果你的程序主要是在移动端运行,开发的时候在模拟器(我是在Chrome上运行)上先将CPU降低到手机的标准不然会误導各位,还以为程序运行得挺流畅实际上卡成鬼。

所以说如果只会画个报表啦,做个Diagram程序啦canvas2d就够用了,但要想在手机端做出渲染操莋比较多而且复杂的游戏(比如3d效果的)用canvas2d是不行的,请转投webgl

游戏就是一个程序而已,有的凸出的是画面有的是在音乐上下了大工夫,而有的却是在游戏剧情安排上很吸引人各有各的受众。
有些人就喜欢绝地求生我就不喜欢,而我很爱玩连连看却被别人笑话幼稚。玩家即是如此何况开发者:你用的Unreal引擎开发的3d游戏,我用的原生js开发的2d游戏你的就要高一档次?
游戏不分贵贱的只有喜欢和不囍欢


图标上点右键 选打开 看看有无反應如果没反应,可能是该软件有问题

如果能打开,看看你的鼠标设定是否双击设定的过快或过慢,如设置正常应该是鼠标左键有問题了,换个鼠标试试

重下游戏戏 要硬件有问题,找人来帮你看看不贵
热血无赖去快快下亲

你对这个回答的评价是


在安装目录下直接點击开始游戏 有有反应??

你对这个回答的评价是?

下载百度知道APP抢鲜体验

使用百度知道APP,立即抢鲜体验你的手机镜头里或许有别人想知噵的答案。

我要回帖

更多关于 打游戏 的文章

 

随机推荐