本文的大概背景,可以总结为一个非科班出身的半道子码农初学python并尝试在一周之内完成一个经典的2048小游戏,然后将这一周所学所感记录成文的非专业性文章。成文的目的只是自我总结与复习、思考而后形成记录,并无意扩散或教别人什么,仅此而已。
为打开思路,梳理脉络,我做了一个简单的脑图。
因此,本文行文大概也是跟着这脉络走的。
一、主要逻辑
既然要做,首先就得想清楚做什么,怎么做。
做什么?python编写2048,那么肯定就得涉及2048的游戏规则,所谓规则,编程里更倾向于叫做“逻辑”。同样,我以展开脑图的形式来呈现我在这一周内实现的2048的逻辑骨架:
明白了要做什么,接下来自然是怎么做?
所谓怎么做,也就是怎么用代码去实现。其实知道了整体的游戏逻辑之后,代码层面该怎么展开也差不多明了了,我们所需要做的只不过是按照逻辑编写代码,然后保证它能够顺利运行而没有bug。主要代码我会在后文逐步放出,在给出参考代码前,先总结一下所需要用到的库。
二、包与模块
python,最让人陶醉的不就是丰富的第三方库吗?正是这些第三方库,让我们的编程变得简单直接、方便快捷。那么,2048需要用到些什么库呢?
首先,2048需要一个界面来放置方块、或是棋盘,顺带放些提示信息、分数之类的在里面,这就涉及到UI。python有wxpython可以实现界面,这个之前用过,总体感觉就是简单,但是低端,即我们常说的low,画面效果极其一般,反正我不太喜欢。于是,我又找了找,发现了pyglet。据我查阅资料过后,发现pyglet很强大,是一个用于Python的跨平台窗口和多媒体库,用于开发游戏和其他视觉丰富的应用程序。它支持窗口,用户界面事件处理,OpenGL图形,加载图像和视频,以及播放声音和音乐。而这些,对我来说都不重要,毕竟我能用到的只是它的窗口和界面事件而已……对我来说,它吸引我的是它!足!够!好!看!
没错,我大概是个颜值党。
于是,pyglet成为了我搭建2048界面的工具。
决定了pyglet,其实这款游戏也就可以动工了,只是在后面的开发过程中,会引入随机函数库random用来随机生成数字和随机确定位置,以及拷贝库copy来实现自定义悔棋功能。
关于这仨,random没什么好说的,pyglet的相关知识建议去官网学习,而对于copy,我想就深拷贝和浅拷贝以及等号赋值这三个概念做个记录,毕竟在这里我是踩了坑的。
【提示:可以用id(x)和==去检测】
三、代码实现
知道要做什么,并且知道有哪些包可以帮上忙后,重头戏当然就是撸代码了。理清逻辑很重要,让逻辑成功跑起来也很重要,所以才有了码农……
我写代码的思路,其实就是根据游戏逻辑来的:
1、画面呈现、界面效果
以上代码就完成了从window到提示消息和棋盘、从棋盘到棋盘上放置数字的方块的界面效果,代码还是很浅显易懂的。
2、初始时刻在棋盘上任意两个位置随机生成数字2或4
既然是初始时刻,那么肯定是放在game_init()函数内的。
3、通过上下左右四个方向的操作,移动数字
对键盘的上下左右做出反应,则是需要slideUpDown和slideLeftRight两个函数。这就涉及到下面第四点内容。
4、移动方向上,碰撞数字相等则合并
这一点可以说是整个2048小游戏逻辑的核心内容,毫不客气地说,理解了这一点的逻辑,写出整个游戏也就没什么其他的拦路虎了。在一个棋盘上,有着随机位置的2或4,伴随着不同方向上的移动,则判断该方向上依次两个数字是否相等,如果相等,则合并,不等则不操作。
逻辑这样描述,虽然已经比较清晰了,但要用代码实现出来可能还需要费些功夫。我们可以把整个棋盘当做一个二维数组,这样就能实现位置和值的对应关系。
接着,从二维数组中抽取一维向量,以便判等合并。而我们知道棋盘上要么是2要么是4,要么是2的N此方(合并过后的结果),而空位置则是0,而0是不需要判定的,所以我们分解这个逻辑的第一步就该是抽取一维非零向量。
抽取过后,该合并的合并。合并的意思是,两个位置删去一个位置,余下的位置变为原来的两倍。
合并之后,就该补0回填了……
我们可以把上下和左右分成横纵两个方向,在横方向上即左右方向,我们可以发现左移、右移的区别只不过是首尾的位置互换而已,纵向同理。
合并的实现代码如上,其实只要理清了逻辑,就能很快写出来了。
以上的代码就是整个的逻辑实现了。
5、移动过程中在棋盘任意非0位置生成2或4
它的返回值,作为我们判断游戏是否结束的标志之一。
6、游戏结束
游戏结束的规则是:当棋盘被充满,并且四个方向都无法再合并。
四、bug修复
在整个实现的过程中,其实没什么特别大的bug,那些由于对python语言不熟悉而造成的语法问题在敲出代码的同时就会知道了,所以自然不消多说。我所记录的主要是由于前期考虑不周全造成的游戏使用过程中出现的bug,2048这个小东西,我就只记录一个游戏结束判定的逻辑缺陷(当然,我在第三点中的第六小点提到的结束规则是改进过后的……)。
最初编写时,我对于游戏结束的规则制定为:当按下一个方向键,棋盘被填满并且无法合并时,即游戏结束。乍看一眼,这规则并没什么毛病啊,于是,代码写出来是这样的:运行一下,会发现棋盘被填满后立即就出现“游戏结束”的标签……
可可可……可是我一眼就能看到棋盘上还能继续玩啊……
只是并不是在该方向上而已。
于是轻而易举就知道了逻辑漏洞:当按下某个方向键时,棋盘虽然充满,但是所谓的不能移动只是在该方向上不能移动,而在其他方向上还存在继续合并的地方。
所以,完善后的代码就成了这样:
五、重要参考与总结
自学是一件比较辛苦的事儿,它不系统,不被科班认可,甚至不自信。但因为它是纯兴趣使然,所以辛苦无所谓,反而会从中收获快乐。而在我的自学过程中,最应该感谢的就是互联网上那一个又一个先行者,是他们的先行尝试,才带给了我捷径与收获。
这一周内,我主要参考和学习的内容如下:
1、https://blog.csdn.net/u011630575/article/details/78604226 (结论稍有错误)
2、https://pyglet.readthedocs.io/en/pyglet-1.3-maintenance/
3、https://study.163.com/course/courseMain.htm?courseId=1263029
代码方面,更多地参考了网易云课堂中黑板客的课程,特此致谢。
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved