cocos的刚体和精灵绑定_使用Cocos进行2D和3D混合开发

e7c1378d89fa706fa7f70fed4c9161c2.png

这是异名上手cocos后,正式开发上线的第一款游戏,用的引擎版本是v.2.2.0,游戏一共有6个场景,其中一个3D场景,4个2D场景,在这里做简单的一个复盘和回顾,因为需要我们平台的账号登录就不能给大家开放试玩了。

项目构建流程

目前来看暂时还没有要引入第三方构建工具的必要。除了几个配置的js和简单的css,合起来就十几K,引擎自身还支持按模块拆分,打包之后引擎也做了混淆压缩和命名hash处理,像精灵图合并和图片压缩目前也有基于引擎的成熟解决方案。另外这个游戏是属于ipad端的游戏,相对于微信小游戏和H5小游戏,一个是因为它有足够的代码存储空间,所以不涉及到资源的拆分问题,二个是因为是页游,我们假定都是在WiFi场景下使用iPad,所以它对流量的要求也没这么苛刻。因此在这个项目,我只基于引擎做了下面这几个常规处理:

  • 图片生成图集
  • 自定义发布模版
  • 图片做压缩

图集管理

图集这里我主要是利用了引擎自身提供的自动图集功能,它会把当前文件夹下的所有 SpriteFrame作为碎图资源,所以我们只需要把不同图集的资源划分文件夹,然后在文件夹下生成自动图集对象就可以了。当时在开发的时候其实我还是配套使用TexturePacker来处理一些小图集,现在回过头来看其实完全可以弃用它,自动图集能够更精简我们的工作流,想要理解这个工具的同学可以看我的上一篇文章。

自定义发布模版

针对发布后的项目,我有两个更改需求:一个就是更改网页的title,另外一个就是更换引擎加载时的logo;这块主要是可以通过自定义发布模版来是实现,复制一份打包之后的index.html,然后更换里面的logo链接和title,然后放到build-templates里面再重新打包一次就可以了。因为是原封不动地挪动build-templates里面的文件去覆盖打包之后的文件,所以在使用过程中有个问题就是当你需要打包vconsole进去的时候,你就得动手去修改模板了8f4bd043c3df4de0db1ad98eefdd4ce2.png

图片压缩

这一块我使用了社区里面的一个插件,它 集成了pngquant 进行图片压缩,目前只能实现png压缩,先打包,然后通过工具去遍历打包之后的目录下面的图片,压缩并覆盖。另外因为我司有自己的发布系统,它对web项目有一套自己的打包和发布流程,而引擎打包之后我需要做个重命名才能走我们的发布流程,所以我还对这个插件做了二次定制,改造之后的插件界面如下,发布之前只需要在构建后,按顺序执行下面两步就可以了:

ad85ea321b1c56900dc56319173989e5.png

场景加载

因为这个游戏有多个场景,还有一个3D场景,所以需要专门做场景的加载。我是直接在主场景上实现的,当玩家点击在进入下一个场景的时候,通过cc.director.preloadScene去预加载下个场景,并且获取加载加载的进度,然后渲染到页面上,等到场景加载完毕之后就跳转过去。

function toPreloadScene (sceneName: string, jump: boolean = false) {
cc.director.preloadScene(sceneName, (completedCount, totalCount) => {
this.loadProgressBar.width = (completedCount / totalCount) * this.loadProgressBox.width;
this.loadProgressLabel.string = (completedCount * 100 / totalCount).toFixed(0) + '%';
}, (error, asset) => {
Store.stageInfo.find((item:checkpointItem) => item.sceneName == sceneName).isLoaded = true;
jump && cc.director.loadScene(sceneName);
});
}

具体效果如下:

5bb141c58b441ad8c94c7f63591f8038.gif

尝鲜3D场景

游戏本身是针对小朋友的,玩法比较简单,这个场景的主要玩法是通过维持手指的点击频率,让玩家角色顺利到达终点?

6012249ef3419520d13c0cfde8335030.gif

做这个项目的时候,引擎已经开始支持3D模式,和我合作的设计师也有在私底下学习C4D,所以我们一拍即合决定尝鲜一下。我是基于v2.2.0进行开发的,这个版本对3D的支持很有限,最新的v2.3.0版本出来后,对3D的支持更好了,所以下面的经验仅供参考

模型导入

我直接把设计导出的.fbx模型拖进引擎,大部分材质就直接应用到画面上来,部分材质的绑定可能会丢失,自己手动绑定就可以了。这块的实践过程我们主要遇到下面几个问题:

  • 模型无法正常导入。这块的排查原因就是模型里面用来中文命名,把模型的中文命名改掉就可以了。
  • 相机节点无法同步。设计师其实已经在C4D里面调好了相机的视角,导进引擎之后,相机节点不会自动绑定,所以后期需要我们自己重新调整。
  • 模型太大。同样的场景,3D的模型大小可能是图片的五六倍以上,为了压缩模型可以让设计师把一些不必要的网格合并了。

渲染效果

初次导入模型之后,我们发现渲染的效果差强人意,下图左边是我们实际的效果,右边是C4D内的渲染效果ba0bbb6b15c2d64e2a3643ab3f936610.png

上面这张对比图可以看出几个问题:

  • 人物以及场景的渲染不到位。这个确实是我们缺乏经验,模型导出前需要设计师先给模型烘焙贴图,后面再导出,前后对比效果就能够提升不少。
  • 环境光不到位。这个没办法了,暂时只能将就
  • 没有阴影。引擎其实已经是支持设置阴影了的,但是尝试之后我们认为有无阴影对效果影响不大,所以我为了节省模型大小,就让设计师把场景上所有的东西都合并了,就没有专门去设置阴影。

还有一个问题就是2.2.0版本还不支持天空盒,但是对我们的影响不大,因为调整了相机之后,用户只能看到正前方,所以我就用了一个纯色的面板放在前面代替。

模型操控

在3D模式下调整相机视角和位置的操作是相对来说比较困难的,之前的项目中也实现过一个2.5D的场景,所以也这里也分享两个快捷键,一个就是调整物体的时候,点击住屏幕,在视图左上角会出现wasd(上下左右)的提示,可以通过键盘上wasd这四个按键来微调方向,相对来说会便捷很多

ba9ecb8462fa0691d124da11ea6c3f52.gif

还有一个就是调整相机的角度,如果我们根据预览效果去调整相机的位置,实际操作起来有一定的麻烦,有一个隐藏的组合快捷键control+shift+f可以把我们场景编辑器的画面同步成相机的预览,它可以让我们更直观得调整相机效果

0f925fd51acacd527069a082ba9cc8f4.gif

玩法实现

核心玩法就是在点击回调中动态改变玩家的位置,这里面还涉及到两个问题,多个相机的应用以及相机跟随。首先在布局的时候,Canvas节点和Main camera主要是用来显示2D物体,所以我把2D的物体都放Canvas节点下。而3D模型我会新建一个独立节点来存放,然后新建一个相机来专门拍摄3D物体。这样子我们就有两个相机了,这里需要理解一下关于相机的几个知识点:

  • 游戏引擎输出画面的时候会把多个的摄像机的图像叠加起来;
  • 摄像机会绘制属于自己渲染分组里面的物体,所以我们需要合理分组给每一个摄像机选取拍摄具体要拍摄的物体
  • 根据摄像机的成像原理,有两种模式的摄像机。在3D模式下,我们需要那种近大远小的效果,这种锥形的成像模式指的就是透视投影模式;而正交投影则适用于2D模式。
  • 摄像机有一个depth属性, depth小的先绘制到屏幕, depth大的后绘制到屏幕。2D的UI界面在游戏的最上层,所以我们要将拍摄UI摄像机的 depth调到所有相机的最高处;
  • 摄像机有—个clearFlag,如果你设置了,当他绘制画面的时候就会清理屏幕。因此我们只给第一个摄像机也就是拍摄2D的摄像机设置 clearFlag;后面拍摄3D模型的相机就不再设置了,不然会把前面摄像机绘制的内容清除掉。

然后玩家的视角跟随,我是直接把拍摄它的相机节点放到玩家节点内,这样改变玩家位置的时候,相机也就会跟着移动了

2D滑行游戏

这个游戏里面有一个场景的玩法是滑行游戏,玩家通过左右按钮控制人物的方向,在滑行的过程中需要绕过特定的障碍物,然后到达终点就算游戏结束。如下图所示:

111d18df69b9930efed2a5fd941306c8.gif

选择参照物

滑行其实有两种是实现方式,主要看是应该选谁为参照物

  • 相机不动,画布移动(相对以屏幕为参照物)
  • 画布不动,相机移动(相对以画布为参照物)

理论上两种方式都只是参照物不同而已,最终实现的效果应该是一样的,并没有谁优谁劣,看需求而定。但是如果遇到要使用物理引擎,选择的时候需要考虑刚体的位置同步。比如父子两个元素:子为刚体,父不是,希望父移动的时候子也跟着移动,但是引擎的实现效果是父移动的时候子不动,原因如下:

  • 物理组件类似一个独立的容器,它的位置只和物理世界里面它的位置有关与非物理世界的位置变化无关。因此父动的时候,因为父不是物理组件,它在物理世界中的位置是不变的,虽然画面上父移动了,但是子是物理组件,子不会跟着动
  • 硬要动,可以让子记录一个初始位置,然后在update的时候,重设node节点的位置(覆盖其初始值),同时手动调用syncPosition来更新它在物理世界的位置。(之前实现的时候效果并不理想,但是引擎重新编译后暂时无法复现)
    onLoad () {
this._pos = this.node.position;
},

update (dt) {
this.node.position = this._pos;
this.getComponent(cc.RigidBody).syncPosition(true);
}

在起初调研阶段其实我有用到物理引擎,但是后来仔细一想,我的这个游戏场景其实只要使用到碰撞组件就可以了,所以就没有引入物理引擎,最终选择了相机不动,移动画布的实现。

背景无限滚动

同时因为赛道的距离可能会很长,所以背景的画布节点应该还是通过程序去动态拼接比较好,具体的实现方式其实就是很经典的轮播图。

38c1027187fa2f846cebf2ac03207842.png

这里有两个适配注意点,一个是背景图的高度不能是768,因为每次距离叠加之后还是会产生误差,如果刚好背景图的高度就是屏幕的高度的话,这个小误差就会产生很明显的空白间隔线,所以最好就是768是内容区,然后上下得各留10px空白像素区,这样子的话挪动背景图片的时候就不会有漏出的情况了。还有一种情况因为我的背景节点有赛道,所以我的背景节点是不能做拉伸的,因为拉伸之后就有真实检测的位置和视图不一致的情况。那就只能有一张固定大小的背景图,但是当用户屏幕宽度很长的时候,左右就会产生黑边,因此我们做了一个处理,就是底层节点做成widget拉伸的纯色的节点,然后赛道图是固定大小的图片,赛道图片的两边颜色刚好可以和纯色接壤,这样子就算用户的屏幕很宽也不会有黑白了。

检测绕过障碍物

因为游戏的玩法里面要求统计玩家绕过障碍物的次数,所以我需要精准地检测玩家是否绕过了该障碍物,前期定了两个方案,一个是划分圆形的碰撞区域,然后统计玩家经过了哪些碰撞区域。第二个方案就是把圆形的碰撞区域简化成三条射线。

6f3f98e9784510354711b65c28a2bf8f.png

目前选用技术方案二,因为射线是物理系统的一个子功能,如果引入物理系统的话会和上面的滚动方案有冲突;而且射线没有debug视图,调试起来也会比较麻烦;后面物体移动之后还会涉及到物理世界坐标和节点位置坐标的转换。所以在项目中我就使用矩形碰撞框来模拟碰撞射线,具体效果如下

42729dba9f308d7f75b774c541ede974.gif

其他备忘

在需求的实现过程中,我还遇到了一些比较典型的现象,也记录一下解决方案

Safari全屏模式断网后,卡死

在Safari全屏模式下,如果自动判断断网刷新或者自动跳转链接的话,是会卡死的,全屏模式无法退出,同时因为断网了,也无法刷新和跳转新网页,解决方法就是判断断网后,先手动退出全屏,然后再刷新网页和跳转链接

if (navigator.onLine === false) {
cc.screen.exitFullScreen();
window.location.reload();
}

Animation恢复到动画的初始状态

设置动画到第一帧的时间,再停止

this.longPressBtn.getComponent(cc.Animation).setCurrentTime(0);
this.longPressBtn.getComponent(cc.Animation).stop();

字体模糊

本质原因是因为字体的底层也是texture,它的渲染模式和画面上其他的图片的渲染模式不一致,所以在混合渲染的时候位图字体就会产生模糊现象,目前在论坛上看很多人都有反映情况但是并没有根本的解决方法

  • 如果是label 可以通过添加labelOutline 组件设置边框为0.3 来欺骗视觉
  • (推荐)无论是label 还是richText 都可以通过设置大字体,然后设置scale来达到视觉效果
    • 如果本身字号大的话可能缩放之后字体边缘会有部分被裁。
    • 如果设置了label 模式为固定宽度缩放模式的话也会有问题,因为字体变大,引擎自身会先做缩放,这个时候再叠加缩放就会更小了,因此这个时候不建议
  • 社区内也有人进行过引擎层面的尝试

打包前正常,打包后不正常

去看看项目的打包模块设置,看看有没有把对应的模块打包进去

该系列其他文章

  • 游戏引擎开发入门实践
  • 使用cocos进行2D和3D混合开发

原文链接: https://juejin.im/post/5e75f4cbe51d4526d87c949f

一个游戏多份收益,助力开发者技术精进副业挣钱,我是工程师经人:张晓衡,欢迎与我建立链接!

29bb558068c2b8de02d380cb51605bad.png

f539ff174f22a767ce47a62b826e225f.png
  • 大龄开发者,我是如何活下来的,又将怎样活下去

  • 极限开发,一个小程序打通任督二脉,加速助力开发者副业变现

  • 实战微信小游戏【球球要回家】视频开讲了!

  • 这位程序猿做了全世界程序猿都想做的事情!

  • 微信引擎插件会对小游戏带来怎样的利好?

  • CreatorPrimer 30篇教程汇总

  • Creator星球教程文章分类导航

  • 盘点Creator星球上的几大开源工具包

7d5b5c83a63fab86d8a9dc788498618d.png
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值