用 3.6.2 开发一个物理画线游戏(支持ugc和分享)

1.0 背景介绍

物理画线游戏是比较经典的2D游戏玩法,从最早的物理画线到画线水杯,出来了一系列的爆款产品。

目前大部分画线都是用了box2d物理引擎,作为一款十多年前的2D物理引擎,3.x有关box2d的资料是比较少。

这篇文章将从立项到游戏逻辑,来讲解如何开发一款物理画线游戏,并支持UGC关卡创作,和微信关卡分享。

体验链接: learncocos.com/iwae

2.0 游戏立项

游戏的核心玩法是玩家需要保护自己的小鸡免受小黑鸡的伤害。

在画布世界中,突然出现了许多黑洞,有许多小黑鸡跨域而来,企图伤害我们的小鸡。

玩家需要用手指画线,保护自己的小鸡免受小黑鸡的攻击和画布中的其他危险。

考虑到制作成本,游戏美术画风将使用简单的手绘风格,用paletton选则比较靠近纸张的颜色,拖动色盘,把比较顺眼的方案保存起来。

确认了游戏整体的风格。

把色板中用到的颜色统一可以使用圆角矩形缩到最小(比如15px,就输出成15+15+2=32px,预留2个px给九宫格拉伸用),游戏所有的ui框架加起来就只有几kb,减小显存的同时,还可以提高加载速度。

考虑到spine 专业版数千元的价格,游戏内的动画效果统一使用了Cocos内置的动画编辑器+序列帧动画。序列帧动画使用PS制作,在画好我们的角色后,PS内复制数个分组,调整角色的五官,输出序列桢,一些相似了,做了剔除,方便减少动画的体积占用。

在Cocos内制作序列帧动画比较简单,桢动画使用30桢即可,新建好动画,并在精灵上面创建好动画组件,在对应的关键桢替换图片即可。

制作好的序列帧动画无需进行合并,拖入到一个文件夹内,使用Cocos自带的自动图集打包即可。

3.0自定义网格背景

考虑到游戏的背景是一个纸上世界,同时需要支持UGC和简单的AI,我们需要一个图片背景+网格,然而使用图片背景+网格图片不够灵活,同时做出来的效果会比较死板,缺少随机性, 就从shadertoy移植了一个纸张网格的效果。

Error - Shadertoy BETA

原shader的for循环比较多,且风格比较写实,这里进行了一定的简化。


这里把shader中常用的属性都进行了暴露,把整体风格调整到和色卡一致。


同时游戏内存在3个场景,分别是游戏界面,游戏内,游戏编辑,都是用到了这个shader的图片作为背景,这里使用精灵图自带的颜色 a_color 来控制网格颜色。

并使用了简单的脚本接受全局事件,可以修改网格的颜色(这里只修改了透明度)。

效果如下:

考虑到游戏的UGC地图编辑需要使用到格子,Astar网格导航也需要,我们以图片的视觉方向为例,在shader里uv的xy方向是左图,astar算法的格子序号从小到大是右图

所以我们需要在shader里对格子进行了转换。

同时考虑到算法简单,这里不做屏幕分辨率的适配了,默认的设计分辨率是750x1334,支持的最大显示高度是750x2.2倍= 1650(绝大多数全面屏手机的最大高度)

uv统一后,我们需要设置下网格显示,默认黄色是安全区域坐标,表示可以放置物体或者有物体,红色是危险区域,表示不可以放置物体。

我们定义后颜色和颜色的顺序,在shader中使用简单的aabb四个方向进行判断,在范围内的就会把颜色改成黄色安全色或者红色的警告色。

同时shader 把worley 褶皱效果也加进材质的宏里,方便自定义开关

如果需要使用自己的图片作为背景,可以打开Use_Texture的宏,使用750x1650的背景拖即可~

有了上述的准备工作,我们就可以开始地图编辑器的制作了。

4地图编辑

在地图编辑之前,我们先确定整体的网格大小为75像素,设计的最大分辨率是750x1650,也就是10x22个格子,这里定义一个全局的class去记录下这些配置。

每个物品的格子数量,可以用物体的图片长和宽分别除以75,然后四舍五入。

(如尖刺,是60 x 148 分辨率,正好是1*2个格子)。

这部分数据我们在初始化物体时候会提前处理,避免重复计算。

接下来根据格子的奇数或者偶数决定落点的位置,可以分为x和y两周轴向分开处理。

如果是偶数就不需要做偏移,在格子中间落点即可,如果是奇数,需要偏移 1个格子单位,同时偶数的格子用Math.floor求最小的整数格子,奇数用Math.round 四舍五入求最近的格子

检测上使用了map做查询,当检测到数据时候我们要把有碰撞的格子范围乘0.1,这样格子的x范围从0到10,变成了0到1,y范围从0到22,变成了0到2.2,正好和我们shader的uv对应(shader内部对原始的uv。y也乘了2.2,并不是0到1了)。

当我们检测到碰撞体时候,当前物体的周围就会变红,存在碰撞体的周围位置会表黄。

游戏内定义了几种物体的名字的枚举,当他们的名字一致时候,初始化时候就会标记为是画线工具,删除工具或者旋转工具,当使用这些工具检测到结果时候,就会对选中物体标亮并进行对应操作。

当保存关卡时候,我们只需要记录物件的格子开始和结束范围,也就是之前map保存的数据,同时记录物件的scale。x和scale.y 记录左右和上下方向的旋转。保存为json数据,再把json数据Stringify 即可。

在地图编辑器和游戏内读取关卡都采用相同的逻辑,如果是runtime时候,我们会顺便初始化一下Astar Graph里的网格权重。

这样在复杂的关卡中,小黑鸡也可以精准的定位的阿鸡

4画线算法

下面介绍下游戏的核心逻辑部分。

游戏的核心逻辑是使用Graphics 画线,并把画线的路径点记录下来,同时我们地图编辑器也会使用到这个功能来画辅助线。

这里使用了曼哈顿距离记录下移动的具体,当距离大于设定值,我们就会存储一次路径点,同时使用graphics绘制一次路径。

这里游戏环境和编辑器环境使用了不同的长度设置,编辑器会更短,来保证储存的关卡长度足够小。

画线第一个点时候我们使用了开销最低的testPoint,记录是否存在碰撞体,第二个点开始,从上个点到当前点发射一条射线进行检测。

需要注意的是,这里的坐标点需要使用物体的世界坐标。

当画线结束时候,我就可以根据路径点去生成碰撞体,这里直接使用了polygon多边形生成碰撞体,已经生成的碰撞体在关卡开始的时候,会回收使用到的vec2 类,减少GC。

我们首先把每2个点连成一条线,再对比每条线之间方向向量的斜率。。,这里考虑到性能没有使用三角函数,斜率在一定范围内,就判断为是平行的,只会去推第二条线的最后一个点。

这样我们参与计算的路径点就可以减少30%-40%,我们用优化过的路径点在来算需要生成的polygon多边形,这里把起始点定为1,结束点定为2,其他中间点是0.

先计算出2个点之前的方向向量。

再计算方向向量上,2个垂直方向的向量,分别乘以我们线段一半的宽度,最后起始点和结束点分辨加上这2个向量,就可以得到3个或者4个路径点,可以构建出一个三角形或者平行四边形(考虑到其实点和结束点可能贴边,这里使用了三角形,避免碰撞冲突的修正)

生成好多边形的路径点,需要我们额外的apply() 一下,我们所有的vec2 都从VecPool单例内存储,减少GC。

这里额外支持了Graphic的自定义材质,玩家可以在游戏内更换画笔的颜色

我们把材质的颜色和材质名在入口脚本里进行配置,商店和gameplay可以根据配置进行加载即可。

感谢社区大佬的Shader~


5.状态机

画好线后,就可以通知小黑鸡出发去攻击我们的小鸡了。

这里使用了Cocos原本的update,当切换状态时候会先执行 onEnter 方法,再执行 onUpdate方法,这里可以把当前状态和之前状态传入,方便做逻辑切换,这里会记录下dt和duration 时长,方便继承状态机的类使用。

小黑子的AI继承了FSM状态机,整个状态比较简单,只有寻路和攻击两个状态。

寻路阶段使用了Astar算法,每找到一个路径点,就会向下一个路径点前进,寻路时候会使用人物的方向向量乘以一定距离,来检测是否存在物理画线。

当检测到物理画线的时候就会对物理画线的rigidbody2d进行攻击,对rigidbody2d施加速度向量,就可以出现线条被抬起来的行为了。

我们会在x方向上做左右的随机,y方向以向上围住,也会随机一定的数值。

小鸡的状态机就相对比较简单,只有基本的碰撞检测,当检测到危险的碰撞体就会触发受伤然后游戏失败。

6.寻路导航

这里使用到了和之前EasyNavmesh 同款的A*导航算法,不同的时我们使用了一个单例进行管理。

这里把Astar 翻译成了TS版本,方法都加了强类型判断,同时把距离算法改成了曼哈顿距离。

游戏内FSM同样使用了曼哈顿距离,当我们的路径点走完,且离目标超过2个格子距离时候就会再次寻路一次。

到这里整个游戏大的逻辑就分解完了,下面介绍一下游戏关卡分享的逻辑。

7. 游戏内拍照

游戏内把物体和UI分辨分成了Default和UI_2D分组,并创建了2个相机,Screencam平时的可见分组为空,只有当拍照时候才能看到Default分组。

截图后一瞬间,我们会读取相机RenderTexture的像素点,并把角色的偏移量传进来,保证截图范围不会太大,同时保证相机对着角色不会出框。

8. 关卡分享逻辑

关卡分享并没有使用云服务器,这里使用了微信分享里的query参数,query最大可以存储2048长度的string(测试中4096也可以跑,担心部分手机可能存在兼容问题,这里最大设置成了2046)

我们在分享时候可以把当前关卡都转换成string,放到query里。

当我们分享给其他玩家,其他玩家打开后,在游戏初始化和微信onshow时候就会检测到是否有对应的query。

微信的getLaunchOption是第一次进入游戏时候会有的,wx.onShow 是后台切换回来触发的时间,当检测到query且query内的关卡数据有效,就可以初始化这个关卡了

游戏内显示好友UGC关卡

好友打开关卡效果

25赞

又一良心力作 :star_struck:

赞赞赞!

你干嘛 哎哟

小黑子 :smile: 找到了我的内测版

凭一己之力拉高了社区的水平

多招几个这样人才,要变天了

haha 果然社区大佬很多~,看了下,这个实现思路和 2.x 这个不太一样,3.x 没有使用b2,setasbox 是自己构建一个正方形传入角度~,这里使用的是多边形+自己的数学方法,希望3.x 官方直接支持 wasm的box2d

商店地址和使用说明,目前上线的是完整的商业版,接好了SDK,后续会上了基础版供代码参考(没有SDK和微信授权~)
https://store.cocos.com/app/detail/4240

上周六,提交的微信小游戏审核,提示有恶意注册的嫌疑,是因为没给原游戏的图片素材的原因吗,

违规图片如下,有点看不懂

大佬牛逼!!只因大佬太美!oh!baby

不换皮就上?

:sweat_smile:原来还需要换皮呀,多想解惑

在此扫码,1折购买

ios划线画的长一点会导致爆卡,怎么解决呀

大佬太强了 太强了

能否增加一个排行榜

刚付款,给授权一下wxec9a03759c06f56c

:kissing_heart: