范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

程序员用12小时复刻羊了个羊,代码已开源

  编者按:前段时间,超休闲三消游戏《羊了个羊》爆火,不少网友表示游戏第二关难度过高,甚至猜测「根本没有通关的解法」。而本文作者 @开发游戏的老王 在尝试复刻游戏之后,提出了一个猜想:过高的难度,也许源于代码层面的瑕疵。
  以下为正文:
  昨天有朋友和我说:"最近有个叫《羊了个羊》的游戏爆火,就是太难玩了,你能复刻一个不?"
  话说上次玩休闲游戏还是在几年前,但是朋友之托必须赴汤蹈火啊,二话不说,开整!然而,冲动是魔鬼,直到此时此刻,老王也没能亲手玩一局原版游戏,不知道是游戏入口设计得太隐蔽还是网络加载太慢,无论手机端还是PC端,游戏都停留在如下界面。
  所以本次游戏的复刻,完全是基于各视频网站云观摩的结果,好在游戏的玩法不是特别难理解。复刻使用的开发工具是Godot Engine(使用其它工具开发原理也是相似的),目前项目已经开源到了GitCode:Godot版《羊了个羊》:
  https://gitcode.net/hello_tute/SheepASheep
  接下来我将通过临摹游戏的方式推测一下这个小游戏的实现原理,本文主要面向对游戏开发有兴趣的朋友,欢迎大家多提宝贵意见。
  01 玩法 第一眼看到《羊了个羊》,老王首先想到当年的《连连看》,不过有网友爆料,该游戏"借鉴"了《3tiles》。瞄了眼《3tiles》,是比较相似。说心里话,这个游戏的玩法并没有什么过于出众的地方,算是个中规中矩的"低卡路里"休闲游戏。
  之所以成为话题作品,主要就是因为它的第2关极其低的通关率,一下子激起了众多玩家的挑战欲望。
  而时至今日这个"低通关率"也被网络上的众多玩家揭秘,第2关其实大概率上本身就是个死局。是程序员故意挖坑设了死局么?先卖个关子,我们先聊聊游戏的开发,然后您自己就会有答案了。
  02 实现概要 游戏的整体很简单,但其中有几个实现的重点需要注意:
  牌堆数据结构的实现
  如何检测和更新可拾取的牌
  先做个小定义,一个牌堆中可被拾取的牌以下将简称其为:"窗口牌"。
  01 牌堆的结构
  最初,我还真被这复杂的牌堆结构蒙住了,但仔细研究一番发现,无论多么复杂的牌堆,其实都是由如下三种牌堆模式组合拼凑而成的:
  蓝圈圈出的牌堆模式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
  02 牌堆的数据结构
  我将其定义为MContainerBase基类 #MContainerBase extends Node2D class_name MContainerBase  func _ready:         add_to_group(name)         add_to_group("game")         var Mask = FileReader.read(mask_file,null)         box.resize(size_x)         for i in range(size_x):                 box[i] =                  box[i].resize(size_y)                 for j in range(size_y):                         box[i][j] =                          box[i][j].resize(size_z)                         for k in range(size_z):                                 if Mask == null or Mask[i][j] == 1:                                         box[i][j][k] = add_tile(i,j,k,get_parent.distribute_face)                                 else:                                         box[i][j][k] = null          for x in range(size_x):                 for y in range(size_y):                         for z in range(size_z):                                 check_is_on_top(x,y,z)
  最基础的牌堆就是一个 x*y*z的三维数组,我们可以使用一切方法构造想要的排队形状:柱形、条形、甚至金字塔形。这都不会影响后面程序的实现。
  项目中为了增加这个"大方块"的多样性,我还给它设置了如下的"遮罩",这就是游戏中CSDN文字的由来。当然我们还可以通过"遮罩"来自由定义窗口牌,这部分就请大家自由发挥了。 # S形遮罩 [         [0,0,0,0,0],         [0,0,0,0,0],         [1,1,1,0,1],         [1,0,1,0,1],         [1,0,1,1,1], ]
  03 如何检测和更新可拾取的牌
  三种牌堆模式分别派生自MContainerBase,并对应着如下三种检测方式:
  牌堆模式A:仅检测自己正上方是否有牌 #1 Cover 1 extends MContainerBase  func check_is_on_top(x,y,z):         if has_tile(x,y,z):                 if not has_tile(x,y,z + 1) :                                 (box[x][y][z] as MTile).set_is_on_top(true)
  牌堆模式B:检测自己上方两方位是否有牌 #1 Cover 2 extends MContainerBase  func check_is_on_top(x,y,z):         if has_tile(x,y,z):                 if z%2 == 0:                         if not has_tile(x,y,z + 1) and not has_tile(x - 1 ,y,z + 1):                                 (box[x][y][z] as MTile).set_is_on_top(true)                 else:                         if not has_tile(x,y,z + 1) and not has_tile(x + 1 ,y,z + 1):                                 (box[x][y][z] as MTile).set_is_on_top(true)
  牌堆模式C:检测自己上方四方位是否有牌 #1 Cover 4 extends MContainerBase  func check_is_on_top(x,y,z):         if has_tile(x,y,z):                 if z%2 == 0:
  if not has_tile(x,y,z + 1) and not has_tile(x - 1 ,y,z + 1)
  and not has_tile(x,y - 1 ,z + 1) and not has_tile(x - 1,y - 1,z + 1):                                (box[x][y][z] as MTile).set_is_on_top(true)                 else:
  if not has_tile(x,y,z + 1) and not has_tile(x + 1 ,y,z + 1)
  and not has_tile(x,y + 1 ,z + 1) and not has_tile(x + 1,y + 1,z + 1):                                (box[x][y][z] as MTile).set_is_on_top(true)
  在Godot中,这三种牌堆模式还可以通过场景节点制作成预制体,这样关卡设计师就可以轻松地制作出美观的关卡了。
  03 如何生成新关卡
  简单了解游戏规则后,我们就不难推导出,每个关卡能被通过的一个必要条件就是每一种图案的总数,必须能被3整除。实现方法如下: var tiles =   export var initial_tiles = {  0:10,  1:10,  2:10,  3:10,  4:10,  5:10,  6:10,  7:10,  8:10,  9:10,  10:10,  11:10,  12:10,  13:10,  14:10,  15:10 } func _init:  for key in initial_tiles:  var num = initial_tiles[key]*3  for i in range(0,num):  tiles.append(key)  tiles.shuffle
  其中字典initial_tiles 的key对应着每一种图案,后面的value对应着这一关该图案出现的"对数"(此处1对等于3个)。按照value乘以3的数量存入数组tiles(下文称之为:待发牌池),然后把待发牌池中的元素打乱顺序,等待"发牌"。
  01 关于游戏中的坑
  很多朋友抱怨:"程序员故意挖坑制作死关卡"。其实不然,他无须故意挖坑,因为这个游戏本身就有很多"天然的坑",如果不使劲填坑,它们自然而然就属于你了。而这里就隐藏了几个可致命的坑:乍一看,待发牌池中所有的图案都可以被3整除那么一定可以通关?那可不一定:
  只有桌面牌堆中牌的数量和待发牌池牌数一致,所有的牌才能"落地",而游戏中桌面牌堆到底有多少(层)本身就是个迷。并且如果没猜错的话,在每一局设计者先要确保牌堆形状好看,然后再使堆牌数和待发池的牌数一致。二者哪怕差1个,也会造成死局。
  上文说了,桌面牌数和待发牌池的牌数一致只是过关的必要而非充分条件。即使该条件满足,如果相对于牌桌上的牌数以及图案数量,窗口牌数太少,也会造成死局。比如下面这个极端的例子:假设游戏共有 15种花色,而牌桌上只有这个模式A牌堆,它有90张牌。那么玩家只要在连续7次拾牌时没有遇到3个相同图案的牌,就"必死无疑"了。
  其实这个游戏,一方面要控制关卡的难度,另一方面又要保证能通关本身就是一个相当困难的问题(至少老王没有想出办法)。
  而设计者反其道而行之,(可能)没有花力气去设计算法,把坑留给玩家,得到了极低的通关率,反而制造了话题并形成爆款。
  如此说来,这确实是个抖机灵的"设计"。但老王认为这种"设计"在游戏策划中是不宜被借鉴的,就像现在市面上泛滥的悬疑剧,开始埋坑无数,吊足观众胃口,最后烂尾不了了之一样,长此以往观众(玩家)对于悬疑剧(游戏)的信任感就被消费殆尽了。
  02 洗牌道具的实现
  洗牌的实现原理很简单,把当前桌面的牌记录在一个数组tiles中,当需要洗牌时,先打乱一下数组中牌的顺序,然后让桌面上每一张牌到tiles中重新取一个值。再来个眼花缭乱点的动画,还真挺像那么回事儿。 funcshuffle_tiles: 		 tiles.shuffle tiles_index = -1 funcredistribute_face-> int: tiles_index +=1 returntiles[tiles_index]
  03 遮罩文件的读取
  这里要夸一下Godot Engine,它的很多功能真是方便,比如下面这个str2var它可以简单粗暴地直接把字符串转换成对象类型。 class_nameFileReader staticfuncread(path,default_data):  vardata = default_data  varfile =File.new  file.open(path,File.READ)  varcontent :String= file.get_as_text  ifnot content.empty:  data = str2var(content)  file.close  returndata
  04 对象间的通信
  这个小游戏中存在大量的对象间的通信需求:牌和牌之间、牌和牌堆之间、牌和关卡之间、牌堆和关卡之间。为了快速实现游戏,我大量使用了Godot Engine的Group机制,不得不说Group是Godot Engine最赞的设计之一。
  04 总结
  小游戏《羊了个羊》,从策划和开发的角度来看并不困难,然而"瑕疵"竟然能够成为"噱头",也让人不得不感慨"游戏世界真的一切皆有可能啊"。
  作者简介:开发游戏的老王,高校教师、技术专栏作者、独立游戏开发者。

又亏30多亿,华为掏空赛力斯?2022年最炙手可热的赛道是什么?新茶饮预制菜,还是露营飞盘,或者咖啡?答案都不是,这一年最卷的行业,其实还属新能源汽车赛道。这一年,新能源汽车的渗透率达到27。4(乘联会111月深圳加大扶持力度为中小微企业保驾护航,创业担保贷款发放金额连续两年位居全省首位2022年深圳发放创业担保贷款43。9亿元,同比增加59。24,发放金额连续两年位居全省首位。尤其是小微企业担保贷款发放金额呈现成倍增长势头。记者从深圳市人力资源和社会保障局了解到这就是信心丨开年用工热,六大关键词看经济回稳向好兔年春节刚过,劳动力市场就呈现加速回暖两头旺。各地正加力保用工稳就业,推动新一年经济发展拔得头彩。六大关键词感知用工热度折射出的经济温度。一边是企业加速复工满产用工需求涨一边是劳动抢赛道落子准浙江首个产值超千亿万亩千亿平台诞生位于宁波前湾新区的极氪工厂新能源汽车生产线。拍友卓松磊超千亿!日前,宁波前湾新区管委会发布2022年成绩单,其智能汽车产业平台去年实现工业总产值1260。8亿元,比上年增长27。2MySQL8。0数据字典表MySQL8。0对数据字典进行了重构,用户表数据字典表MySQL其它系统表的元数据都统一保存到mysql库的数据字典表中了。mysql库中,除了generallogslowlog2二月份如何操作时间过的太快了,转眼过完春节又开始过元宵节了,首先祝投资的朋友们元宵节快乐!元月份已经收盘了,下面我分析一下月线图的运行趋势,如下面的月线图所示股指在元月份收了一根带上引线的长阳线中企砍单970亿颗芯片后,美芯片巨头纷纷官宣,外媒扛不住了中国是全球最大的芯片消费国,每年芯片进口额都在3000亿美元以上。不过芯片规则持续修改,再加上国内芯片需求下滑,导致2022年中国减少了970亿颗芯片进口。这对于大量销售芯片的美企AI爱情机器人时代指日可待超越知识文化艺术领域,进行隐秘的情感交流将硬件与人工智能结合,破坏力超乎想象在新的人工智能(AI)技术面前,整个世界都面临着巨大的地壳变动。由微软(MS)投资的开放AI于2022年中企传出消息!首台上海微电子光刻机搬入光刻机之所以非常重要,主要是因为光刻工艺约占整个芯片制造成本的三分之一,耗时约占整个芯片制造时间的二分之一。因此,光刻机是芯片制造必不可少的设备。然而,全球能够生产光刻机的企业并不今年315,没有爱奇艺我不看2023年开年,爱奇艺在通过狂飙收割新会员的同时,也被老会员骂上了热搜。近日,话题3个设备同时登录爱奇艺账号被封登上微博热搜。热搜内容提到,一位爱奇艺年费会员因使用3个设备同时登录技术分析中国软件荣联科技江瀚新材安妮股份三中国软件中国软件与技术服务股份有限公司,是中国电子信息产业集团有限公司(CEC)控股的大型高科技上市企业,是CEC网络安全与信息化板块的核心企业,承担着软件行业国家队的责任和使命
江河奔腾看中国丨珠江潮涌广东奋楫作者丨倪新平吴桂兴梁伟编辑丨谭婷珠江奔流不息,勾勒出广东日新月异蒸蒸日上的繁荣景象。携手港澳加快建设国际一流湾区和世界级城市群创新浪潮涌动,持续推进制造强省科技创新强省建设,形成新梅西依赖症?梅西不上,巴黎真是爆冷了00兰斯头条创作挑战赛10月9日3时,法甲第10轮展开角逐,兰斯坐镇主场迎战巴黎圣日耳曼。上半场,拉莫斯直红离场。半场兰斯00与巴黎战平。下半场,内少替补登场,双方均无建树。最终巴黎少一人少了梅西就是不行?全场被狂射门!10人大巴黎客场艰难守平弱旅10月9日凌晨法甲联赛第10轮,领头羊大巴黎客场挑战排名第14的兰斯,缺少梅西的大巴黎全球低迷,拉莫斯上半就因抗议裁判2黄变1红变罚下,下半场少打1人的大巴黎依靠对方的疯狂浪射和多转会11曼联或冬窗1000万送C罗去美国巴黎欲明夏截胡枪手猎物小贝球队渴望引进C罗据英国媒体报道,美国大联盟球队迈阿密国际可能会将克里斯蒂亚诺罗纳尔多从曼联的噩梦中解救出来。据说红魔已经准备好让他以1000万英镑的价格离开,而大联盟的迈阿密国26万左右,问界M5本田CRPHEV,谁更强近两年,传统燃油车的市场份额被新能源车不断蚕食,汽车行业基本也达到了燃油车新能源车共存的状态。说实话,我一直认为燃油车和新能源车就应该共存,至少现阶段不存在谁淘汰谁的可能性。在20喜迎二十大从3G突破到5G引领看中国移动通信业十年之变飞象网讯(高靖宇文)从3G4G到5G,中国信息通信业发展跑上高速快车道,从万物互联到数智融合从场景应用到生产模式,十年间,通信网络已打造成为一张看不见的立体智慧网络,赋能中国经济社10月书单遇见一本好书,遇见更好的自己一本书可能是国家崛起的起点,也可能是您不断精进的开端。为您送上10月书单,每一本都是助力前沿技术发展的硬核技术图书,让我们在秋意渐浓的假日里,遇见一本好书,遇见更好的自己。重磅推荐刘强东断然离场?45股权再度转让,是不甘还是无奈?最近,最受媒体关注的商业顶流,绝对是前任京东CEO刘强东。尤其是在明州案件有了结果之后,大家更是对这位商业大佬有了不一样的看法。他的口碑,也因明州事件的再次崩塌。现在刘董事长可以算重读列宁伟大的马克思主义革命家理论家列宁的格言依然具有现实意义车尔尼雪夫斯基曾经写到历史的道路不是涅瓦大街上的人行道,它完全是在田野中前进的,有时穿过尘埃,有时穿过泥泞,有时横渡沼泽,有时行经丛林。其蕴含的社会真谛是蔡斌担任女排主教练,妻子称,你肩上担子不轻,我会帮你稳固后方一个好妻子,是男人背后一座巨大的靠山,能够给他遮风挡雨,也能够给他支撑和信心,让他在困难时,信心倍增,让他在发展时,心安理得。拥有一个相濡以沫的结发之妻,是男人一生的幸福,也是男人零跑股价被腰斩,上市就破发的朱江明急什么?1hr亏损在新能源界从来不是新闻,毕竟除了比亚迪外,中国其他新能源车企都在亏,特斯拉成立后也亏了15年。观察一家新能源公司,关键看有没有未来。新能源车企未来的支撑点有两个技术和品牌