Scratch第五十讲:超级玛丽(一)

admin 发表于 2019-11-01 14:24

首先感谢一下这么多人对CC哥的支持和鼓励,实话说CC哥并不是编程的专家,我也是一个跟着大家一起学习的编程爱好者。CC哥希望通过这个公众号,把自己学习Scratch过程中的一些体会和经验免费的分享给大家,让所有爱好编程的朋友们能够通过CC哥的分享共同来体会编程的乐趣。平常CC哥也很忙,但是不管再忙,为了支持CC哥的朋友们,CC哥也尽量做到每周一更,只要能帮到大家,CC哥就会觉得很开心。,而支持CC哥的朋友们只要把这些学习内容,分享给更多的朋友,那就会享受跟CC哥一样的开心。

今天跟大家一起交流一个超级玛丽的例子,这个程序是CC哥在官网上看的,通过认真分析了编程者的代码,CC哥也学到了一些挺有意思的技巧,在这里分享给大家。

CC哥今天只是把程序中的一部分摘取出来跟大家分享,这样让大家更容易理解和学习。先看看运行效果。

看到没,今天分享的很简单,就是马里奥在奔跑和跳跃。其他的内容以后CC哥再慢慢分享。

做程序首先要构思,你想做成什么效果,然后这些效果你实现的方法是什么,有哪些难点,你有什么解决方案?所以做程序,不是上来就做,认真思考是第一步。

思考程序的关键点

那这个超级玛丽奥的关键点在哪里?

1:首先是奔跑的效果。大家简单一想就知道,奔跑肯定不是马里奥在往前跑,而是路在向后跑,实际上马里奥在屏幕上的位置是不变的。很多跑酷游戏都是这样的,通常是背景在动,而不是人物在动。那这个路往后跑怎么实现?

2:这个路是高高低低不断变化的,你用什么办法能实现这种效果呢?

3:这个路是高高低低不断变化的,那你怎么保证马里奥是随着路升高而升高,随着路降低而降低呢?

4:另外游戏要做得精细,那么马里奥奔跑的动作效果就特别重要,用什么方法来实现这个动画效果呢?

马里奥奔跑动作的效果

让我们先从最简单的开始,马里奥奔跑动作的效果很简单,就是用无数个造型循环播放就行了,这个大家应该都知道了。这个游戏里面马里奥一个简单的奔跑用了24个造型,其中每两个连续的造型都是重复的。

https://cdn.china-scratch.com/timg/191101/1424153M2-0.jpg

通常我们做动画效果都会强调放等待命令,否则程序执行的太快,那么角色的动作可能就变成了快镜头。而这个编者就没有放等待命令。

奔跑的效果的关键是角色的步子的频率要跟路移动的速度一致,这样才能体现出来在路上跑,而不是在路上滑着跑。(好的编程就是对细节的处理)大家可以自己试一下,比如把等待命令放进去,看看这两者的差别。

另外如果大家觉得这个角色很好,特别是这几十个造型,也是不容易找到的,想把这个角色拿到自己的程序里用,要怎么做呢?CC哥在这里教初学者一下,很简单,你只要把这个角色导出来,再导入到你自己的游戏里即可。

https://cdn.china-scratch.com/timg/191101/142415Bc-1.jpg

在角色上点右键,选择导出。然后保存在某个目录里,那么就会有一个后缀为sprite3的文件。然后打开你自己的程序。在角色的图标上选择上传角色就可以了。

https://cdn.china-scratch.com/timg/191101/1424155610-2.jpg

所以以后大家看到好的素材,只要有源代码,都可以很简单的导入到你自己的程序里面。(CC哥所有例子的源代码,只要你懂得分享,都可以免费得到哦https://res.wx.qq.com/mpres/htmledition/images/icon/common/emotion_panel/smiley/smiley_13.png

路的实现方法

跑酷的关键就是背景的移动,这里路就相当于背景。路的关键点是两个,一个是不断往后退,一个是出现高高低低的效果。这里面其实有两种方法实现,一种是用屏幕滚动,之前的讲座里面有讲过,大家可以去复习一下第十八讲:Scratch第十八讲:如何正确实现背景的滚动。这是一种方法。但是缺点是路径会是循环的,而不是随机的。而且背景和角色的互动就只能通过颜色的碰撞来侦测了,编程难度增大。

今天介绍的是另一种方法,用角色来克隆实现。就是把路的一部分作为角色,然后不断克隆来组成路,再通过移动来形成背景移动的效果。

https://cdn.china-scratch.com/timg/191101/1424155304-4.jpg

这个路的角色有三个造型,一个是水平的路,一个是上升的路,一个是下降的路。这三种路的造型随机组合起来,那么就形成了高高低低不断变化的路。

https://cdn.china-scratch.com/timg/191101/14241610J-5.jpg

https://cdn.china-scratch.com/timg/191101/14241A519-6.jpg

这段程序大家可以自己读。这里面有两个关键点:

1:第一个就是克隆的频率要和路移动的速度一致,如果不一致,那就乱套了。如何保持一致是关键点。

https://cdn.china-scratch.com/timg/191101/1424163423-7.jpg

这就是不同步的效果。

那如何做到呢?我们分析代码,会发现这里面有一个状态变量:LaunchObstacle。这个变量就是一个开关变量,来决定什么时候开始一个克隆。在主程序的循环里面有一个等待命令。这有等待到这个变量的值是YES的时候才会克隆下一个变量。而在克隆启动的子程序里面,我们会看到有一段语句:

https://cdn.china-scratch.com/timg/191101/1424164c2-8.jpg

也就是克隆出来的变量要先向左边移动15步之后,这个变量才会变成YES。也就是说克隆出来的变量移动开之后才会克隆一个新的克隆体。

通过这个变量和等待命令,就完美的让一条路连贯起来。大家一定要记得这个编程方法哦。很实用的。

2:除了路的克隆频率和运动速度的同步,还有一点很关键,如何让三个造型能够完美衔接,而不是会突然凸起一块和凹陷一块。也就是让路平滑。所以每次克隆体的位置也是不一样的,每次克隆的位置都要根据上一个克隆体的位置决定,如果前面一个克隆体是向上走的造型,那么下一个的起始位置就要向上一些。如果前面是向下走的造型,那么下一个克隆体的起始位置就要向下一些。

https://cdn.china-scratch.com/timg/191101/1424155304-4.jpg

大家再看这三个造型的名字,看明白没有?只要你读取了每一个造型的名字,就知道下一个克隆体的起始位置了。是不是很有技巧。虽然不复杂,但是确实很有技巧https://res.wx.qq.com/mpres/htmledition/images/icon/common/emotion_panel/smiley/smiley_13.png。所以编程并不是非要做得很复杂,关键还是思路和实用。

https://cdn.china-scratch.com/timg/191101/14241IN1-11.jpg

好点子比技术更重要

3:另外路的高低要有限制,不能太高也不能太低,虽然用了随机数,但是需要做控制。这个大家自己看程序吧。

马里奥如何在路上跑

如何在路上跑,说的意思是如何保持在路上,而不是跑到空中和路的下面。做这个我们最常见的思路是检测马里奥角色和路之间的碰撞关系。但是之前很多例子我们有讲过,当你做碰撞检测时,如果角色的造型比较复杂,那么碰撞检测往往会出现很多问题。所以对于复杂造型角色的碰撞检测,往往引入形状简单造型的角色专门用来做碰撞检测,然后只要保证让复杂的角色来随时跟随这个简单造型的角色即可。

https://cdn.china-scratch.com/timg/191101/14241K409-12.jpg

看,这位编程者不但引入了一个,而是引入了三个。为什么要引入三个?且让CC哥慢慢给你分解。https://res.wx.qq.com/mpres/htmledition/images/icon/common/emotion_panel/smiley/smiley_4.pnghttps://res.wx.qq.com/mpres/htmledition/images/icon/common/emotion_panel/smiley/smiley_4.png

马里奥的第一个影子角色

这个程序真的是体现了:简单的技巧,好的点子,达到完美效果的一个实例。所以说,以后大家不用太担心自己的编程技巧,把更多的重心放在如何利用你掌握的技巧想出好的点子来实现完美的效果。大家一定要记住CC哥今天说的话。

马里奥角色代码

https://cdn.china-scratch.com/timg/191101/14241I125-15.jpg

这是马里奥角色的代码,非常简单,就是跟紧controller这个角色。也就是说,controller这个角色是关键,用来跟路做互动的角色。而不是马里奥这个角色。然后就是不断切换造型来形成跑步的动画效果。

controller角色代码

https://cdn.china-scratch.com/timg/191101/14241L0E-16.jpg

这是controller的完整代码,让CC哥跟大家一起来分析一下。

https://cdn.china-scratch.com/timg/191101/14241IW4-17.jpg

大家知道,这种影子角色都是来做侦测的,不能让大家看到。但是又不能隐藏,因为角色隐藏之后就无法再做碰撞检测了,那怎么办?这里面有一个技巧就是把透明度设定成100!透明的角色,虽然你看不到,但是角色之间的碰撞检测没有任何问题。(这哥们为什么设成99?CC哥也没搞明白,是不是有什么暗招CC哥没看出来?不知道,大家如果知道了告诉我一声。)

https://cdn.china-scratch.com/timg/191101/14241LU5-18.jpg

这一段代码是指没有跳跃动作的代码,其中的主循环是一个下降动作的标准代码。Obstacle是路的角色名字。这段代码就是保证角色始终会落到路面上(下落过程是带加速度的)。前面CC哥讲下落讲太多了,这里就不需要再讲了。

https://cdn.china-scratch.com/timg/191101/14241Q560-19.jpg

这段代码是处理收到起跳命令之后的跳跃处理过程。JumpStartPower这个变量是指起跳的初始动能。代码先包括一个起跳过程,然后是一个下降过程。这段代码如果大家学过上一讲:Scratch第四十九讲:完美的下落和反弹,就非常容易理解。(他这个是简化的起跳和下落,不是完美的https://res.wx.qq.com/mpres/htmledition/images/icon/common/emotion_panel/smiley/smiley_12.pnghttps://res.wx.qq.com/mpres/htmledition/images/icon/common/emotion_panel/smiley/smiley_12.png

大家需要学习的是他的流程控制。大家注意到,编程者非常习惯用消息这个模式,也就是很多进程的控制都是用消息来实现的。

程序开始就用了#NotJumping这个消息来启动这个影子角色的关键任务,始终保持落在路面上。

然后用了#Jump这个消息来处理起跳和降落的过程。每个消息都有循环过程,那如何让不同的消息导致角色控制不出现相互干扰呢?

https://cdn.china-scratch.com/timg/191101/14241U128-22.jpg

编程者用了这条命令,停止该角色的其他脚本,来确保该角色在起跳后不受其他部分代码的干扰,也就是保证起跳部分代码的独立性。

https://cdn.china-scratch.com/timg/191101/14241R628-23.jpg

而在起跳这部分代码完全结束后,再重新广播#NotJumping,来保证原先进程的继续执行。

所以大家可以学习这个技巧,当你一个角色里有多个行动进程的时候,可以用这组命令来进行控制,确保某部分动作或进程的独立性。

大家注意到没有,这个影子角色的代码只做到了让角色始终会落到路上,路水平和路下降都没问题,但是如果当路向上升的时候,怎么让角色也跟着向上升呢?这个影子角色里没有这部分的代码!那如何实现呢?

马里奥的第二个影子角色

https://cdn.china-scratch.com/timg/191101/14241V345-24.jpg

CC哥把这三个影子角色的显示特性打开,这样我们就看到了三个影子角色,其中最上面小的那个,就是Riser角色。

这个影子角色的目的是用来让马里奥能够做到随着上坡的路,一起上升的。


Riser的角色代码

https://cdn.china-scratch.com/timg/191101/14241RO9-25.jpg

代码很简单,就是检测Riser这个变量如果碰到了路的角色Obstacle,然后就发布消息Raiseshadow3。

https://cdn.china-scratch.com/timg/191101/14241U196-26.jpg

在第一个影子角色Controller里有对应的接收消息的程序。如果接收到这个消息,就向上移动3步。

问题来了,为什么要增加一个单独的影子角色来判断呢?为什么不用之前那个影子角色来做判断呢?

编者的思路还是很巧妙的,第一个影子角色的目的是为了让马里奥始终能落在路上。所以这个角色应该是尽量保持在跟路(Obstacle)接触的状态。路向下走,角色自然会落下来。但是如果往上走,那该怎么判断路已经在向上的走呢?第一个影子角色显然就判断不了了。编者另外设计了一个影子角色Riser,然后让这个角色的位置比Controller这个角色高一点,也就是正常来说,这个Riser的角色不会碰到路。会保持在比路高一点的位置。一旦路往上走,那么Riser继续往前走就会碰到路,一旦Riser碰到路,那么马上就知道路向上走了,就发布向上走的消息,通知Controller往上移动三步,如果继续碰到路,说明还在向上走,那么就再移动三步。

是不是很有意思,虽然增加了一个角色,但是编码简单了很多,否则如果只用一个影子角色来做,岂不是要增加无数条代码,和处理更复杂的判断。

https://cdn.china-scratch.com/timg/191101/14241W203-27.jpg

CC哥把四个角色,一个马里奥,三个影子角色全部都移动到中心位置。移到中心位置以后,大家就会看到这四个角色的位置关系。所以说,编者在布局时就让马里奥的脚底跟Controller的角色的平齐,然后Riser的角色高于Controller角色。(一个简单技巧,当你想调整角色之间的位置关系的时候,就让每个角色移动到正中心,然后再在造型里面把每个角色拖到你想要的位置。)

https://cdn.china-scratch.com/timg/191101/1424194615-28.jpg

马里奥的第三个影子角色

最难理解的就是第三个影子角色。CC哥也是想了半天,为什么要用这个角色呢?

Controller2的角色代码

https://cdn.china-scratch.com/timg/191101/14241aN3-29.jpg

从代码里看,这段代码就是一个功能,控制马里奥起跳的,非常简单,但是为什么单独放一个角色呢?以我们做程序的经验,完全可以把这个角色省略掉,直接把这段程序做到controller里面。

https://cdn.china-scratch.com/timg/191101/1424192023-30.jpg

就把这段程序放到controller的主循环里就好了。为什么单独启用这个角色呢?CC哥还特意测试了一下,放到controller完全没有问题,代码的逻辑也没有毛病。这个角色完全可以删掉。百思不得其解。然后CC哥就是反复比较这两种模式游戏执行的情况。试验了十几分钟后,突然领悟了!领悟完不得不点赞编程者思路的严谨。

https://cdn.china-scratch.com/timg/191101/14241954V-31.jpg

大家仔细看这一段,CC哥把controller的角色显示打开,这样大家可以看得更清楚。大家发现一个问题没有?

当controller往上走和水平走的时候,controller会始终贴在路上。而在往下走的时候,controller就不是一直贴在路上了,而是跳着往下落,并不是一直贴在路上往下走。这就是关键点!

这种情况对程序有什么影响呢?

我们看代码就知道了,起跳的关键判断就是确保controller在路上才行。如果controller不在路上,那么起跳动作就不会发生。而刚才大家看到了,controller在下降的时候并不是一直在路上。所以当controller正好不在路上的时候,马里奥就无法起跳了。而超级玛丽这个游戏大家都知道,需要随时起跳,吃金币,躲避怪物等等,如果在沿着路向下走的时候由于controller不能始终贴在路上,所以会造成没法及时起跳。一个起跳不灵活的超级玛丽是不是就没法玩了。

所以编程者特意做了一个新的角色,controller2,特意比controller低了一些,其目的就是为了确保在路下降的时候,controller2能尽量贴着路,避免掉controller随着路向下走的时候不能及时贴到地的情况。

有意思吧,有时候分析一个游戏的代码就像看一个艺术作品,慢慢体会编码者在编程时候的思路和想法。好的程序都会有无数个思维巧妙的地方。不见得代码有多复杂,逻辑判断绕多少圈就显得水平高。一个完全让别人都看不懂的代码也不是什么好代码。

后记,小编朋友公司研发了一个游戏化的少儿编程在线课程(5-12岁),游戏化教学结合scratch(一款在线少儿编程工具,类似乐高的积木拼搭),我家娃娃学了几次课,非常喜欢(超预期),16次课才200多块钱,对锻炼孩子的思维能力和动手动力很有帮助。

感兴趣的朋友可以扫描二维码,关注一下,或微信搜索“大耳猴少儿编程”

https://www.china-scratch.com/Uploads/Editor/2018-04-22/5adca08bdc212.jpg