一、引言
克隆技术是时下热门的Scratch少儿编程考试或者竞赛必须面对的重点与难点之一。本文试图从较高的层面探讨Scratch编程中克隆技术的本质,并进一步把克隆技术的应用划分为两大类型——共享数据克隆体和非共享数据克隆体,最后给出各自的应用举例。
对于初、高中对面向对象编程概念没有基础的小朋友,可以略过本注释的阅读转而进行第二小节的阅读。熟悉其他高级编程语言的朋友都了解面向对象编程,其三种特征(封装、继承与多态)都在Scratch中得到非常自然与形象的体现。在此暂不赘述,有兴趣的朋友可多多联系这些概念,从而更有助于理解Scratch中的克隆技术。
二、 Scratch中克隆体可分为两种类型
编程中,当需要大量相似的精灵完成相似的任务时,建议主动考虑使用克隆技术。其实,通过大量应用克隆技术的案例分析后,总体归纳一下,克隆体不外乎如下两大种类型:
**【共享数据克隆体】**这种克隆体允许其他克隆体或母体精灵访问它的数据(可以是任何信息,如生命值、X位置、Y位置等)。
**【非共享数据克隆体】**如果一个克隆体的数据不需要被除自身以外的任何其他角色访问,称此种克隆体为"非共享数据克隆体"。
在以后的文章中我会通过全面的应用实例来展示这里的结论。在本篇中,我们仅通过典型的小例来说明问题。
三、非共享数据克隆体举例
这种类型的克隆体不需要共享信息,因此这种情况下问题比较简单。
情形1:根据指定顺序生成克隆体对象
制作非共享数据克隆体的一种方法是根据创建克隆体的顺序为每个克隆体指定一个标识号。且看下面的举例运行时截图。
1、快照
设计想法是:当游戏开始时(点击绿色小旗子),母体(黄头猫)生成自己的三个克隆体,并在不同位置显示;然后各自发言。造型设计如下:
下面,我们来看相应的编程实现。
2、编程分析
首先,需要定义以下变量:
标记每一个克隆体ID的局部变量,在本文中称为“lvID”(“lv”含义:Local Variable,即局部变量)。
标记克隆体任何属性名称的局部变量,如位置、速度等,本例中省略。
先来看母体代码:
如前所述,当点击绿色小旗开始程序执行时,母体生成三个克隆体。
**【问题】**为什么上面的变量定义为局部变量?其实,看过 Scratch高级编程之妙用变量管理母体与克隆体 的朋友都可能理解了在克隆体编程中局部变量与全局变量在克隆体编程中的奇妙作用。稍后的代码中还会说明这一点。
接下来,我们分别为每个克隆对象启动下面脚本(积木【当作为克隆体运行时】的作用):
大家先略过代码【说(你好)】这一句,往下看。意思是:根据变量lvID的当前值,克隆体切换成不同的对应造型并修改自身其他属性。
**【问题】**如果有需要所有的克隆体立即都要做的事情,怎么办?
【方法一】如上面代码所示,可以在条件语句之前放置相应的命令块(此处只有一句问好)。
【方法二】或者说是更推荐的做法是,将这些命令块放在另一个“当作为克隆体启动时”中。
【小结】至此,我们实现了既定目标:根据指定顺序生成克隆体对象。
接下来,我们来简单介绍一下局部变量与全局变量在克隆体编程中的区别。这里只使用代码展示一下:
首先,在母体角色(黄头猫)引入一个全局类型变量“gv克隆体计数器”。简单修改一下母体代码如下:
意思暂时不用解释了。接下来,添加如下代码:
我们想点击每一只猫时让它说出对应的数据,显然触发器“当作为克隆体启动时”中的代码块只有克隆体执行,而其他地方的代码母体与克隆体都要执行。因此,上面的代码是母体与克隆体都要执行的。
执行结果截图如下:
绿猫一开始说出自己的“lvID”值,稍后说出全局变量“gv克隆体计数器”的值,如下图:
点小红猫,显示如下:
其他省略…
熟悉面向对象编程的朋友,如果结合变量(也称作“属性”)的继承来理解这里公有变量与私有变量区别的话,相对更容易一些。
情形2:基于多种属性制作克隆体
在更多情况下,例如在开发炮塔类游戏的情况下,方向、造型及其他属性的组合非常之多,简直数不胜数。在这种情况下,典型的方法是将这些属性(方向、造型等)直接定义为一些全局变量——这些变量不属于某一种角色,从而使得各种游戏角色(包括各种角色的克隆体)都可以操作这些变量。虽然这样的方案(定义很多的全局变量)也带来整体数据管理的复杂性,但这种办法也确实是由软件本身的复杂性和Scratch的局限性不得已而确定的。
接下来的代码片断中,我们只作部分关键代码的展示,在以后的文章中我还会开发出完整的小游戏来展示这些技术的应用。下面是一个简单的空战小游戏的截图(尚不完善,所以只给出与本文主题直接相关的代码分析)。
简单快照
当然,实际空战游戏中,会有我方与敌方的许多战机(以及类型),子弹或者炮弹数量及类型更可能是很多种且数量更大。
数据结构定义
基于上面分析,我们需要针对子弹的多种属性创建许多全局变量(而不是局部于子弹角色的局部变量!)。在本例中,将使用以下定义:
“gv子弹类型”用于区分玩家(我方)的子弹和对手的子弹
“gv子弹X坐标”和“子弹Y坐标”表示子弹的起始位置
“gv子弹移动方向”表示子弹的移动方向
关键代码一
每当想让我方战机角色开始射击时,可以把下面脚本放在某个触发器中(比如在按下空格键之后,或者某种情况下):
再看子弹角色造型设计:
当然,为了简化问题,我们只设计了两种造型:一种是我方子弹,一种是敌方子弹。
关键代码二
相应于上面的代码,作为子弹角色,典型的克隆体执行代码设计可能是:
至此,我们给出非共享数据克隆体w使用的第二种更典型的情形分析。
接下来,我们来看相对复杂的共享数据克隆体应用的情况。
四、共享数据克隆体举例
如前所述,本节中讨论的“共享数据克隆体”是指其他精灵(包括母体精灵)或克隆体需要访问当前克隆体的私有数据,例如它的位置、造型名称与编号等。
这里提供一种通用技巧就是,将克隆体的各私有数据存储在若干个对应的全局列表中,从而实现其他精灵可以访问特定克隆体的特定数据(属性值)。
因为本处的例子也是小例,但是为了有助于后面理解,先上运行时快照的第一个。
注意:本例中,母体是老黄猫!
(一)数据结构定义
要创建共享数据类型的克隆体,需要以下数据结构:
一个叫做“gv克隆体计数器”的全局变量:这是一个计数器变量,用于统计创建的克隆体个数。
一个叫做“lv克隆体编号”的局部变量:用于克隆识别自己。
“gv造型名称列表”:这是一个存储克隆体共享属性的全局类型列表变量。在本例中,我们只定义这一个全局列表,用于(母体或者其他角色或者本克隆体的其他克隆体兄弟角色)跟踪克隆体的造型信息。
(二)母体角色代码一
本代码实现了三点主要功能:
(1)母猫站在舞台左上角(说出自己的身份),两个全局变量初始化
(2)通过执行4次的循环,创建4个克隆体,并初始化一个(这里只有一个!)全局列表变量
(3)还原母体造型(因为经过循环中的不断切换造型的克隆操作后,母体造型有所改变)
**【问题1】**为什么这里造型名称列表内容是空的?
**【问题2】**私有变量“lv克隆体编号”为什么没有在这里露头?
(三)克隆体执行代码
含义:每只小猫变得小些;私有变量“lv克隆体编号”在此出现,用于存储当前克隆个体编号(这个编号正在存储在产生本个体时的全局变量中)。然后,调整一下自身位置,并报出自己的个体编号。最后一句比较关键:把当前克隆体造型名称存储在全局列表变量中。
【思考】上述代码中,能否把最后一句中的变量“lv克隆体编号”使用全局变量“gv克隆体计数器”代替?为什么?
(四)母体角色代码二
很简单,当点击任何角色(包括母体)时,报出自己的造型名称。
【问题】当点击母体时,报出自己的造型名称是否正确?根据Scratch支持特征,尽管母体中仍然可以访问私有变量“lv克隆体编号”的值,但是其含义已经“歪曲”了!请认真思考。当然,有“面向对象编程”概念基础的朋友,是不难理解这个问题的。
(五)母体角色代码三、四
由上面第三段代码知,我们的设计目的是:当用户按下空格键时,让2号小猫思考“妈妈在哪?”这个问题,并通过Scratch消息广播技术寻找妈妈。
在第四段代码中,妈妈接收消息时,报出自己的身份。
乍看起来有点复杂,但是结合我前面发的两篇克隆文章并结合本文前面的介绍,在真正理解了全局变量与私有(也称“局部”)变量在克隆技术中的特殊用法后,上面的问题肯定会豁然开朗。有关截图如下:
小猫思考:
猫妈妈回应:
其实,我们还可以将两种克隆体结合起来使用,在共享数据的同时创建不同类别的克隆体,从而创建功能更强大的游戏、创新创意小程序……
五、小结
本文结合Scratch克隆体应用的各种情形,大胆地把克隆体使用的各种场景归结为两种类型,即“共享数据克隆体”和“非共享数据克隆体”,并给出各自的应用举例。实践需求是复杂的,也灵活多变。此外,我们还有可能将两种克隆体结合起来使用,即可以在共享数据的同时创建不同类别的克隆体,从而创建功能更强大的程序。本文中若有概念或结论不当之处,请读者朋友批评指正!