您当前位置:首页 - 攻略资讯 - 详情

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)

2024-12-19 16:48:31|网友 |来源:互联网整理

如何将Python版「羊了个羊」打包成exe文件

在Crossin的编程教室,我们了解到如何将Python版的「羊了个羊」游戏转换成exe文件,让无需Python环境的朋友也能玩。

以下是简单的打包步骤:首先,确保你的Python环境配置无误,命令行能正常运行Python程序并能使用pip。

如果遇到问题,检查系统环境变量PATH,确保包含Python和Scripts目录。

安装pyinstaller,可以通过pip快速安装,如需加速,可以指定国内源。

然后,在代码目录下使用命令行,运行以下命令进行打包:bashpyinstaller--collect-all-F-wyour_script.py打包过程中可能遇到资源文件未被包含的问题,如pgzero模块或图片。

这时,需要编辑生成的.spec文件,添加外部资源文件的路径至datas列表,并在打包时指定--collect-all。

打包完成后,可能会有额外的_internal目录和命令行窗口。

要解决这些问题,再次打包时加上-F选项,同时修改命令生成spec文件而不是执行打包:bashpyi-makespec-F-wyour_script.py最后,确保.spec文件中的配置正确,删除不必要的文件夹,再次打包,你的「羊了个羊」游戏就应该以单exe文件的形式,无命令行窗口地运行了。

如果你对Python编程有更多的探索需求,可以参考Crossin的新书《码上行动:零基础学会PYTHON编程(CHATGPT版)》,书中还提供了全程的陪读支持。

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第1张图片-拓城游

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

视频介绍

以下为正文:

昨天有朋友和我说:“最近有个叫《羊了个羊》的游戏爆火,就是太难玩了,你能复刻一个不?”

话说上次玩休闲游戏还是在几年前,但是朋友之托必须赴汤蹈火啊,二话不说,开整!然而,冲动是魔鬼,直到此时此刻,老王也没能亲手玩一局原版游戏,不知道是游戏入口设计得太隐蔽还是网络加载太慢,无论手机端还是PC端,游戏都停留在如下界面。

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第2张图片-拓城游

所以本次游戏的复刻,完全是基于各视频网站云观摩的结果,好在游戏的玩法不是特别难理解。

复刻使用的开发工具是Godot Engine(使用其它工具开发原理也是相似的),目前项目已经开源到了GitCode:

Godot版《羊了个羊》:

https://gitcode.net/hello_tute/SheepASheep

接下来我将通过临摹游戏的方式推测一下这个小游戏的实现原理,本文主要面向对游戏开发有兴趣的朋友,欢迎大家多提宝贵意见。

01 玩法

第一眼看到《羊了个羊》,老王首先想到当年的《连连看》,不过有网友爆料,该游戏“借鉴”了《3tiles》。

瞄了眼《3tiles》,是比较相似。

说心里话,这个游戏的玩法并没有什么过于出众的地方,算是个中规中矩的“低卡路里”休闲游戏。

之所以成为话题作品,主要就是因为它的第2关极其低的通关率,一下子激起了众多玩家的挑战欲望。

而时至今日这个“低通关率”也被网络上的众多玩家揭秘,第2关其实大概率上本身就是个死局。

是程序员故意挖坑设了死局么?先卖个关子,我们先聊聊游戏的开发,然后您自己就会有答案了。

02 实现概要

游戏的整体很简单,但其中有几个实现的重点需要注意:

牌堆数据结构的实现

如何检测和更新可拾取的牌

先做个小定义,一个牌堆中可被拾取的牌以下将简称其为:“窗口牌”。

01 牌堆的结构

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第3张图片-拓城游

最初,我还真被这复杂的牌堆结构蒙住了,但仔细研究一番发现,无论多么复杂的牌堆,其实都是由如下三种牌堆模式组合拼凑而成的:

蓝圈圈出的牌堆模式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

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第4张图片-拓城游

牌堆模式B

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第5张图片-拓城游

牌堆模式C

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第6张图片-拓城游

02 牌堆的数据结构

我将其定义为MContainerBase基类

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],
]

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第7张图片-拓城游

03 如何检测和更新可拾取的牌

三种牌堆模式分别派生自MContainerBase,并对应着如下三种检测方式:

牌堆模式A:仅检测自己正上方是否有牌

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中,这三种牌堆模式还可以通过场景节点制作成预制体,这样关卡设计师就可以轻松地制作出美观的关卡了。

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第8张图片-拓城游

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第9张图片-拓城游

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个相同图案的牌,就“必死无疑”了。

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第10张图片-拓城游

其实这个游戏,一方面要控制关卡的难度,另一方面又要保证能通关本身就是一个相当困难的问题(至少老王没有想出办法)。

而设计者反其道而行之,(可能)没有花力气去设计算法,把坑留给玩家,得到了极低的通关率,反而制造了话题并形成爆款。

如此说来,这确实是个抖机灵的“设计”。

但老王认为这种“设计”在游戏策划中是不宜被借鉴的,就像现在市面上泛滥的悬疑剧,开始埋坑无数,吊足观众胃口,最后烂尾不了了之一样,长此以往观众(玩家)对于悬疑剧(游戏)的信任感就被消费殆尽了。

02 洗牌道具的实现

洗牌的实现原理很简单,把当前桌面的牌记录在一个数组tiles中,当需要洗牌时,先打乱一下数组中牌的顺序,然后让桌面上每一张牌到tiles中重新取一个值。

再来个眼花缭乱点的动画,还真挺像那么回事儿。

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第11张图片-拓城游

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最赞的设计之一。

如何将Python版「羊了个羊」打包成exe文件(程序员用12小时复刻《羊了个羊》,代码已开源)-第12张图片-拓城游

04 总结

小游戏《羊了个羊》,从策划和开发的角度来看并不困难,然而“瑕疵”竟然能够成为“噱头”,也让人不得不感慨“游戏世界真的一切皆有可能啊”。

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。 E-MAIL:admin@bb1314.com 

复制本文链接攻略资讯文章为拓城游所有,未经允许不得转载。