Scratch游戏《2048》实战(五)

今天,我们接着上次留给大家的作业继续。

我们要用【待处理元素】列表中的内容替换掉【元素布局】列表中对应行的4个元素,可我们发现当我们使用一个4次循环将内容替换掉以后,实际的前端显示并不是右对齐,而看起来像是左对齐的,这是因为我们在处理【待处理元素】最后,使用的是将0加入到列表末尾的方法,所以导致我们在替换【元素布局】对应的内容时,使得0到了一行的右端,所以整体看起来像是按了左键该有的效果。
那么我们怎么来解决这个问题呢?我们这里考虑两个思路,第一个,在处理【待处理元素】的最后,我们将0加到头部,这样就可以在替换【元素布局】时有正确的效果了;第二个,在替换【元素布局】时,我们从【待处理元素】中倒着取出元素去替换,这样的效果就会正确了。

实际上这两个效果的最终效果是一致的,我们这里暂时选取第二个方案来执行。

选取思路二,倒着替换【数据布局】中的元素

这样我们的右移操作就基本上形成一个大体框架了。
参照右移的代码,我们尝试把左移、上移、下移补全,并增加对应的按键响应,我们游戏的整体框架就初见端倪了。

下节课我们将继续完善游戏的其他细节,使游戏变得更加完善。
下课~

Scratch游戏《2048》实战(四)

上节课最后,我们把制作右移操作积木块的任务当成了作业布置下去,大家做的如何呢?下面我们先简单看一下老师这边的实现情况。

我们再稍微往细致思考一下,还记得上节课分析出来的三个步骤吗?
①删除同行的所有0;②检查相邻元素是否相同,如果相同就去掉一个然后留下的+1;③补“0”使得该行总数为4。
我们依次来看。

第①个,删除同行的所有“0”。
删除所有的“0”,这个好实现,但是“同行”应该怎么操作呢?
第一行是列表【数据布局】中的第1、2、3、4个元素,第二行就是第5、6、7、8个元素,第三行是第9、10、11、12,最后一行是第13、14、15、16,那么我们发现了什么样的规律吗?是否有一个通用的公式可以代表每行的四个元素的下标呢?大家自己思考一下,我把答案用白色的文字写在这里:在下面~
(行号 -1)×4+列号

这样的公式第一行第三个元素的序号是3,我们把行号=1,列号=3代入计算,得到的结果为3,这当然没问题了;换一个试试,比如第三行的第二个元素的序号是10,将行号=3,列号=2代入计算,得到的结果确实是10。由此可见,我们使用了一个通用的公式完成了【数据布局】中的元素序号和行列坐标关系的转换,这样对于我们下面的工作又做好了铺垫。

我们第①步要做的是删除每行所有的“0”,那么我们自然不能在【数据布局】这个大的列表中进行删除,因为这样会影响到这个列表的排列顺序,所以我们选择一个比较稳妥的方法,将每行的元素取出到一个临时列表变量【待处理元素】中,每次放一行共4个元素,然后我们开始在这个小的列表里进行处理。

既然要放到这个小列表里,那么我们首先要保证这个小列表里是空的,即便它本来是空的,我们也应该在开始时进行一个初始化,删除掉它当中的所有元素。然后我们就可以依次放入第一行的内容了,用我们刚才得出的公式,嵌入到一个4次循环中,每次取出一个,放入到【待处理元素】中。我们发现这个公式里要考虑两个变量——行号和列号,所以我们需要两个临时的变量来用于计算。

这里要注意添加到小列表时的顺序
这里使用的是在“第1项前插入”,为什么呢?

对应行的内容我们取到了,“0”我们也去掉了。
那么下面我们执行第②步:检查相邻元素是否相同,如果相同就去掉一个然后留下的+1。
这一步操作,实际上,我们要进行3次比较,分别比较第一列和第二列、第二列和第三列、以及第三列和第四列,如果有相同的,我们就删掉一个,然后留下的+1。

使用一个临时变量,完成了一行的4个元素的比较

好了,下面剩下最后一步了,第③步,补“0”使得该行总数为4。

为了完成这最后一步的要求,我们要先把【待处理元素】这个列表中补齐4个元素,这个简单。

用0补齐

最后,我们按照顺序,用【待处理元素】列表中的元素,替换【数据布局】里对应行的元素,之后就完成咯。

这部分内容留给大家做本次内容的作业吧!

Scratch游戏《2048》实战(三)

上节课我们成功构建了后台数据和前台显示部分的关联,同时还做好了空白位置新增加一个数字2的功能。那么接下来就到了游戏的核心部分,通过方向键来对棋盘上的数字进行移动的部分了。

在这个状态下,按下右键

我们还是以这个图为例来进行分析。
当我们按下方向键右时,棋盘上的所有棋子首先全部右移,先来看数据层面是如何移动的。

(后两张图里我将0设置了隐藏不显示,这里我就不再补充了,大家明白就好)

再图①的情况下按下右键,然后我们发现这里经历了两个过程,首先是由①到②的这个步骤:在这个过程中,所有的棋子都靠到了自己所在行的最右边,也可以换一个思路来理解,就是把之前所有的“0”删除,然后又全部补充在了现有序列的左边,比如第一行由“1010”,变成了“0011”(请仔细思考)。接下来第二个过程,由②到③的步骤:将相邻的元素从最右边开始向左比较,如果有相同的元素,那么我们去掉一个,然后把留下的元素+1,所以第一行的“0011”就变成了“0002”(结合刚才的思考,“0”是不是在这里添加更合适呢?)

这里会有同学提出异议了,之前的文章里指出的游戏规则里说的是,相邻的两个相同的元素应该在移动过程结束后相加呀,1=1=2,这里怎么说是+1呢?
这个问题,我们带着它继续往下看下面这张图,可能就会明白了。

对比上面的后台数据图,是不是

我们从后台数据转化成前台显示后,我们发现,这个相加的规则是针对前台显示而言的,而对于后台的数据来说,显示部分的相加,恰好就等价于数据部分的+1。

既然我们已经分析出了右移时的三个步骤,分别是:①删除同行的所有0;②检查相邻元素是否相同,如果相同就去掉一个然后留下的+1;③补“0”使得该行总数为4。当然,这三个步骤要重复4次(为什么?)。

那么,我们这堂课的作业就是将右移操作转换成代码。

Scratch游戏《2048》实战(二)

上一回,我们已经成功把问题的重点就挪到了用来存储棋盘数据的列表变量上,那么这次我们就先从这个列表的结构说起,看看它里面应该如何合理的构建这个存放数据的列表,才能帮我们实现游戏的效果。

内部数据 → 显示画面

还记得这张图吧,左边的部分就是我们游戏后台的内部数据,我们将其存储在一个列表变量中,起名为“数据布局”,比如图中的数据,我们把它存入列表以后,列表应该是这样的:

数据布局

那如何让16个克隆的角色获取到自己对应的那个位置的值呢?
首先,我们这里要使用一种特殊的变量——局部变量(角色专用变量)。这种变量只可能被某个角色使用,其他角色是无法看到代码区里又这个变量的,同时,如果一个拥有局部变量的角色执行了克隆命令,那么每个克隆体之间的局部变量又是互不干涉的,他们的初始值与角色本体的局部变量值相同,但之后两个变量就互不相干了,克隆体中的变量值发生变化,对角色本体的变量没有影响。

这里,我们使用一个局部变量来给每个克隆体编号,我们给这个变量起名叫“角色ID”,ID变量初始值为1,每克隆一个克隆体,这个变量的值+1,所以这样下来,第一个克隆体的ID变量值为1,第二个克隆体的ID变量值为2,以此类推,第16个为16,而克隆全部结束后,本体的ID变量值为17,不过这个已经无关紧要了。

下面就是要让克隆体盯紧列表变量“数据布局”中自己所需要关注的那个变量的值了,我们发现,它们的ID变量这时候就起作用了,使用【数据布局】中第【角色ID】个元素,就可以轻松的获取到每个克隆体对应的数据值,我们使用一个死循环,让克隆体一直关注这个值,等于0时隐藏起来,一旦大于0,我们就把造型编号根据这个值进行一个改变,这样每个用于显示的克隆体就与数据部分一一对应起来了。

想想看这样的代码有什么好处

接下来,我们还要找到一个合适的方法来在空白的位置添加一个新的“2”,在Scratch3中,我们借助列表变量中特有的方法“【数据布局】中的第一个(0)”,就可以轻松找到列表中的第一个0。但我们现在需要的不是第一个,而是所有,我们要收集整个列表中所有的“0”元素的位置,然后从中随机挑一个出来,还好我们的列表并不大,只有16个元素,我们可以用一个16次的循环,挨个查看列表中的每个元素是否为0,如果是,那么就把序号保存到另一个列表变量——“可能零位”中。这里,我们需要一个临时变量,用来做序号,从1开始一直到16,方便我们收集。当这个循环结束时,我们就会在“可能零位”这个列表中收集到一些数据,其中包含所有目前棋盘中还空着的位置,然后,我们使用随机方法,取出【可能零位】中的第【(1)到【【可能零位】的项目数】的随机数】项作为一个目标位置,然后将【数据布局】中对应的位置,设置为1。如此我们就完成了在棋盘空白位置添加一个“2”的这个操作了。另外,我们很容易想到,这部分代码是要频繁调用的,所以我们可以给它封装成一个单独的自定义积木块来方便我们后面的使用,当然在封装的时候,我们也要考虑一些局部的初始化过程。

请大家仔细想想,这段代码还有可以优化的地方吗?

Scratch游戏《2048》实战(一)

今天开始,我们来对前几日说的游戏《2048》,来进行一个系统的分析,对制作过程进行一个比较详细的说明,后期也会有视频教程放出,大家有兴趣的朋友们可以跟着一起来做做看。

作为游戏制作的第一篇内容,我们首先就要对游戏进行一个分析,有了分析思考的过程,才可以在制作的过程中有的放矢。

首先,《2048》这个游戏的规则大致是这样的,玩家需要控制所有方块向同一个方向运动,两个相同数字方块撞在一起之后合并成为他们的和,每次操作之后会随机生成一个2,最终得到一个“2048”的方块就算胜利了。

那么我们在构建这个游戏系统的时候,就需要先想好游戏的主框架,包括核心算法的主思路、主要的变量、主要的角色等等。由于是在Scratch中进行开发,所以我们在设定核心算法时要考虑到Scratch本身的特点和局限性,下面我们来具体分析。

游戏中存在一个4*4的棋盘,而后面反复出现的棋子很容易使我们联想到要使用到的是“克隆”功能。在这里,我使用一个相对比较容易理解的思路来进行构想,假如4*4的棋盘里每个位置都有一个克隆角色,而每个位置在数据存储中都有一个数字与之对应,0代表不显示,1代表显示2,2代表显示4,以此类推,11代表显示2048。反应比较快的朋友可能已经看出来,除了0以外,这刚好是2的整数次幂的规律所在。而且,用来代表的那个数字是从1到11,刚好可以与角色的造型功能相结合,实现起来也比较容易。

内部数据 → 前景显示

好了,经过上面的分析,我们发现,游戏的显示部分,通过16个克隆体和一个用来存储棋盘数据的列表变量就可以完美的解决。那么问题的重点就挪到了用来存储棋盘数据的列表变量上,它里面应该如何存储,它又该如何根据玩家的按键发生变化成为了我们接下来要思考的核心问题。

Scratch讨论之《2048》

电脑没有拿回家,发现今天还没更新,只好用手机来码字了。

今天主要在Scratch群里讨论了游戏《2048》的一些问题,我这里也写出我的思路来,大家可以一起讨论。

在这个题目刚出现的时候,我第一时间想到的是利用角色的克隆体进行上下左右的移动,但是在实践时发现,在处理移动动作时,要提前判断目标位置是否有其他的数字块,而这就存在一个判断时机的问题,如果目标位置本来有数字块,在当前数字块判断的时候还在,但稍后它移走了,这就造成当前数字块错过了移动时机,从而产生逻辑错误。

当然,想要解决这个问题也是有方法的,针对不同的移动指令,从需要移动的末端开始倒着进行移动指令的执行,这样就可以解决掉移动时机的问题,但是这样做会产生一个不确定因素,就是部分性能不好的电脑在执行这里会出现卡顿,导致数字块移动的过程被过分的放慢,体验很不好。

所以我更换了一套思路,使用16个数字块克隆体,充当一个“LED灯珠”角色,当数据部分需要对应位置的数字块呈现数字的时候,克隆体直接切换为对应造型并显示,这样数据部分的运算封装到一个不显示过程的自定义积木中,这样数据部分的运算相当快,然后16个克隆体只要根据数据分配,实时切换造型就好了。

大讨论作品之《成语接龙》

为了督促自己不停下学习的脚步,我决定尽可能每天来这里更新自己在编程方面学习和研究的成果,只要平时闲暇之余有所感触,我都会发到这里,大家一起勉励。

首先是上周在QQ群里和大家提到了制作“成语接龙”游戏的话题,在聊起这个话题之后,很多朋友给出了响应,并纷纷制作出了自己的项目,在这里首先感谢大家的支持。

这里稍稍总结一下。

游戏的规则我想我们大家都是了解的,无非就是在说出一个成语为前提的情况下,第一个字要和上一个人的答案中最后一个字相同,或同音同字,或同音不同字,或同字不同音。一开始,大家主要以制作同音同字这种情况为主,遇到的问题瓶颈在于,结尾的字太多了,相当于要把所有汉字开头的成语全部罗列出来,这将带来一个庞大的数据集合(列表),在Scratch群中,大家借助了列表的文本导入功能,成功将这个问题进行了变相的解决,之后按照读音,对文字进行分类,每个音一个列表,进而实现了同音不同字的解决方案。而对于最后一个同字不同音的部分,虽然可行,但奈何要从庞大的列表数据中筛选这些内容确实效率不高,所以最终没有特别的去实现它。

在Python群中,讨论的方向则在一开始就有了分歧,一部分学员依靠数据本地化,采用了类似Scratch中把成语存储在本地数据中的方法,建立了一个庞大的库文件(-_-||),另一部分学员则准备使用网络爬虫功能实现网络数据的采集,让网络作为程序天然的数据库存在,最终的事实证明这样的方案也确实优秀。

我们找到了一个网站,这个网站使用拼音作为页面,页面中列出了该读音下所有成语,这样问题转化为两个:1、将上一条成语的末尾文字的拼音找出来;2、从对应的成语列表页面内搜集所有的成语,并随机取出一个。

第1个问题,我们直接发现就在这个网站里,支持对每个文字进行Unicode检索,检索结果中恰好就有该文字的拼音,而且是所有读音,于是这个问题又分解成了两个新的问题:1、将该文字转换为Unicode;2、从文字检索结果页面中搜集所有的读音,并随机取出一个。

现在问题成了三个,但三个问题的难度对于爬虫知识来说,都不算难,其中页面数据搜集部分涉及到了正则表达式知识。这些问题多多少少都给大家带来了一些问题,绝大部分使用爬虫来做的学员只能使用比较繁琐的字符串处理方法,在获取的页面内容中搜集结果,正则表达式部分由于没有接触过,都不太明白该如何下手。最后在大家一起讨论+学习的氛围下,我们成功完成了正则表达式的匹配,也成功搜集到了我们要搜集的内容,并最终实现了在线版的“成语接龙”游戏。

之后我又在基础上,实现了判断回答的词组是否为成语等等。

然后,我通过玩这个游戏,认识了好多从来没听过的成语……