- assets 游戏资源文件夹,包括所有图片、音效、脚本等资源。
- animation 游戏动画资源文件夹,包括游戏中用到的所有动画(目前只有两个)。
- fonts 游戏字体资源文件夹,包括游戏中用到的所有自定义字体。
- prefabs 游戏预制资源文件夹,包括游戏中用到的所有预制体资源。
- platforms 所有生成平台的预制资源。
- skins 所有人物皮肤的预制资源。
- 其他游戏需要的预制资源。
- resources 游戏场景的资源文件夹,包括所有音效、粒子、图片资源。
- audio 所有音效资源。
- texture 所有图片、粒子资源。
- scripts
- dataManager 管理玩家数据的脚本(即每次重进游戏需要读取的数据,包括当前拥有的皮肤、钻石总数等)。
- game 所有与游戏进程有关的脚本资源文件夹。包括人物跳跃的控制、平台的生成,下落与消失、游戏镜头跟随主角的移动等。
- resManager 管理游戏资源的脚本,包括所有皮肤的预制资源、所有平台的预制资源、所有音效资源等。 * UI 管理所有场景的脚本资源文件夹,其中每个文件负责管理一个单独的游戏场景。
- dataManager 管理玩家数据的脚本(即每次重进游戏需要读取的数据,包括当前拥有的皮肤、钻石总数等)。
- settings 包括所有与项目配置有关的脚本的文件夹。
- 选题 游戏灵感来源于手机游戏幻径及其他类似游戏。
- 素材 此次游戏开发中大部分素材来自网络资源,少部分资源如粒子资源是我在EffectHub网站制作完成的,预制体资源都是我自己选取原始素材然后经过cocos creator加工后制作而成。
- 声明 本次游戏所有工作,包括前期准备、素材搜集与加工、游戏架构、游戏代码等均由本人独立完成。
- 开发环境
- 开发语言:JavaScript (ES6)
- 操作系统:Windows 10 家庭中文版
- 处理器:Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz 1.80GHz
- 场景编辑器:cocos creator v2.1.1
- 代码编辑器:visual studio 1.36.1
- 发布及真机调试工具:微信开发者工具 v1.02.1907160
- 游戏介绍:
- 名称: PathToGod
- 核心玩法: 玩家通过点击屏幕的左右两侧来控制游戏人物左右移动,以此控制人物借助不断掉落的平台向上跳跃。同时在跳跃过程中,玩家需要注意控制游戏人物不要触碰到障碍物或掉落平台。
- 特点:
- 此游戏画风和游戏场景均较为简约,其中主要游戏场景仅由背景、人物、平台组成。
- 同时玩家每次进入游戏后都会在四张背景图、四种不同样式的平台中随机选择。在保证不影响玩家游戏体验的同时提供一个和谐、美观的背景和布置。
- 游戏的核心玩法也符合小游戏简单、易上手、同时又有良好的可玩性的特点。
- 游戏元素
- 游戏人物。玩家控制的游戏人物,目前共有四种皮肤可供选择。
- 游戏平台。游戏人物站立的平台,包括普通平台、组合平台和障碍物钉子平台。其中普通平台共有四种样式;组合平台共有三种样式,每种样式共有2-4种平台;钉子平台共有左右两种样式
-
开始场景
进入游戏的初始场景。
最上方为游戏标题及logo。
中间为开始游戏按钮,点击后即可进入游戏场景开始游戏。
最下方从左至右依次为:
- 商店入口按钮。点击即可进入
- 商店场景购买皮肤,其中此按钮当前显示的图片即玩家当前使用的皮肤。
- 排行榜界面按钮。点击即可进入排行榜界面查看历史最高分记录。
- 游戏静音按钮。点击即可切换游戏为静音/非静音状态。
- 重置游戏数据按钮,点击即可重置游戏数据。包括已拥有的皮肤、已拥有的钻石总数、历史最高分等数据。
-
游戏场景
主要的游戏场景,玩家在此场景内控制人物移动、向上跳跃。
- 左上角为游戏暂停/继续按钮,点击即可暂停/继续游戏。
- 最上方中间为玩家当前分数。
- 右上角为玩家本局吃到的钻石数。
- 场景中央为玩家控制的游戏人物和生成的游戏平台。
-
游戏结束场景
游戏结束后的场景。
- 最上方为玩家本局最终获得的分数。
- 分数下方为玩家历史最高分。
- 最高分下面为玩家本局获得的钻石数。
- 最下方从左至右依次为:
- 排行榜界面按钮。点击进入排行榜界面。
- 重玩一局游戏按钮。点击重玩一局游戏。
- 游戏主页按钮。点击即可进入游戏主页,即游戏开始界面。
-
商店场景
此场景中玩家可用获得的钻石购买皮肤。
- 左上角为返回按钮。点击即可返回游戏主页。
- 最上方中央为玩家当前拥有的钻石总数。
- 钻石数下方为玩家当前浏览的皮肤名字。
- 名字下方为玩家当前浏览的皮肤。
- 最下方为选择/购买按钮。
- 若玩家已拥有当前皮肤,点击即可选择该皮肤。
- 若玩家未拥有当前皮肤且钻石总数大于皮肤价格,点击即可购买该皮肤。
- 若玩家未拥有当前皮肤且钻石总数小于皮肤价格,点击提示钻石数量不够。
-
排行榜场景
玩家历史最高分排行榜场景。
从上至下依次为玩家历史分数的前三名。
-
cocos creator游戏引擎的学习与使用。
谈到技术难点首先肯定是游戏引擎的学习。在开发此次小游戏之前,我对cocos游戏引擎一无所知。
在游戏开发过程中,我从游戏引擎的开发文档开始学习。首先学习了官方小游戏的制作教程,基本熟悉了cocos编辑器的结构和功能。
这次综合实验不仅是小游戏的开发过程,同时也是cocos游戏引擎的学习过程。在游戏开发过程中我遇到了很多有关cocos creator的问题。但由于是一个人开发的原因,因此只能自己上网搜索相关问题和解决方法。然而不管是cocos官方中文社区还是百度谷歌等搜索引擎,几乎每次都无法给我一个满意的回答。因此每次碰到问题后,我都是先参考官方相关技术文档和API指南(在此吐槽cocos官方文档和API真的有待“进步”,比如对于ScrollView组件的scroll-end, touch-up等滑动事件,官方技术文档和API都没有给出详细的定义,所有事件的解释均为“注意:此事件是从该组件所属的 Node 上面派发出来的,需要用 node.on 来监听。”因此我只能通过名字推测以及找到源码中的定义来了解这些事件),在有了一个大概的概念之后再集中网上能够搜集到的所有零碎知识,最后经过自己实践并不断完善、修改后才能达到自己想做的效果。
不得不说一个人开发游戏在学习、运用新知识以及debug等的过程中是真的辛苦,有时候两个人讨论几分钟,结合网络资料就能出结果的问题一个人需要几个小时甚至一天才能通过搜索引擎、自己思考得出答案。但同时这样也能让自己对新知识掌握的更牢固。
-
人物的跳跃。
在实现人物跳跃的开发过程中,我尝试了moveTo, moveBy, jumpTo, jumpBy等多种不同的方式,但都会出现各种各样的bug:如跳跃途中人物出现“瞬移”,未跳至平台上人物便开始向下坠落等。
在经过艰苦的问题搜索、官方文档死磕、bug调试后,我最终选取了moveBy结合刚体重力实现人物跳跃的方式。即通过moveBy让人物移动至平台上方,在到达最高点后再让人物受重力影响向下坠落。
这种方式只有一个bug,即有时候跳跃至平台上方后人物不会受重力影响向下坠落,而是“卡”在半空,导致下一次跳跃会出现“瞬移”。又经过一番艰苦的console.log历程后,我发现问题在于我设置了人物刚体组件的allowSleep属性为true,而此属性会导致刚体进入“休眠”状态,因此才会出现人物不下落的情况。
具体实现参考scripts/game/Character.js文件。
-
人物、平台刚体组件的添加和碰撞检测。
此游戏最重要的部分便是游戏人物与游戏平台的碰撞检测。对于碰撞检测,我首先参考了官方API文档,结果发现根本看不懂。
然后我又在网上搜索cocos碰撞检测的实现,但是网上似乎没有关于这个问题的完整、系统的解释。经过几个小时的搜索整理之后,我终于了解了cocos碰撞系统的使用详情。在此大致总结如下:
-
要实现碰撞检测。首先需要在脚本中开启物理系统和碰撞系统:
cc.director.getPhysicsManager().enabled = true; cc.director.getCollisionManager().enabled = true;
-
只有碰撞组件之间以及物理组件之间才能发生碰撞检测。
- 碰撞组件之间发生碰撞会触发回调函数(如果用户有在脚本中定义的话),而不会产生物理碰撞效果(即碰撞后会停止、掉落等效果)。
- 物理碰撞组件依赖于刚体组件,只有添加了刚体组件的结点才能添加物理碰撞组件。
-
物理组件之间发生碰撞默认不会触发回调函数(就算用户有在脚本中定义),如果需要触发,则需要先在脚本中将物理组件依赖的刚体组件开启回调监听:
rigidbody.enableContactListener = true;
-
除了上述设置以外,还需要将两个需要进行碰撞检测的物体分别添加到两个已设置可碰撞的分组,否则它们之间不会进行碰撞。
-
-
游戏相机跟随游戏人物的移动。
此游戏还有一个很重要的部分在于每次人物跳跃后要确保人物仍然处于游戏场景的中央。
在参考官方文档后我想到了用摄像机位置的移动来实现。在脚本中添加对应代码后发现效果并不如意。因此我又去搜索网络资料。后来发现需要将人物的坐标转换为世界坐标才能正确移动摄像机。
在实现摄像机跟随人物的移动后,又发现背景图片、暂停/继续按钮等静态组件也出现了移动,于是我又参考了官方文档发现可以用一个静止的摄像机来拍摄这些静止的物体,同时用另一个动态移动的摄像机来跟随人物的移动。
而为了实现这个效果,我又去了解了摄像机的cullingMask属性,即此摄像机只负责拍摄哪些分组的物体,最终结合官方文档和网络上各种解决方法才实现想要的效果。
同时为了不让跳跃途中屏幕过于“抖动”,我设置了只有当人物在向上跳跃时才会更新摄像机位置,即当人物在下落时不更新摄像机位置。
-
游戏物体(平台)的管理和样式的自适应。
在玩家控制人物跳跃的过程中,需要不断生成新的平台,因此需要一个专门的平台池来管理已生成和未生成的平台:
存储未生成的平台以便将来需要生成时直接使用,同时对已经掉落的平台进行销毁,以减小内存消耗。具体实现见PlatformPool.js和PlatformManager脚本。
由于组合平台和钉子平台的预制资源只有普通平台的版本,且每次进入游戏时平台样式都会从四种样式中随机选择,因此还需要将组合平台和钉子平台中普通平台的图片资源更换为当前随机选择的平台图片资源。因此对每个平台的预制资源,我都添加了PlatformManager脚本对其样式进行更改。
同时对于部分平台,此脚本还会对其进行左右位置的随机,以实现障碍物会随机出现在平台左右两侧的效果。
-
游戏结束的判断。
游戏共有三种结束方式:
-
玩家触碰到障碍物。
这种游戏结束的判断比较简单,将障碍物的碰撞组件的分组设置为Obstacle,平台的碰撞组件分组设置为Platform,每次碰撞检测后判断与人物相碰的物理组件的分组,若为Obstacle则游戏结束。
-
玩家未跳跃至平台。
这是三种结束方式中最难判断的一种。需要用到物理系统中的射线检测。
官方对于射线检测说明不够详细,导致我最开始使用时并没有检测到任何物体。后来经过搜索资料后才了解射线检测的正确使用,但是从人物中心往下检测100个像素点的方式只检测到了人物的碰撞组件,并未检测到人物下方的平台碰撞组件。
开始时我以为是距离不够的原因,于是将100改为500,但没有检测到。
后来我以为是横坐标位置不对,于是希望通过修改横坐标能够检测到平台,依然没有检测到。
最后我发现原因在于我是在人物的垂直速度小于零时就开始了射线检测,而人物在跳跃途中便已经开始受重力影响。因此在人物离开平台后的“一瞬间”便已经具有了小于零的垂直速度,而此时人物并没有处于平台碰撞组件的正上方,因此才会出现检测不到组件的情况。
同时在检测到人物没有跳跃至平台上后,需要实现人物从平台“后方”掉落的效果。但由于人物结点位于平台结点的下方,因此人物会默认覆盖平台优先显示。查阅相关资料后我发现可以通过设置结点的zIndex属性和结点的setSiblingIndex方法来实现结点的显示优先级。最终通过修改人物结点的zIndex属性实现了这个效果。
-
玩家在平台掉落之前没有跳离平台。
这种判断方式是通过判断人物与摄像机之间的垂直距离是否大于一个指定值来实现的。
-
-
玩家信息/数据的持久存储和场景间数据的传递
由于游戏需要实现玩家当前拥有的皮肤、钻石数等数据的持久存储,因此在查阅了官方文档后我采用了
cc.localstorage.getItem的方法来实现,具体使用参见官方文档。
同时我添加了一个DataManager的脚本专门用来管理游戏的各种数据。这个脚本采用单例模式实现,即各场景均可以访问到这个脚本,从而顺便实现了场景间数据的传递。
但最后在真机调试的过程中,却发现了诸如“n.split is not a function”、“r.push is not a function”等的语法错误,经过查阅后发现微信开发者工具并不支持所有的JS语法(这个坑让我调试了半晚上),因此最终我自己用另外的代码代替这些内置函数实现了相同的功能。
除此以外,真机调试时,我还发现一个巨坑(这个坑又让我调试了半晚上)。那就是cc.localstorage.getItem在cocos creator编辑器和微信开发者工具中有不同的返回值!!具体如下:
- getItem函数的参数为一个字符串,若系统中存在与此字符串相同的键值,则:
- cocos creator会无视存储时的数据类型,返回一个字符串类型。即就算存储的是数组[1, 2, 3]也会返回"1,2,3"。
- 而在微信开发者工具中,会直接返回原有数据类型,即返回存储时的数据类型!
- 若系统中不存在相应键值,则:
- cocos creator会返回null。
- 而微信开发者工具会返回空字符串!
这两点导致我的原有代码完全混乱,游戏出现各种莫名其妙的bug,最终通过修改代码将其改为微信开发者工具”适配“的版本后才通过编译。
- getItem函数的参数为一个字符串,若系统中存在与此字符串相同的键值,则:
-
商店界面的实现及完善。
具体难点在于以下三点,总之由于官方文档的”精简“,我不得不又各种搜索调试才达到想要的效果,已经回想不起一个人盯着电脑debug的惨状,具体实现见scripts/game/ShopScene.js文件。
- 皮肤滑动浏览的实现。
- 每次滑动结束后皮肤的定位。
- 每次进入商店界面自动定位当前已选择的皮肤。
主要的测试如下:
机型 | 测试效果 |
---|---|
Oppo R9s | 各项均正常 |
iPhone 8 Plus | 人物跳跃较慢,其余UI显示正常 |
Vivo V3Max A | 人物跳跃正常,部分UI数字显示不完整 |
iPhone X | 人物跳跃正常,部分界面横向显示不全 |
iPad Pro | 人物跳跃正常,部分界面竖向显示不全 |
经测试后,最终各种场景的UI设置如下表:
场景 | 设置 |
---|---|
StartScene | Fix Width, Fix Height |
ShopScene | Fix Height |
RankScene | FIx Height |
GameScene | FIx Height |
GameOverScene | Fix Width, Fix Height |
本次开发是我第一次借助游戏引擎开发游戏,开发过程中我熟悉了cocos creator引擎的使用。
但同时,我也感受到了cocos creator官方文档的不足,以及个人开发游戏的优缺点:优点在于掌握知识牢固,能力提升水平高,缺点在于开发进度、bug调试进度慢,游戏内容不够充实。
本次开发与我预想的结果差距还是挺大的,原本我还想实现不同的游戏模式、游戏道具的添加与道具等级的升级、微信排行榜等功能。但中途很多时间都花在了学习cocos API以及bug调试上,导致最终没有时间来实现这些功能。
同时,在参考了其他同学的成果后,我发现我选择的这款小游戏开发难度较高,主要技术重难点已经列举如上。有点长,但却是我开发历程的真实记录,中间碰到了很多问题官方API根本没有解答或者提及,比如cc.localstorage.getItem函数,官方根本没有说明它在微信开发者工具上会有不同的表现,最后还是在一篇CSDN博客上看到一样的问题才知道答案。
总之开发过程充满艰辛,同时也有收获成果时的充实,不管最终游戏效果怎么样,我已经尽力去做好了,希望自己下次在做类似的任务时能够完成的更好!