【技术分享】《星旅 StarTrek》的开发周记(2021年2月7日)

《星旅 StarTrek》的开发周记(2021年2月7日)

游戏介绍和故事背景请直接跳转到文末

距离写上一篇周记时间已经过了一个星期,1月30日的时候小游戏的开发进度还只有40%,这几天保肝又完成了55%,现在小游戏的完成度已经有95%了。小游戏从零开始开发到现在刚好已经过了两个星期。这14天里我从零开始学习CocosCraetor引擎和TypeScript语言,写了9400多行TypeScript代码(快一万行了,Cocos Creator的脚本基本上没有自动生成的),应该还算挺高效率的。昨天为游戏定下了一个名字:《星旅 StarTrek》,画了一个LOGO,做了一个宣传图,写了一个游戏自审报告,填写了一堆信息,把代码提交给微信审核,不出意外的话再过一两天就能审核通过上线发布了。

同样,在这七天中我又遇到了很多坑,也学习了不少技术,特此记录,也分享给大家作为前车之鉴。接下来我会对游戏开发中遇到的问题、使用的技术、游戏的背景和玩法介绍等一一写下。囿于时间匆忙,本人文笔粗糙,还请谅解。

能够自动跟踪的导弹

在游戏中,你可以点击自己发射能够自动攻击目标的导弹,还可以点击自己通过拖拽延指定方向和力度发射导弹。其实自动跟踪导弹并没有用什么高深的寻路算法,而且如果用上的话对游戏性能要求也很高,因为游戏中的天体数量特别多,玩家或者UFO也可以短时间内发射数十颗导弹,如果用寻路算法的话不仅效果可能不好,而且会使游戏性能大大降低。所以我使用的是一个基于物理模型的简单算法,原理特别简单,效果也非常好,在物理引擎里非常容易实现。

这个算法其实就是在导弹和距离最近的目标之间加了一个原长为0的弹簧,符合胡克定律 \vec{F}=k·\vec{x} ​ 。在游戏的每一帧,通过遍历附近的所有目标,计算导弹到目标的距离,找到距离最近的目标,然后以从导弹到目标的向量乘以缩放比例,作为给导弹的力作用在导弹的刚体上。这样就轻松的实现了导弹的自动跟踪效果,并且导弹能在运行中配合粒子画出优美的曲线,另外一个优势就是目标距离很远时导弹能够自动加速,距离近时则依靠惯性去打击目标(太空中没有阻尼,所以并不会减速)。

上图你可以看到我作用给导弹的并不是冲力,而是一个冲量,其实效果基本上是一样的,因为冲量的表达是 F\Delta t ​ ,而游戏中每一帧的 $\Delta t$​ 基本上是一个定值。另外,之所以我要对力进行归一化,是因为实际上我给导弹设定了一个线性阻尼,这样的话我便只取力的单位向量即可,可以让导弹拥有一个最大速度,而不是在距离远时无线加速。

同样,游戏中火箭:rocket:、地球:earth_asia:、或者UFO​:flying_saucer:跟随玩家的手指,其实也是这个原理,只不过是把其看作导弹,然后它的攻击目标是玩家指尖罢了(但是不会爆炸,更不可能撞伤或者炸伤玩家)。

游戏中的动画以及爆炸效果

游戏在转场、按钮点击,星球爆炸消失等多个地方利用了动画效果,而不是强硬的切换或者直接消失,这样可以给玩家带来比较好的视觉效果。利用Cocos Creator的缓动系统 cc.Tween 可以非常方便的实现一些动画。上一篇周记讲过,Cocos里的每一个实体都是一个节点 cc.Node ,这个节点其实可以有很多属性,例如位置、旋转角、颜色、锚点、透明度等。通过缓动系统,你可以指定一个缓动函数,缓慢的将某个值改变到另一个值。这里我拿Manim的动画系统举例,实际上与Cocos仍有差异,但是效果差不多。(图片来源于Manim.ml,是MK组织为Manim编写的中文文档)

在Cocos里,通过缓动系统缓慢的改变一个节点的位置,大小,透明度等,便实现了视觉效果较好的菜单打开关闭动画、渐变转场、星球毁灭时的缩小消失等效果。

这里的smooth函数其实是一种平滑的阶跃函数,说白了其实就跟上一篇周记里写的 sigmoid 函数很像(sigmoid 有点像step smooth的谐音),但是其不需要指数运算,并且运算效率比较高,在数值很大时稳定性也很好,我们在Cocos的源代码里能一探究竟。

image

爆炸效果

实际上爆炸效果制作非常简单,先用粒子系统做一个爆炸,指定一个自动停止发射的时间,然后设定自动销毁,把这个节点做成一个预制体(prefab),当某个位置需要爆炸时,就在这个位置将这个预制体实例化为一个节点,然后把这个节点添加到场景中即可。这里我以点燃气体星球产生爆炸,并给玩家带来一个反方向的冲击力为例。

游戏中星球的生成以及星球的呼吸、移动和自转效果

为了移动端的游戏性能,实际上每一帧计算和渲染的星球不会超过100个,每当玩家到达了新的区域,便随机生成新的地图。理论上玩家可以探索到无限大的,永不重复的宇宙。游戏地图的生成机制是这个项目以开始就马上完成的,实际上我对这个也没有任何经验,所以设计的可能不太好。生成系统会把地图分成4个区域,由里到外分别是屏幕显示区域、玩家视野、生成区域、销毁区域。

每一帧,系统会在灰色区域自动生成新的天体,在灰色区域记录并在下一帧销毁记录的所有天体。在程序中是可以调整不同天体的生成比例的,在下面的代码中其实你可以发现,我们设计了近40种不同的天体。

新生成的每一个天体都会有呼吸(由正弦曲线控制的缓慢放大缩小动画)、移动和自转。其实现原理是给每一个不同的天体的节点挂载不同脚本,在脚本开始时随机生成呼吸、自转角速度、移动速度,然后在每个 update 函数里控制这个节点的缩放、角度和位置。以行星挂载的脚本为例。

太阳系的构建

在游戏的主界面菜单中,你可以进入太阳系并观察到整个太阳系的运转。实际上构建这样一个模型利用的就是节点与子节点的关系,通过设定父节点锚点为太阳中心加上一个偏移(模拟椭圆的焦点不在圆心的效果),然后把太阳系行星绑定到这个节点上,控制父节点的旋转角,就可以实现一个行星的公转,而控制这个节点的旋转角,就可以控制这个行星的自转。最后加上粒子特效和摄影机的呼吸效果,便打造了一个太阳系的模型。

题库、分享以及好友排行榜

在游戏结束后,玩家可以选择答题来获得重新开始这一关卡的机会,题库包含了天文的基础知识、人类尤其是中国古今天文成就。题库量还是比较大的,直接写在了脚本里。

游戏菜单和失败的排行榜界面有分享按钮,玩家可以点击按钮将这个小游戏分享给其他人。实际上分享特别简单,如果是微信平台,直接调用微信的API即可。不过要注意的是,为了兼容性,在使用微信API之前必须先判断环境是否为微信环境。

image

微信的 shareAppMessage 方法实际上还可以传入一个对象指定分享的标题以及图片等,但是因为我懒或者没必要我就什么都没传了,微信默认标题是这个小游戏的名字和当前画面截图。

微信提出了开放数据域的概念,提供了一些较为方便的API来实现好友排行榜,但比较奇怪的是,微信为了保证数据的安全,开放数据域是一个独立的JavaScript环境,在Cocos里需要另外建立一个微信开放数据域项目制作排行榜的UI和相关逻辑,然后把这个Cocos项目合并到之前的游戏项目。然而合并之后,游戏就携带了一个新的游戏引擎的js文件,并且开放数据域工程无法分离引擎!这样项目的体积又会增加1mb多,然而此使我的游戏打包编译后已经超过微信规定的4mb限制了。超过4mb后这个小游戏就无法发布,怎样才能突破4mb限制呢?

突破4mb限制

网络加载资源

实际上我并没有突破4mb限制,而是把部分资源文件进行了压缩,把游戏背景音乐放到了腾讯云的服务器上进行网络加载。Cocos有一个 assetManager 对象提供了 loadRemote 方法能够加载网络资源,并提供了一个回调函数来使用这个资源。

游戏分包(Bundle)

将游戏分为不同的包,然后把一些包放到网络上,在游戏过程中加载分包。不过我没用过,具体不清楚。

删减引擎、分离引擎

在cocos的项目设置中,你可以删掉游戏中没有用到的Cocos 2d游戏引擎模块,这样能够减少游戏引擎代码的占用空间。不过更简单的方式是在构建项目时使用分离引擎,这样打包的微信小游戏就不会携带Cocos的游戏引擎,而是利用微信提供的Cocos游戏引擎插件。

资源压缩

资源压缩这一方面特别重要,不仅能够减小游戏包的体积,体积小的资源加载速度也更快,占用资源也更少。游戏里的资源主要是贴图、字体文件(ttf)、音频、文本。接下来我会一一介绍压缩它们的办法。

  • 贴图:减少贴图的尺寸,不需要透明度则转为jpg,以及在ps里勾选较小文件等选项,还可以利用图片压缩软件进行压缩。
  • 字体文件:字体文件压缩的性价比特别高!对于我这个独立游戏来说,游戏内的文本是固定的,因此字库文件并需要所有的汉字。因此我们可以找到游戏里出现的所有字符,在字库文件中只保留我们所需要的字体就好,这样就能大大压缩文件体积,一次能压缩几百kb,性价比特别高。这里我使用在Github上开源的Fontmin软件进行了字库的删减(https://github.com/ecomfe/fontmin-app/)

  • 音频:通常音频文件为mp3文件,这里我通过ffmpeg将其转码为m4a文件,并合并两个声道为一个声道,能够大大减小音频文件的体积。ffmpeg的使用也很简单 ffmpeg -i in.mp3 -ac 1 out.m4a 即可。不过要注意的是mp3有些时候有编码版权问题,可能无法转换为m4a。
  • 文本:文本主要指的是代码文件,因为编译之后最终都是js代码,而js代码是可以大大压缩体积的(通过替换变量名,删除换行空格等方式),网上有很多js代码压缩的软件。

游戏贴图的制作和调整

这里我使用的是Adobe Photoshop。有时候游戏贴图边缘会出现黑边,可以使用ps构建选区,根据选区添加一个蒙版,然后使用滤镜里的最小化调整边缘,直至黑边消失。

还可以通过ps的色相调整功能更改贴图的色相,这样能立即得到一个另一种颜色风格的贴图。使用ps的杂色-蒙尘与划痕滤镜,可以减少图片中的尖角,有时候能够得到去锯齿的效果。使用滤镜库能够得到另一种风格的图片。当然,反相,黑白化,调整亮度、对比度、色彩饱和度等也能够将贴图是视觉效果调整的更好。

昨天,我用ps简单的做了一个游戏图标,又我按照微信提供的一个例子,花了十几分钟为游戏做了一个简单的宣传背景图。在ps里面,将素材拖进去,调整缩放、角度,加个背景,用钢笔工具构建选区,复制图层改变色调和透明度,在Shadertoy随便截了一张星空的贴图,调整透明度作用于背景,加几个滤镜,为贴图增加一个动态模糊,调整角度…很快啊,游戏宣传背景图就做好了!看,年轻人就是这么不讲*德。

image

打包Android项目并构建APK

原生平台构建的游戏性能比较好,所以我尝试打包了一个APK,不过这之间遇到了很多坑。在Cocos里要构建APK,必须下载 安装JDK,以及安卓的SDK、NDK ,官方和网上都推荐使用Android Studio。刚好我本来就安装搭建好了Android Studio的环境,于是顺利的把SDK和NDK都安装好了,系统环境变量也配置好。然而在用Cocos打包时一直出现Java的空指针错误,折腾了很久都未果。最终发现:用Android Studio下载安装的NDK是阉割版! 所以还请到Android开发者官网,下载完整版最新的NDK,然后添加到系统环境变量,确保在cmd里输入 ndk-build 后有响应。 最后利用Cocos打包APK,打包过程中内存和CPU会飙升,在任务管理器中可以看到数个Cmake++在疯狂编译Cocos的C++代码,JDK在编译Android的Java代码,风扇狂转,就我的垃圾笔记本编译时间会非常久,不过最终编译成功生成了APK文件。似乎Cocos的编译脚本里已经自动给APK签名了,所以不需要手动签名。不过为了确保安全,可以使用工具生成签名文件为APK手动签名。

鼓励创作

游戏由我们团队爆肝创作,游戏目前完全免费,没有任何广告,而游戏素材放在腾讯的服务器里却是要收费的。如果大家喜欢我们的作品,可以转发支持鼓励我们的创作(自愿)。

游戏介绍

游戏很快就能上线发布了,这里我简单介绍一下游戏的故事背景、玩法等。

《星旅》是一个太空科幻题材的微信小游戏。游戏的启程、殖民、拯救、逃亡、毁灭、对抗6个关卡讲述了一个关于人类命运存亡的科幻故事。在游戏中你可以探索无限大的宇宙空间,在符合物理的太空中自由穿梭与几十种不同的行星、恒星、超新星、彗星、黑洞与白洞之间。你可以控制火箭穿出地球大气层、控制地球逃离太阳系、控制UFO毁灭银河系、还可以发射自动跟踪导弹与UFO在处处充满危险的宇宙战场中一决胜负。2077年,人类的命运如何?就靠你了!

背景故事

前三个关卡(启程、殖民、拯救)

旅行者一号由美国在1977年9月5日发射,100年后的2077年,人类命运突然发生了改变。太阳:sunny:突然变得极其不稳定,那一年数次史上最大的太阳风暴席卷全球,造成了无数人造卫星和太空探测器的损坏,以及数天的停电。经中国科学家观测,首先发现了太阳内部的氢在剧烈消耗,氦在大量积累(比较奇怪的是,氦的积累速度远远超过了氢的消耗速度,这违反了物理定律,让物理学家们百思不得其解)。简单点来说,太阳正在急速老化,科学家预测100多年后太阳将会剧烈膨胀,并发生氦闪,能够瞬间气化整个太阳系。

于是人类在联合国紧急成立了组织,利用现有技术大力发展太空科技,并陆续发射殖民者号战舰和拯救者号战舰。殖民者号战舰携带数亿植物种子、孢子以及数百万动物冷冻胚胎向宇宙远航,寻找易于人类居住的星球,创造新的家园,延续人类文明。当殖民号飞船寻找到KTS-2星系里的一颗宜居星球(代号ET2)后,人类发射比殖民者号大数百倍的拯救者号战舰,携带数十万拥有优先权和勇气的人类前往新的星球移民。抵达新的星球后,拯救者号战舰必须携带大量资源立即返回地球,然后携带更多人类陆续移民。

后三个关卡(逃亡、毁灭、对抗)

然而,人类的计划最终还是失败了。按照人类计划,至少要300年才能完成全人类的移民,在拯救者号战舰启动后,科学家对太阳进行了一次新的全方位分析,发现太阳发生氦闪的的时间比预期早了很多!现在人类只剩下40年了!于是人类启动了新的计划,直接利用核聚变行星发动机推动地球前进,理论上只需要37年就可把地球推进到太阳系以外,而再需要100年的加速和280年的流浪和70年的减速,地球就能到达KTS-2星系,并按照科学家的预想进入恒星轨道,与ET2建立稳定的双星系统。

在接下来的时间内,人类的科技发生了翻天覆地的改变,航天科技的迅猛发展带动了人类科技指数级发展。人类逐渐摸清了暗物质、反物质的本质,并能够利用反物质制造源源不断的能源。此时,地球仍然在流浪,但是科技的迅猛发展带来了人类生活幸福指数的不断提高。

在这场与每一个人息息相关灾难面前,人类证明了自己:同舟共济、守望相助是完全可以做到的。 人类的勇气与坚毅,将永刻于星空之下。

(游戏的故事背景有多处借用、致敬了刘慈欣的科幻作品设定。刘慈欣是我非常喜欢的科幻作家,它的科幻作品从来都有一种魅力)

番外篇

然而,不速之客又来了!科学家观测到一个直径远远超过一万公里的UFO正在高速向地球冲来,其经过的恒星和行星都发生了爆炸。科学家预计UFO还有80年就会抵达地球,显然对方不怀好意,人类文明将再次面临毁灭!

人类到底能否生存?就靠你了!欢迎大家来玩我们的游戏,谢谢大家的支持!

8赞

你说了这么多 怎么连个视频都没有 年轻人不讲武德 直接贴二维码 我是没想到

收款码删了 是真的牛逼

1赞

非常抱歉!收款码确实可能有些不妥,所以删了。不过作为技术分享,放上二维码希望得到大家的赞赏是一件很正常的事情,也完全是自愿的,不知道您酸什么呢?

你自己觉得不妥 还说我酸什么 不妥的人 看别人说什么都以为是攻击他

歪瑞谷德 :laughing:

1赞

14天美术这么好!

1赞

谢谢鼓励,这个小游戏是我和我的另外两个同学一起完成的,我主要负责程序以及策划。图片素材是网上找的商业可用素材+自己的修改

细节好到位啊,还有公式的运用也很棒,加油啊:muscle:

1赞

谢谢鼓励啦!:)

试玩了游戏,怎么火箭左右的运动方向与手指的左右运动方向相反?另外,没看到有 3D 效果

嗯,原本有shader实现3d的效果,后来发现在桌面端流畅,但在移动端性能不够理想,就去掉了3d效果的shader

第一关是:rocket:火箭飞出地球,手指在火箭下方控制火焰:fire:的推进方向