第28章:程序:戳气泡小游戏
ξ 28.1 需求描述
屏幕上随机生成一组气泡,可以设置数量;
气泡随机飘动,颜色随机变幻;
用户移动鼠标戳气泡,戳中则气泡消失;
提示用户气泡剩余数;
所有气泡都戳破后,显示所用时间。
ξ 28.2 设计思路
分解:数据初始化,随机飘移,随机变色,戳气泡,显示完成时间
连接:初始化数据;定时飘移气泡、定时修改颜色、鼠标事件中处理戳气泡逻辑;显示所用时间。
递归:
数据初始化:气泡数、气泡大小、舞台大小,状态数据,设置气泡初始位置和飘移方向
随机飘移——分解:擦除气泡图案,新位置重绘气泡图案
——连接:先擦除气泡图案,再在新位置重绘气泡图案
——递归:
擦除:画笔设为擦除模式,原位置重绘,即擦除
重绘——分解:设置飘移偏移量,检查边界,画气泡
——连接:先生成偏移量,再检查边界修正,最后画气泡
——递归:
设置飘移偏移量:在飘移方向上随机生成偏移量
检查边界:检查新位置是否处于舞台边缘,或与其它气泡相遇,若是则反向飘移。
画气泡:在新位置画气泡图形,即圆形
随机改变颜色:定时器随机修改画笔颜色
戳气泡——分解:戳中气泡,擦除气泡,输出剩余气泡数
——连接:判断是否戳中,若是则擦除气泡,并输出剩余气泡数,否则不做处理
——递归:
戳中气泡:判断当前位置到各个气泡图形中心点的距离,小于半径为戳中
擦除气泡:设置画笔为擦除模式,此时画气泡即为擦除
输出剩余气泡数:剩余气泡数减一,输出文本
显示完成时间——分解:输出完成时间,释放资源
——连接:先在屏幕上输出完成时间,再释放资源
——递归:
输出完成时间:程序从开始到戳破所有气泡两次记录毫秒数之差,屏幕输出
释放资源:关闭各个定时器,停止捕获鼠标事件
ξ 28.3 程序设计
定义两段程序
程序chuo_qipao:
主程序
参数shuliang,表示气泡总数
程序qipao_juli:
计算到气泡的距离
参数weizhi_yi, 表示当前位置
参数weizhi_er,表示第二个位置
参数qiu_banjing,表示气泡的半径
ξ 28.4 编程实现
代码
to chuo_qipao :shuliang ;戳气泡
;参数shuliang:气泡数量
if :shuliang < 2 [ make "shuliang 2 ] ;数量最少是2
if :shuliang > 10 [ make "shuliang 10 ] ;数量最多是10
make "daxiao 25 ;气泡半径大小25像素
;舞台大小400像素,表示横轴(-400,400),纵轴(-400,400)
make "wutai_daxiao 400
;气泡数组,元素是气泡状态,气泡状态也是数组,有5个元素
make "qipao_zu (array :shuliang)
;下面的循环语句为每个气泡状态设置初值
for [ i 1 :shuliang 1 ] [
;单双数序号的气泡,状态值中的飘浮方向不一样
test (remainder :i 2) = 0
;气泡状态,有5个元素,
;按顺序分别是:横坐标、纵坐标、
;竖直飘移方向(1:向上,0:向下)、
;水平飘移方向(1:向右,0:向左)、
;是否已删除(1:是,0:否)。
ift [ setitem :i :qipao_zu { 0 0 1 1 0 } ]
iff [ setitem :i :qipao_zu { 0 0 -1 -1 0 } ]
]
make "kaishi_shijian timemilli ;开始时间毫秒数
;当前时间毫秒数,这个变量会在后面的代码中随着时间修改
make "dangqian_shijian timemilli
setpensize [ 1 1 ] ;设置画笔宽度为1
make "yanse 0 ;画笔颜色,会在定时器代码中随机修改
make "yanse_zu { 0 1 2 3 4 5 6 8 9 10 11 12 13 14 15 } ;画笔颜色数组
;定时器,ID是1,间隔时间是1秒,用于动态改变画笔颜色
settimer 1 60 [
;在颜色数组中随机选择一种颜色
make "yanse (item (random (count :yanse_zu)) + 1 :yanse_zu)
;更新当前时间变量的值
make "dangqian_shijian timemilli
]
;舞台上剩余的气泡数量
make "shengyu_shu :shuliang
;循环检查时,上次的舞台剩余气泡数
make "shengyu_shu_shangci :shuliang
make "x 0 ;气泡中心点的x坐标
make "y 0 ;气泡中心点的y坐标
make "x_pianyi 0 ;气泡下个位置的x坐标轴的偏移
make "y_pianyi 0 ;气泡下个位置的y坐标轴的偏移
;气泡下个位置在x坐标轴偏移的方向,1就正向,-1是反向
make "x_biaozhi 0
;气泡下个位置在y坐标轴偏移的方向,1就正向,-1是反向
make "y_biaozhi 0
make "shanchu 0 ;删除标值,1:已删除;0:未删除
;是否已为气泡组中所有气泡生成初始位置的值
make "fenbu 0
make "juli_dong 0 ;气泡移动时计算与其它气泡的距离
make "shu_weizhi [ 0 0 ] ;鼠标的位置
make "juli_shu 0 ;气泡移动时计算与气泡的距离
make "qipao_shu [] ;当前气泡,用于处理鼠标事件
;定义鼠标事件,在按下鼠标左键时,
;判断是不是点在气泡上,
;若是,则将气泡的删除标识设置为1
mouseon [
make "shu_weizhi mousepos ;鼠标位置
;循环处理每个气泡
for [ i 1 :shuliang 1 ] [
make "qipao_shu (item :i :qipao_zu) ;当前气泡
;调用qipao_juli程序,获取当前气泡到鼠标位置的距离
make "juli_shu (qipao_juli :qipao_shu :shu_weizhi :daxiao) ;如果距离小于气泡的半径,则认为戳中了气泡
if :juli_shu < :daxiao [
;则将气泡的删除标识设置为1
setitem 5 :qipao_shu 1
]
]
] [ ] [ ] [ ] [ ]
ht ;隐藏箭头
make "zhuangtai_biaoqian [] ;游戏进度状态标签
;当前气泡项的状态值,用于处理气泡移动
make "qipao_dong []
while [ :shengyu_shu > 0 ] [
make "shengyu_shu 0 ;剩余气泡数
;循环处理每个气泡
for [ i 1 :shuliang 1 ] [
pu ;抬笔
;移动到当前气泡中心位置
setxy (item 1 (item :i :qipao_zu)) (item 2 (item :i :qipao_zu))
pe ;设置画笔为擦除状态
pd ;落笔
circle :daxiao ;擦除气泡
;取出气泡的删除标识
make "shanchu (item 5 (item :i :qipao_zu))
;若删除标识为0,表示气泡未删除,
;要在新位置重绘,
;否则表示该气泡已删除,不用再画
if :shanchu = 0 [
;重新统计剩余气泡数量
make "shengyu_shu :shengyu_shu + 1
;取出横轴偏移方向
make "x_biaozhi (item 3 (item :i :qipao_zu))
;取出纵轴偏移方向
make "y_biaozhi (item 4 (item :i :qipao_zu))
test :fenbu = 0 ;判断是否已生成初始位置
;若还未生成,则在此处生成
ift [
;横坐标,舞台大小范围的随机数
make "x (random :wutai_daxiao * 2) - :wutai_daxiao
;纵坐标
make "y (random :wutai_daxiao * 2) - :wutai_daxiao
]
;若已生成,则动态生成偏移量,气泡移动位置
iff [
make "x_pianyi (random 10) ;x轴偏移量
make "y_pianyi (random 10) ;y轴偏移量 ;取出x坐标位置
make "x (item 1 (item :i :qipao_zu))
;修改x坐标,即加上偏移量
make "x :x + :x_pianyi * :x_biaozhi
;取出y坐标位置
make "y (item 2 (item :i :qipao_zu))
;修改y坐标,即加上偏移量
make "y :y + :y_pianyi * :y_biaozhi
;x值超出舞台右边缘、
;或超出舞台左边缘,则修改气泡移动方向
if or (:x > :wutai_daxiao - :daxiao/2) (:x < -:wutai_daxiao + :daxiao/2) [
;偏移方向,乘-1即反向
make "x_biaozhi (:x_biaozhi * (-1))
;校正超出的位置
ifelse :x > :wutai_daxiao - :daxiao/2 [
make "x :wutai_daxiao - :daxiao/2
] [
make "x -:wutai_daxiao + :daxiao/2
]
]
;y值超出舞台上边缘、
;或超出舞台下边缘,则修改气泡移动方向
if or (:y > :wutai_daxiao - :daxiao/2) (:y < -:wutai_daxiao + :daxiao/2) [
;偏移方向,乘-1即反向
make "y_biaozhi (:y_biaozhi * (-1))
;校正超出的位置
ifelse :y > :wutai_daxiao - :daxiao/2 [
make "y :wutai_daxiao - :daxiao/2
] [
make "y -:wutai_daxiao + :daxiao/2
]
]
;在气泡相遇时,做出反弹的效果,
;逐一判断是否与其它气泡相遇
for [ j 1 :shuliang 1 ] [
if not (:i = :j) [
;排除当前气泡自身
;排除已做删除标记的气泡
if (item 5 (item :j :qipao_zu)) = 0 [
;调用qipao_juli程序,
;获取当前气泡到其它气泡的距离
make "juli_dong (qipao_juli (list :x :y) (item :j :qipao_zu) :daxiao)
;如果距离小于气泡半径加10像素的值,则认为两个气泡相遇
if (:juli_dong < :daxiao * 2 + 10) [
;将当前气泡x轴偏移方向反向
make "x_biaozhi (:x_biaozhi * (-1))
;将当前气泡y轴偏移方向反向
make "y_biaozhi (:y_biaozhi * (-1))
]
]
]
]
]
;重置当前气泡状态数据
make "qipao_dong (array 5)
setitem 1 :qipao_dong :x ;横坐标
setitem 2 :qipao_dong :y ;纵坐标
;横轴偏移方向
setitem 3 :qipao_dong :x_biaozhi
;纵轴偏移方向
setitem 4 :qipao_dong :y_biaozhi
setitem 5 :qipao_dong :shanchu ;删除标记
;修改当前气泡状态数据
setitem :i :qipao_zu :qipao_dong
ppt ;设置画笔为绘画状态
setpc :yanse ;设置画笔颜色
pu ;抬笔
;移动到新位置
setxy (item 1 (item :i :qipao_zu)) (item 2 (item :i :qipao_zu))
pd ;落笔
circle :daxiao ;在新位置画气泡
]
]
make "fenbu 1 ;修改已初始化气泡位置变量值为1
wait 1 ;延时六十分之一秒
;如果前次剩余气泡数与当前不相等,即又戳中了新气泡
if not (:shengyu_shu_shangci = :shengyu_shu) [
if :shengyu_shu > 0 [;如果当前剩余气泡数大于0
pu ;抬笔
;移动位置到舞台右侧中间位置
setpos (list :wutai_daxiao :shengyu_shu * 20)
seth 90 ;设置方向为水平向右
;要显示的文本
make "zhuangtai_biaoqian (word "还有 :shengyu_shu "个)
ppt ;设置画笔为绘画状态
;在舞台右侧中间位置显示剩余气泡数
label :zhuangtai_biaoqian
]
]
;修改剩余气泡数变量的值
make "shengyu_shu_shangci :shengyu_shu
]
mouseoff ;关闭鼠标事件
cleartimer 1 ;关闭定时器
ppt ;设置画笔为绘画状态
pu ;抬笔
setxy 0 0 ;移动到舞台中央
pd ;落笔
setpensize [ 3 3 ] ;设置画笔宽度
seth 90 ;设置方向为水平向右
;显示游戏完成所用时间
label (word "完成!用时 ((:dangqian_shijian - :kaishi_shijian) / 1000) "秒)
setpensize [ 1 1 ] ;恢复画笔宽度
end
to qipao_juli :weizhi_yi :weizhi_er :qiu_banjing
;计算两个位置间的距离
;参数weizhi_yi:第一个位置
;参数weizhi_er:第二个位置
;参数qiu_banjing:气泡的半径
;取出第二个位置的x坐标值
make "x_shu (item 1 :weizhi_er)
;取出第二个位置的y坐标值
make "y_shu (item 2 :weizhi_er)
;取出第一个位置的x坐标值
make "x_dangqian (item 1 :weizhi_yi)
;取出第一个位置的y坐标值
make "y_dangqian (item 2 :weizhi_yi)
;调用平方差公式,计算距离,保存到jieguo变量
make "jieguo (sqrt ((:x_shu - :x_dangqian) * (:x_shu - :x_dangqian) + (:y_shu - :y_dangqian) * (:y_shu - :y_dangqian)))
op :jieguo ;返回计算结果
end
运行程序
chuo_qipao 8
运行效果
下一篇
附录:指令速查索引