自制小球淘汰赛教程(二)传送门、闪队小球以及它们背后的原理
一、传送门和碰撞代码基础格式
小球淘汰赛里面的传送门是怎么实现的呢?
首先,画一个你想用来当传送门的东西,比如我随便画个红色长方形。
右键传送门,打开script脚本选单,找到onCollide.
oncollide的意思是:这个东西每被碰撞一次,就执行一次右边的代码。
右边的代码默认是(e)=>{ },你可以把它理解成代码格式,这是空代码,什么效果都没有。
今后所有这类(e)=>{ },要书写代码时,千万别把(e)=>{ }删掉!自己的代码写在大括号里面。
那么,先看一个传送门代码的实例:

如果你会英文,知道英文单词的意思,应该一眼就能看明白。
英文字母大小写无所谓,不影响实际效果。你可以全部大写,全部小写,或者大小写混杂都没问题。
(e)=>{}和e.是代码格式,先忽略掉。
onCollide(在碰撞时)other(别的)pos(位置)=(等号,是赋值符号)[60,100](一个具体的位置坐标)
意思就是,当它被碰撞时,撞到它的东西,位置坐标就变成[60,100]。
是不是很好理解?
所有onCollide代码都可以套用这个格式。
other可以改成this,this表示这个东西本身,other表示撞到它的东西。
比如,你写e.this.pos = [60,100],意思就是当它被碰撞时,这个东西本身的位置坐标变成[60,100]。
e.this.可以省略,直接写pos=[60,100]默认就是e.this.。e.other.不能省略。
那么,总结一下oncollide代码的基本格式:
等号左边:写想要变的属性的名字,至于名字是啥,去看教程第一章讲的,script脚本选单里面那些英文单词就是属性名字。
等号右边:写你想要的数值。可以不写具体数值,但要注意,不管写什么,格式都保持一致!比如,坐标pos是二项数组,等号右边就必须全部是二项数组,不能写成单个整数。
下面看几个淘汰赛常用的onCollide代码实例:
(这里做展示,我把所有的(e)= > { }格式省略掉了,你自己在algodoo里面写代码时千万别把(e)= > { }删掉)
①e.other.vel = [5,5]
②e.this.color = e.other.color
③angle = angle + 1
④text = e.other.materialname
⑤color = color -[0,0,0,0.25]
只要看得懂这些英文单词,你应该就大概知道这些代码会是什么效果了。
①other(别的)vel(瞬时速度)=(等号,是赋值符号)[5,5](一个具体的瞬时速度)
就是会给小球特定速度的障碍物,用来当做通关路径比较固定的关卡地板。
②this(这个)color(颜色)=(等号,是赋值符号)other(别的)color(颜色)
被撞之后,它的颜色变成撞它的东西的颜色,这就是能被染色的障碍物。
③(没有e.xxxx.前缀,默认是e.this)angle(角度)=(等号,是赋值符号)(没有e.xxxx.前缀,默认是e.this)angle(角度)+1
意思就是,被撞一次,这个东西的角度值就加一。
④(没有e.xxxx.前缀,默认是e.this)text(文字)=(等号,是赋值符号other(别的)materialname(物体名字)
意思就是,被撞一次,它显示的文字就变成对方的名字。这就是过关区上方显示名字的文本框。
⑤color(颜色)=(等号,是赋值符号)color(颜色)-[0,0,0,0.25]
color是个四项数组,意思就是,每被撞一次,它的color值的第四项就减少0.25。color第四项是透明度,也就是被撞一次就变得更透明一些。
.
.
.
如果你要使每次碰撞执行多个效果,比如既传送又改变速度,就用分号隔开,像这样写:
(e)=>{
e.other.pos = [60,100];
e.other.vel = [5,5]
}
以上就是oncollide代码的最基本的格式。
二、其他(e)=>{ }

我们可以看到,除了oncollide,其他还有好些地方默认写的(e)=>{},代码书写方法跟oncollide是一样的,只是触发条件不同。
e.other是oncollide专有的,其他地方没有e.other.,但是都有e.this.(当然e.this.可以省略不写)。
oncollide是碰撞时才执行代码,那么说下其他地方代码的触发条件。
【poststep】当场景在运行时,这里的代码就会一直执行。
【update】只要你打开了algodoo,不管你有没有按空格开始运行,这里的代码始终在执行。
【onClick】这个东西被鼠标左键点击了,才执行代码。
【onDie】这个东西以任何方式死了(消失了),就执行代码。
【onHitByLaser】这个东西被激光照到了,就执行代码。这里可以写e.laser.,让照到它的激光笔来执行代码,跟oncollide的e.other.原理相同。
【onKey】我没用过,不做介绍。
【onSpawn】这个东西出现在场景的一瞬间,执行代码。(刚打开场景也算onspawn,可用来记录自定义函数。)
三、条件判断和逻辑判断
在做传送门时,有时候可能会有这样的困扰,直接写e.other.pos = [x,y]的话,只要碰到它的东西就都会传送。但是,如果我只想让小球传送,其他东西碰到不传送,该怎么办呢?
这时候就要用到条件判断了。
条件判断和逻辑判断符号:
> 大于
< 小于
== 等于(条件判断中的等于是两个等号,只有一个等号表示赋值)
>= 大于等于
<= 小于等于
!= 不等于
&& 与
|| 或
!非
条件判断的格式:
条件 ? {符合条件执行的代码}:{不符合条件执行的代码}
当然,还是得写在(e)=>{ }的大括号里面,这个格式始终不能省略。
我们看个例子,写在oncollide里面的:
e.other.radius == 0.25 ? {
e.other.pos= [60,100]
}:{}
对照上面的条件判断符号,就知道它的意思了
这是传送门的碰撞代码,但是加了个判断条件:
对方的半径是不是等于0.25?如果符号这个条件,就执行e.other.pos= [60,100],也就是传送;如果不符合条件,不符合条件代码是空的,也就是无事发生。
这样,这个传送门就“只传送半径为0.25的东西”,只要不符合这个条件,都不会被传送门传走。什么方块障碍物啊,乱七八糟的多边形啊,你都没有半径这项属性,别想着碰我传送。
。
其他条件判断和逻辑判断符号使用示例:
①在poststep里写
(e)=>{
e.this.colorHSVA(3)<=0? {
e.this.timetolive = 0
}:{}
}
ColorHSVA(3)是什么意思呢?这种格式表示取数组中的一个数字。我们知道colorHSVA是四项数组,假设某个东西的colorHSVA是[180,0.5,1,0.25],那么colorHSVA(0)表示第一个数180,colorHSVA(1)是第二个数0.5,colorHSVA(2)是第三个数1,colorHSVA(3)是第四个数0.25。
对,数组的编号是0,1,2,3……的顺序,也就是说,你要取数组中的第x个数字,就得写数组(x-1)。
回到这个判断语句,poststep是运行后时刻执行代码,这东西的colorHSVA第四项是不是小于等于0?如果是,那这东西的存活倒计时就变成0;如果不是,就无事发生。
配合oncollide里写colorHSVA = colorHSVA - [0,0,0,0.2],就做出了“撞一次就变透明一点的方块,完全透明就消失”的障碍物,也就是淘汰赛中的“集体打工关卡”。
②在oncollide里写:
e.other.colorHSVA(0) >=165 && e.other.colorHSVA(0)<=195 ? {
e.other.pos = [60,100]
}:{e.other.pos = [60,90]}
对照上面的符号表,意思就是,碰到它的东西colorHSV第一项(也就是色相)是否大于等于165,并且同时满足色相小于等于195?(这个条件就是筛选出色相在165~195的东西,也就是青队)如果满足条件(是青队的),就传送到坐标[60,100];如果不满足条件(不是青队的),就传送到坐标 [60,90]。
总结一下,条件判断作用就是分类筛选,只让符合条件的对象执行代码。
四、常用数学表达式
前面我们可以看到,oncollide代码里面,等号右边不一定要写具体的数值,比如angle = angle + 1。
那么,既然能写加号,其他数学符号能不能用呢?
当然可以用。
如果你数学成绩很差,就用最简单的加减乘除吧;如果数学还不错,可以用取余、三角函数、对数、根号、勾股定理等更多数学表达。
①基础运算:
+ 加号
- 减号
* 乘号
/ 除号
()括号 四则运算当然是先乘除后加减,如果想要先加减,请使用括号提高优先级。
% 取余(也可以写成mod) 示例:angle % 2 就是角度值除以2的余数。
^ 幂运算 示例:vel ^ 2 就是速度的平方
(多项数组每项都会执行运算,比如速度是[3,6],那么vel ^ 2就相当于[3 ^ 2 ,6^2]也就是[9,36])
! 用于布尔变量转换,也就是true和false互换。比如我在oncollide里写e.other.heterocollide = ! e.other.heterocollide,heterocollide是无自身碰撞,意思就是被撞一次,无自身碰撞如果是true就变成false,如果是false就变成true。
②特殊数和随机数
math.pi 圆周率,algodoo取的值是3.1415927
math.e 自然常数,algodoo取的值是2.7182817
rand.uniform01 取一个0~1之间的随机数(每个数概率相同)
Rand.direction2D 在[0,0]为圆心、半径1的圆上,随机取一个坐标点(每个坐标概率相同)
Rand.boolean 随机生成布尔值,即随机选true或false
Rand.normal 取一个-6~6之间的随机数(一维正态分布形式)
Rand.normal2D 取一个每项都在-6~6之间的随机二项数组(二维正态分布形式)
比如,我想让碰到的小球随机传送到四个顶点分别为[0,0],[0,8],[8,0],[8,8]的正方形区域内随机坐标,那就可以在oncollide里写e.other.pos= [8*rand.uniform01 ,8*rand.uniform01].
③实用进阶数学运算
有math.前缀不能省略!正弦和余弦虽然去掉math.也有效,但加上math.可以略微减轻卡顿。
math.sin (x) 正弦
math.cos (x) 余弦
math.tan (x) 正切
math.toInt (x) 取整(去尾法)
④更多数学运算(淘汰赛中很少用,想整花活的可以看一看)
math.sum ( ) 求和(括号里必须是一个数组)
math.sqrt (x) 开方
math.vec.len ([a,b]) 求一个二项数组对应的坐标点到[0,0]的长度,类似勾股定理,比如math.vec.len([3,4])等于5
math.log10 (x) 以10为底的对数
math.asin (x) 反正弦
math.acos (x) 反余弦
math.atan (x) 反正切
math.toFloat (x) 转换成浮点值(即有小数形式)
math.toString (x) 转换成纯文本
math.comp.eq(a,b) 比较两个值是否相同,如果相同,输出true;如果不同,输出false
其他比较函数同理:
math.comp.g(a,b) a是否大于b
math.comp.ge(a,b) a是否大于等于b
math.comp.l(a,b) a是否小于b
math.comp.le(a,b)a是否小于等于b
math.comp.ne(a,b)a是否不等于b
还有些函数我都没怎么用过,就不列出来了。
五、闪队代码,以及利用sim.time、system.time或sim.tick实现动态函数
sim.time和system.time都是自带的计时代码。
sim.time:表示这个场景启动后累计运行的秒数,初始为0,当你按下空格开始运行的时候,sim.time就持续计时;暂停运行时,sim.time也暂停增加.
system.time:类似于sim.time,但它是打开algodoo的时候就开始计时了,你暂停模拟的时候它照样在持续计时。
sim.tick:跟sim.time原理一样,只不过sim.tick始终是sim.time的60倍。
所有在持续变化的数值,都可以利用这三个动态数字实现。
个人习惯用sim.time,后面都用sim.time做例子,有时候用sim.tick。
你应该知道基础的函数吧,比如y=2x这种。

把x换成sim.time,y换成你想要动态变化的值,再把这段代码放在poststep或update里,就形成了动态函数。
比如在poststep里写:
(e)=>{density = 2 * sim.time}
这个东西的密度就按照y=2x函数的图像变化(从x=0开始),也就是运行1秒的时候密度是2,运行2秒的时候密度是4,运行114.514秒的时候密度是229.028.
这样一来,闪队颜色代码就可以实现了。
首先搞清楚这个颜色是怎么闪的。
比如黑白闪就是黑-暗影-灰-银-白-银-灰-暗影-黑-……无色循环往复变化,用colorHSVA表示的话,就是H等于0不变,S等于0不变,V在0和1之间循环往复,A等于1不变。
彩虹则是红-橙-黄-黄绿-酸橙-……色相0-360循环变化,用colorHSVA表示的话,就是H从0逐渐增加到360,然后又变回0再逐渐增加到360,如此循环,SVA都是1不变。
那么再找到按照这个规律变化的函数,于是我们找到,在0和1之间循环往复的正弦函数y=sin(x)/2 + 0.5,以及符合彩虹变化规律的取余函数y = x mod 360.
这样,把x换成sim.time,y换成algodoo里面对应的属性,然后个别数学符号换成algodoo的书写形式,就得到闪队代码了。
黑白闪:
colorHSVA = [0, 0, math.sin(sim.time ) / 2 + 0.5, 1]
彩虹:
colorHSVA = [sim.time % 360, 1, 1, 1]
写好之后测试一下,感觉闪得太快了或太慢了?给sim.time做个乘法或除法就行。
比如:
黑白闪colorHSVA = [0, 0, math.sin(sim.time * 6 ) / 2 + 0.5, 1]
彩虹colorHSVA = [270*sim.time % 360, 1, 1, 1]
至于乘多少或除以多少,自己看情况调整频率即可。
下面列出我自己使用的一些闪队代码,都可以写在poststep里或者update里。始终记住(e)=>{}的格式必须要有!
彩虹: colorHSVA = [270 * sim.time % 360, 1, 1, 1]
暗彩虹:colorHSVA = [270 * sim.time % 360, 1, 0.5, 1]
浅彩虹: colorHSVA = [270 * sim.time % 360, 0.5, 1, 1]
黑白闪: colorHSVA = [0, 0, math.sin(sim.time * 6) / 2 + 0.5, 1]
彩虹闪:colorHSVA = [270 * sim.time % 360, math.sin(sim.time * 6) * 0.5 + 0.5, math.cos(sim.time * 6) * 0.5 + 0.5, 1]
红黑闪:colorHSVA = [0, 1, math.sin(sim.time * 6) / 2 + 0.5, 1](其他黑闪,把色相的0改成对应数字就行)
青白闪: colorHSVA = [180, math.sin(sim.time * 6) / 2 + 0.5, 1, 1](其他白闪,把色相的180改成对应数字就行)
灰暗闪:colorHSVA = [0, 0, math.sin(sim.time * 8) / 5 + 0.4, 1]
随机色:
sim.tick % 24 == 0 ? {
sim.tick % 96 == 0 ? {
colorHSVA = [360 * rand.uniform01, 1, 1, 1]
} : {
colorHSVA = [360 * rand.uniform01, 0.7 * rand.uniform01 + 0.3, 0.7 * rand.uniform01 + 0.3, 1]
}
} : {}
渐变红:
math.sin(sim.time * 3) / 2 >= 0 ? {
colorHSVA = [0, 1, 1 - math.sin(sim.time * 3) / 2, 1]
} : {
colorHSVA = [0, 1 + math.sin(sim.time * 3) / 2, 1, 1]
}
(其他渐变单色,自己改色相就行)
更多闪色,知道它闪的规律,找到对应的函数就行了。
其他有规律变化的东西,都可以利用sim.time,system.time,sim.tick加上函数表达式来实现,发散思维,别被定式限制了。