最近火了的羊了个羊游戏,连续好几天了,热度仍然不减,现在你还在玩吗?羊了个羊游戏属于是一款益智游戏闯关类的,新颖的挑战消除游戏,一旦玩下来就停不了。
羊了个羊?
游戏简介:
微信小程序羊了个羊属于是免费下载的软件,玩法简单,第二关比较多难,很难通关。玩游戏的好玩地方就是在于其他地域的玩家也能加入组队打榜,将自己的身份分数提升到至高点,即可领取团体的胜利。
不得不说,这游戏真上(xia)头。刚打开的时候,我心里想的是:“就这?”,然后进入第二关,一开始还没觉得什么,直到后面,我感觉到了不对劲。大概刷了七八次,我就放弃了。然后过了一个小时...不行,我今天一定要把它通了!大概又刷了七八次,这什么破游戏!坑爹呢!
作为一款闯关消除类小游戏,通关率却不到0.1%,属实说不过去,传言该游戏在一天内服务器崩溃三次,事后,官方直接微博招聘开发,有人就借此调侃道:随便做做,谁知道能火。红红火火,恍恍惚惚...
先说说玩法
第一眼看到《羊了个羊》,我首先想到当年的《连连看》,不过有网友爆料,该游戏“借鉴”了《3tiles》。瞄了眼《3tiles》,是比较相似。说心里话,这个游戏的玩法并没有什么过于出众的地方,算是个中规中矩的“低卡路里”休闲游戏。
之所以成为话题作品,主要就是因为它的第2关极其低的通关率,一下子激起了众多玩家的挑战欲望。而时至今日这个“低通关率”也被网络上的众多玩家揭秘,第2关其实大概率上本身就是个死局。是程序员故意挖坑设了死局么?先卖个关子,我们先聊聊游戏的开发,然后您自己就会有答案了。
说实话,这个游戏,一开始我是看不上的,我认为这个游戏的真正厉害的地方,在于方块堆叠的设计,看似没有多少层,但是扒开一层,却发现还有下一层,当你觉得你越来越接近最后一层的时候,实则还在差的老远了;另外,看似随机打乱的方块,实则也需要精打细算每一步。我不知道真正通关这个小游戏的人有多少,但至少,我是没那个耐心的,大家尽可放心,不能通关这个游戏,只能证明这个方块的随机性和摆放的设计非常巧妙,并不能证明智商高低,至少从程序的角度来看,无非就是随机打乱而已。
实现概要
游戏的整体很简单,但其中有几个实现的重点需要注意:
牌堆数据结构的实现
如何检测和更新可拾取的牌
先做个小定义,一个牌堆中可被拾取的牌以下将简称其为:“窗口牌”。
牌堆的结构及其数据结构
最初,我还真被这复杂的牌堆结构蒙住了,但仔细研究一番发现,无论多么复杂的牌堆,其实都是由如下三种牌堆模式组合拼凑而成的。
蓝圈圈出的牌堆模式A:上面1张牌只挡住下面1张牌;同时下面的牌仅被上面1张牌挡住。只要上面的1张牌被取走,下面的牌就成为窗口牌;
蓝圈圈出的牌堆模式A:上面1张牌只挡住下面1张牌;同时下面的牌仅被上面1张牌挡住。只要上面的1张牌被取走,下面的牌就成为窗口牌;
红圈圈出的牌堆模式C:上面1张牌可以挡住下面4张牌;同时下面的牌可能被上面4张牌挡住,一张牌只有它上面的4张牌都被取走,它自己才成为窗口牌。
虽然上图中体现不是很明显,但不难猜想出,第三种牌堆模式B 的存在,那就是:
上面1张牌可以挡住下面2张牌;同时下面的牌可能被上面2张牌挡住,一张牌只有它上面的2张牌都被取走,它自己才成为窗口牌。
对于牌堆模式A,有些朋友会迫不及待地用“队列”或“栈”实现它,这样做有两个缺点:
逻辑上牌堆模式A的窗口牌也可能是2维的,如果用队列实现就限制了它的灵活性;
牌堆模式B和C都不好用队列实现,所以想追求数据结构的统一,还要另求他法。
实际上无论牌堆模式A、B还是C,都不过是3维数组结构,上图中模式A看起来特殊,无非是它的x,y维度都为1罢了。而三种牌堆的区别也无非就是当一张窗口牌被取走,检查牌堆是否出现新的窗口牌的方法罢了。
牌堆模式A
牌堆模式B
牌堆模式C
牌堆的数据结构
我将其定义为MContainerBase基类
#MContainerBaseextendsNode2Dclass_nameMContainerBasefunc_ready():add_to_group(name)add_to_group("game")varMask = FileReader.read(mask_file,null)box.resize(size_x)fori in range(size_x):box[i]=[]box[i].resize(size_y)forj in range(size_y):box[i][j]=[]box[i][j].resize(size_z)fork in range(size_z):ifMask == null or Mask[i][j] == 1:box[i][j][k]=add_tile(i,j,k,get_parent().distribute_face())else:box[i][j][k]=nullforx in range(size_x):fory in range(size_y):forz in range(size_z):check_is_on_top(x,y,z)
最基础的牌堆就是一个 xyz的三维数组,我们可以使用一切方法构造想要的排队形状:柱形、条形、甚至金字塔形。这都不会影响后面程序的实现。
项目中为了增加这个“大方块”的多样性,我还给它设置了如下的“遮罩”,这就是游戏中CSDN文字的由来。当然我们还可以通过“遮罩”来自由定义窗口牌,这部分就请大家自由发挥了。
如何检测和更新可拾取的牌
三种牌堆模式分别派生自MContainerBase,并对应着如下三种检测方式:
牌堆模式A
仅检测自己正上方是否有牌
#1Cover1extendsMContainerBasefunccheck_is_on_top(x,y,z):ifhas_tile(x,y,z):ifnothas_tile(x,y,z +1) : (box[x][y][z] as MTile).set_is_on_top(true)
牌堆模式B
检测自己上方两方位是否有牌
#1 Cover 2extendsMContainerBasefunccheck_is_on_top(x,y,z):ifhas_tile(x,y,z):ifz%2==0:ifnothas_tile(x,y,z+1)andnothas_tile(x-1,y,z+1):(box[x][y][z]asMTile).set_is_on_top(true)else:ifnothas_tile(x,y,z+1)andnothas_tile(x+1,y,z+1):(box[x][y][z]asMTile).set_is_on_top(true)
牌堆模式C
检测自己上方四方位是否有牌
#1 Cover 4extendsMContainerBasefunccheck_is_on_top(x,y,z):ifhas_tile(x,y,z):ifz%2==0:ifnothas_tile(x,y,z+1)andnothas_tile(x-1,y,z+1)andnothas_tile(x,y-1,z+1)andnothas_tile(x-1,y-1,z+1):(box[x][y][z]asMTile).set_is_on_top(true)else:ifnothas_tile(x,y,z+1)andnothas_tile(x+1,y,z+1)andnothas_tile(x,y+1,z+1)andnothas_tile(x+1,y+1,z+1):(box[x][y][z]asMTile).set_is_on_top(true)
在Godot中,这三种牌堆模式还可以通过场景节点制作成预制体,这样关卡设计师就可以轻松地制作出美观的关卡了。
关于游戏中的坑
很多朋友抱怨:“程序员故意挖坑制作死关卡”。其实不然,他无须故意挖坑,因为这个游戏本身就有很多“天然的坑”,如果不使劲填坑,它们自然而然就属于你了。而这里就隐藏了几个可致命的坑:乍一看,待发牌池中所有的图案都可以被3整除那么一定可以通关?那可不一定:
只有桌面牌堆中牌的数量和待发牌池牌数一致,所有的牌才能“落地”,而游戏中桌面牌堆到底有多少(层)本身就是个迷。并且如果没猜错的话,在每一局设计者先要确保牌堆形状好看,然后再使堆牌数和待发池的牌数一致。二者哪怕差1个,也会造成死局。
上文说了,桌面牌数和待发牌池的牌数一致只是过关的必要而非充分条件。即使该条件满足,如果相对于牌桌上的牌数以及图案数量,窗口牌数太少,也会造成死局。比如下面这个极端的例子:假设游戏共有 15种花色,而牌桌上只有这个模式A牌堆,它有90张牌。那么玩家只要在连续7次拾牌时没有遇到3个相同图案的牌,就“必死无疑”了。
其实这个游戏,一方面要控制关卡的难度,另一方面又要保证能通关本身就是一个相当困难的问题。而设计者反其道而行之,(可能)没有花力气去设计算法,把坑留给玩家,得到了极低的通关率,反而制造了话题并形成爆款。如此说来,这确实是个抖机灵的“设计”。但老王认为这种“设计”在游戏策划中是不宜被借鉴的,就像现在市面上泛滥的悬疑剧,开始埋坑无数,吊足观众胃口,最后烂尾不了了之一样,长此以往观众(玩家)对于悬疑剧(游戏)的信任感就被消费殆尽了。
玩笑归玩笑,娱乐归于娱乐。看见别的bug,我们总是很开心的,但是我们也要时刻通过这些bug反思我们自己。如果是我,我要如何防范这些破解和刷分? 从羊了个羊这个游戏来看,对这个游戏的破解无非就分为两大类:本地修改客户端,和伪造消息给服务器
本地修改客户端
就算是说破了天,单机游戏想要破解都只是时间早晚的问题。别说微信小游戏了,主机游戏甚至游戏主机,都难逃命运,不是破不了,只是时间成本的问题。但我认为,我们还是可以通过以下方式来做安全防护:
1.资源文件加密处理
这也是我用到的第一个修改方式——修改关卡配置,但是如果配置文件本身做加密处理,或者打包成二进制文件等,机器认识,人不认识,那么也就很难做修改了。
2.内存防护
我之前项目有用到过腾讯提供的客户端加壳保护,一旦发现你通过内存修改器修改内存等违规操作,保护程序会立即杀死程序来做自我保护。以前我玩腾讯PC游戏的TP子程序,大致也是这个作用。
3.服务器校验
一般来说,就算是单机游戏,我们最好也需要有服务器来进行校验。 往简单了说,我们至少可以通过分析玩家的行为,比如通关时间过短,通过次数过多,或者点击次数过快等分析出玩家的异常行为。 往复杂了说,我们把单机改成服务器运行逻辑,或者单机和服务器分别运行逻辑,服务器来做数据校验。 总之,服务器参与的越多,本地修改数据的可行性越低
伪造消息包
这种方式需要抓包客户端和服务器的交互数据。一旦玩家发现了交互逻辑,则可以通过伪造相同的消息包来达到相同的效果。而这种方式,我们需要在服务端做好各种非法请求的校验。 从通用点来说,我们可以通过接口请求频率,ip访问限制,参数篡改校验等拦截一批非法请求。 从逻辑点来说,我们依然可以通过玩家的异常行为,来拦截玩家非法请求。
一键通关
一键通关的接口随意发送通关消息,就能无限通关,它的保护做的是远远不够的。从游戏逻辑上来说,羊了个羊每天是只能通关一次的,至少从这一点来说,如果对每天通关次数有拦截的话,就算是伪造消息,也只能一天通关一次;其次,重复伪造消息就需要重发消息包,只要对消息包加上时间戳或者事物id,保证同一个包只能请求一次,这也能拦截一大批重发的伪造消息包。
修改任意玩家地理位置
另外需要重点说的是,能通过接口请求修改其他人的定位地址,绝对是属于很严重的漏洞了。它的原理是直接请求修改地理位置的接口来达到修改玩家定位的效果,但是致命的是,它只需要传玩家的uid,就可以修改对应玩家的定位,但偏偏游戏大厅能请求到所有玩家的uid。这样的话,修改任意玩家的定位就显得十分容易。在这个接口中,最基础的,我们不能泄露玩家的关键信息在公共接口中,必须保证请求这个接口的一定是玩家本人;其次,修改地理位置这个操作理应也做一些限制,比如必须一天或一周或一个月才能修改一次。
既然你来了,今天我就绝不会让你空手而归,教你如何一分钟快速通关!教程操作非常傻瓜式,丝毫不费吹灰之力!感兴趣的朋友可以看,包教包会。功能完整,代码结构清晰。值得推荐。架构师必须具备的一些技术总结出来一套思维导图和录制了一些相关视频,分享给大家,供大家参考。感兴趣的铁子们可以后台私信【架构】免费获取高清知识体系思维导图及相关资料。
二、教程演示
演示环境:Windows10
如下是具体的破解教程,请把每一步都走踏实咯。
1️⃣首先打开Windows电脑端,登录微信。并打开微信设置:
2️⃣找到文件管理,点击打开文件夹。
3️⃣打开后找到名为【Applet】文件夹,将【Applet】文件夹整个删掉。
4️⃣接下来我们打开【羊了个羊】小程序,点击打开小程序。
5️⃣找到小程序并点击打开。
6️⃣进去玩一局,快速通过第一关,然后第二关,不要玩,直接点击右上角的圆圈,直接关掉小程序。
7️⃣接下来我们回到我们之前打开的【文件管理】文件夹,会发现之前被删除的【Applet】文件夹又出现了,我们直接点进去,按此路径目录进去[Applet]->[wx141bfb9b73c970a9] ->[usr] ->[gamecaches] ->[resources] ,接着在【resources】目录下,我们找到大小为【2kb】的文件【16635532267364.json】。
8️⃣选择记事本打开【16635532267364.json】文件。可以看到是一串json串。
9️⃣然后在公众号【猿圈奇妙屋】发送【羊了个羊】, 平台会发送给你脚本代码,你只需要将脚本代码完整复制保留即可。
1️⃣0️⃣然后将获取到的脚本代码直接黏贴替换掉【16635532267364.json】文件中原有的内容,然后保存,再关闭该文件。
1️⃣1️⃣接着我们再次从电脑端打开【羊了个羊】小程序,我们直接进入游戏,快速通过第一关。
接下来就是见证奇迹的时刻了,你会发现第二关也变成了第一关,再次快速通过来到第三关,同样也是第一关的样子,最后呢还有第四关,快速完成后呢,你就成功加入羊群啦。
如下是我测试后成功加入的截图,获取了一个通过羊皮肤。
1️⃣2️⃣查看一下你的名片,看看你的通关时长?还能有比我快的么?
希望大家能从文章中得到帮助获得收获,也可以评论出你想看哪方面的技术。文章会持续更新,希望能帮助到大家,哪怕是让你灵光一现。喜欢的朋友可以点点赞和关注,也可以分享出去让更多的人看见,一起努力一起进步!