颤抖吧!零零后已经开始爆肝写游戏了!

COCOS

共 5592字,需浏览 12分钟

 ·

2021-02-20 12:10

春节这段时间,社区有 3 位来自华南师范大学的大一新生寒假爆肝 14 天,一起完成了一个太空科幻题材小游戏的创作,已经上线发布在微信小游戏,用户满意度更是全 5 分,目前游戏完全免费也没有任何广告,感兴趣的童鞋可以微信搜索【星旅StarTrek】体验一下噢。
游戏讲了一个关于述人类命运存亡的科幻故事。在游戏中你可以探索无限大的宇宙空间,在符合物理的太空中自由穿梭与几十种不同的行星、恒星、超新星、彗星、黑洞与白洞之间。

期间主程 HK-SHAO 也写了开发周记作为技术分享发到了 Cocos 的论坛,本篇文章摘自开发周记,希望分享的技术能够帮助大家。


大家好,我是 HK-SHAO ,随着游戏的不断迭代,游戏效果、内容也逐渐丰富,游戏项目和其代码有越来越多的细节实现需要注意。
所以我特写下周记,记录一些我在开发过程中,游戏效果实现的方法、原理,以及一些需要注意的方面。同时也很感谢 Cocos 为我们提供了免费的开发工具进行游戏的创作
囿于时间匆忙,内容可能会略显晦涩和粗糙,见谅。
1


TS 和 Creator 的简单介绍



首先,这个 Cocos Creator 小游戏的程序部分是用 TypeScript 编写的,原因在于 TypeScript 是一个比较严谨的语言,它是由微软开发的,拥有强类型并且比 JavaScript 的功能更多,被称作 JavaScript 的超集。
TypeScript 可以被编译成任何版本的 JavaScript,所以它完全可以代替 JavaScript
正是由于 TypeScript 的强类型,TypeScript 代码的编写不容易出现隐藏的错误,因为 IDE 会进行非常好的错误提示,这样能够大大提升代码的生产力。
另外,用 TypeScript 编写游戏脚本,IDE 会进行代码提示,一个对象是什么类型、拥有哪些成员变量和方法、这些方法的作用是什么,这些 IDE 都会在代码编写过程中给予提示,省去了频繁查找 Cocos Creator 官方文档的麻烦。
然而上述在软件开发过程中 IDE 提供的比较基本的功能,由于 JavaScript 的弱类型而都无法提供。
在 Cocos Creator 里面,每个实体都是节点(cc.Node),而这些节点的结构都是树状的,我个人觉得这个设计特别好,不仅对于这个游戏引擎来说能够提高某些算法的效率,而且对于游戏开发者来说能够省去不少功夫。
这里要提的是,在前端开发中,网页的 DOM(文档对象模型)也是树状的,正是因为这个数据结构的优越性,网页才能被快速的渲染和更新。
正是由于 Cocos Creaor 使用了这样的数据结构,再加上它的游戏脚本是前端里的 JavaScript 或者 TypeScript,所以 Cocos Creaor 的游戏能够被打包到几乎所有平台(包括 Windows、Android、Linux、Mac OS、Web 以及许多第三方平台例如微信小游戏、百度小游戏等)。
简单点来说,只要能够打开浏览器上网的平台,CoCos Creator 都能够一键打包并发布到这个平台。
接下来,我会把游戏开发里面的一些技术细节一一列举出来
1

动效和物理效果



非线性动效 
非线性动效相比线性的、匀速的动效在视觉观感上会好很多,这在现在的手机系统动画以及一些优秀的 PPT 内应用非常广泛。这个游戏在很多方面多次使用了一种非线性函数实现动效,以达到流畅、柔和的视觉效果。
这个非线性函数其实就是这个微分方程  y ′=y ( 1 − y )  的解 ,这是一个非常有用的非线性函数,在各种领域作用广泛。
例如开源的 Python 数学可视化引擎 Mainm 里就有它的身影,最近开源的动画引擎 movy.js 也有它的身影,除此之外这个魔幻的函数还在高中生物的种群密度、人口学、统计学等多个领域大显用途。我们先解一下这个微分方程。
如果常数 C 为 0,最后可以化成  , 这其实就是大名鼎鼎的 sigmoid 函数,其还在人工智能领域应用广泛。
这个函数曲线是 S 形的,所以高中生物也称作 S 形曲线。它也被称作 Logistic 函数,在最近的疫情分析,传染病模型里面也有它的身影,它的形状是这样的。我们可以看到,它的定义域是实数集,值域是 [ 0 , 1 ] ,并且从 0 到 1 变化非常缓和。
好了,回归正题,我在 Cocos Creator 的游戏开发中频繁的使用了这个优美的函数曲线,达到了比较赏心悦目的平滑动画效果。
它在程序里的实现方式是这样的  y ← y ∗ ( 2 − y ) 。更通俗一点来说,如果我想让一个值 t 平滑的从 min 变化到 max 可以在 update 函数里这样写。
t += (max-t)*dt*speed;
反之,如果我想让一个值 t 平滑的从max变化到 min 可以在 update 函数里这样写。
t -= (t-min)*dt*speed;
例如,我使用这种方法实现了根据触摸改变的非常平滑的摄影机缩放效果。
除此之外,根据地球到太阳的距离自动显示距离箭头,并且根据触摸自动隐藏或者显示,也利用了这个特性。
游戏中为了实现平滑而不生硬的视觉效果,我均采用改变游戏中实体受力的方式而避免直接改变速度,因为直接改变速度往往是在撞击时发生的,在其它情况尽量少发生。
例如,游戏中操控地球的前进,是给予地球刚体一个根据触摸位置而发生改变的持续力。而改变地球的旋转角度,则避免直接改变地球的旋转角,而是给予地球边缘一个小的持续力,以改变地球的角动量,从而实现改变地球的旋转角。
此外,我避免摄影机和背景直接跟随摄影机,而是通过给予它们对应刚体的受力,来实现一种错位的立体感和平滑移动感。
归一化 
一个向量的归一化就是这个向量的单位向量,在 Cocos Creator 里面,有 cc.Vec2 和 cc.Vec3 两种类型的向量,它们都有 normalize 这个方法获得单位向量。但是有时候我想把一个实数归一化怎么办?
聪明的读者可能已经想到了前文所提及的 sigmoid 函数,但是除此之外,我们其实还可以用 arctan 函数来对一个实数归一化,让它的值介于 0 到 1 之间。实际上这个函数作为激活函数在人工智能领域也和 sigmoid 一样应用广泛。
将一个实数归一化之后,我们能够较为容易的对这个数进行操作,这种方法在图形学,例如 Shader 程序设计里面应用广泛。我在游戏脚本里也用了这种方法,使得后面的计算和操作更为简便。
物理效果 
在游戏主函数里,有大量向量之间的运算,用来计算受力的大小和方向。例如,在旋转地球时,通过给予地球边缘一个持续力来改变地球的角动量,从而改变旋转角,是一种非常优美的方法。
以黑洞为例,每个天体和地球之间都会形成大小相同,方向相反的力,符合牛顿第三定律
在物理引擎中,设置太空为失重环境,并且置地球的线性速度衰减为 0,符合牛顿第一定律。
而天体与地球的引力通过牛顿的万有引力公式计算,符合万有引力定律。
除此之外,为了考虑狭义相对论的长度收缩效应,地球在高速移动的时候,会在与速度垂直的方向收缩,这样地球会一定程度上变成椭球形。
另外游戏中的所有天体都会给地球一个力,因此地球所处的引力场其实非常复杂,这在一定程度上来说带给了玩家挑战。
游戏中的黑洞会有远高于其它天体的强引力,而游戏中的白洞会有强斥力,玩家可以充分发挥自己的能力,利用这样特性来实现一些高难度技巧性操作。
游戏在设计时的细节非常多,并且还在不停地打磨和完善,例如黑洞吸进了天体会碰撞,两个黑洞碰撞会形成更大的黑洞,而黑洞和白洞相撞会湮灭。
实际上玩家想要看到这些效果的概率较低,但是通过这些特性,相信玩家能够进行许多有趣的操作。
游戏中可以发射核弹,并且这些核弹会自动寻找距离最近的可打击天体然后毁灭之。
而游戏中的彗星在撞击时冲量很大时会自动销毁,被恒星或者黑洞捕获也会自动销毁。
地球的每次撞击都会调用震动相关 API,以实现更加真实的效果
游戏的相关奖罚机制在不断调整参数,以达到更好的效果。
游戏中充分利用了粒子,以实现更好的视觉效果,这一点在生成星空、彗星的尾巴和火焰时显得尤为重要。

视觉效果



视觉效果这一方面,着色器的作用非常大,然而移动端 GPU 太垃圾,好多着色器运行都会很卡,这是非常可惜的,这里我还是介绍一下 Cocos Creator 的材质 Materials 和效果 Effects。
在 Cocos Creator 里,每个材质可以绑定一个 Texture 纹理和 Effects,Effects 是 Cocos Creator 特有的一种程序,里面是包含了配置信息、顶点着色器和片元着色器,其中片元着色器非常强大。
例如你可以用 shader 程序生成一片动态的立体星空:
利用shader生成一颗旋转的恒星:
甚至一颗立体的、动态的黑洞:
或者生成一个银河系等:

1



游戏性能优化



随着游戏的不断迭代,我实现了越来越多的效果,同时游戏主逻辑也越来越复杂,再加上要渲染的实体越来越多,游戏在移动端的帧率有下降趋势(实际上,游戏在电脑网页上能够 200 满帧运行,然而移动端的性能比较差,可能会不足 60 帧或者有略微掉帧情况)。


我们先看看游戏在调试模式下的帧率信息:
其中 FPS 就是 Frame per Second(每秒的帧数),一般来说帧率在 60 左右就足够了,实际上 Cocos Creator 将游戏锁定在了 60 帧左右,通过修改程序或者调试模式下更改游戏画面上方的配置,能提升游戏帧率上限。
现在我们看到,游戏其实是相当流畅的。然而电脑上的流畅不代表移动端的流畅,经过我的一些调试,我发现其实帧率能够在 170 以上,移动端就可以比较流畅了。实际上,上方图片还隐藏了很多信息,下面我一一举出。
  • Frame Time 游戏运行一帧所需时间 

  • FPS 每秒帧数 

  • Draw Call CPU 打包发送给 GPU 去渲染的打包数量 

  • Game Logic 运行在 CPU上 的游戏处理逻辑每帧所需时间 

  • Renderer GPU 渲染一帧画面所需时间 

  • WebGL 是否启用 WebGL 

实际上,Frame Time = Game Logic + Renderer,从上面的数据你也可以看出来。而 FPS 与 Frame Time 成反比,所以我们想要提高游戏的帧率,必须从 Game Logic 和 Renderer 下手。



优化 Game Logic



使用缓存、算法 
首先,在 Game Logic 方面,其实是游戏的脚本运行在 CPU上 的耗时,通过缓存游戏对象,自动销毁无用的节点以及挂载,并运行在其上脚本来提升游戏逻辑方面的性能。
在游戏加载时,把必要的序列化对象全部加载,并且缓存到一个列表里面。
每次获取刚体对象都要调用 node.getComponent 方法,不如提前缓存了。
调用过的方法尽量都缓存下次使用,这样就不需要重复调用方法,大大提高效率。
另外,其它的一些算法也可以来优化游戏性能,例如检测并作用一个范围内的碰撞体,可以使用四叉树算法(如果是三维则用八叉树算法)。
使用Prefab 
除此之外,游戏中经常出现的或者需要频繁重复生成的节点,利用 Cocos Creator 的序列化功能,提前把这个节点序列化,生成一个 Prefab 文件,在需要使用时可以通过 cc.instantiate 方法进行反序列化,这样就可以更加高效的获得一个节点对象。
需要注意的是,如果这个 prefab 需要反复多次频繁反序列化,需将这个 prefab 设置为这样:

优化 Renderer



自动图集 
使用自动图集能够让 Cocos Creator 自动把一系列碎图打包为整图,使用自动图集的优势非常大!
一张整图相比碎图来说网络请求次数更少,或者不需要重复进行硬盘读写,从这一方面来说,使用自动图集能够减轻 CPU、硬盘、网络等方面的压力
更为重要的是,使用整图可以让 GPU 一次渲染完成,避免 CPU 重复调用 GPU 进行渲染,这样可以降低 Draw Call,而 Draw Call 其实非常大的影响了游戏图像的渲染效率,所以使用自动图集能够提升渲染效率。
减少节点数 
减少节点数同样是通过降低 Draw Call 来提升游戏性能,所以当一个节点在地图外不可见,或者以及无用,则考虑将这个节点自动销毁。
优化纹理(Materials和Effects) 
每个 Materials 可以挂载一个 Effects,而 Effects 说白了就算着色器(Shader),着色器分为顶点着色器和片元着色器,它们是直接运行在 GPU 上的程序,能够在 GPU 层面直接对图像进行操作,效率非常高。
在合适的时候使用着色器来实现效果,能够极大地提升游戏性能
然而,着色器虽然跑得很卡,也要适可而止。移动端的 GPU 性能远低于电脑,在电脑上高清无码满帧,可能在手机上就卡成 PPT,所以复杂的需要大量运算的着色器可能会拖慢游戏图像的渲染速度,使游戏性能大大降低。
例如下面这个着色器,在电脑上满帧,手机上会卡成 PPT。

最后非常高兴能够和 cyb 和 lby 合作这个游戏项目,游戏的初衷是能完成自己喜欢的作品,并给玩家带来快乐希望我们能够一起加油把这个项目继续完善得更好。

游戏已在微信发布,欢迎感兴趣的童鞋扫码体验噢。


戳【阅读原文】前往社区查看更多详细信息,快乐交流~
浏览 35
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报