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移植了一个纸张网格的效果。
原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关卡
好友打开关卡效果