下面我们来实现子弹击中敌人的判定,以及实现敌人血量机制。
在初级篇教程中,我们通过使用“碰到...”来判断是否击中敌人。
但是使用这个积木有几个缺点:
- 如果要在滚动大地图中判断子弹击中敌人,当子弹移到舞台外时,这个积木就会失效。
- 这个积木只能判断子弹碰到了敌人,但是没法确定子弹究竟碰到了哪个敌人。
为了解决这些问题,我们不再使用“碰到敌人”来进行子弹击中判定,而是通过子弹坐标到敌人坐标的距离来进行判断:
- 使用列表来存放每个敌人的坐标信息和血量信息。
- 子弹通过查找列表,计算到每个敌人的距离,如果小于一定值就判断为击中,同时将对应敌人的血量在列表中的值减少。这样,我们不仅可以解决屏幕外的碰撞检测问题,还可以实现精确判断碰到哪个克隆体问题。
让敌人上传坐标信息
- 首先,在左侧积木栏中,点击“建立一个列表”,依次创建 3 个列表:敌人x,敌人y,敌人血量。
- 新建私有变量“敌人编号”,然后为敌人编写下面的代码:
- 点击绿旗时,清空 3 个列表的内容,将“敌人编号”设为 0。
- 当克隆敌人时,将敌人编号增加 1,并向三个列表添加内容。(注:每次克隆敌人时,敌人编号 +1,克隆时,私有变量会复制一份到克隆体,从而实现了给敌人编号)
- 然后,敌人每帧将自己的坐标信息更新到列表中。
这样,我们就将敌人的信息存到了列表中。每个列表第一项存的是 1 号敌人信息,第二项存的是 2 号敌人信息,以此类推。例如,通过列表可知 3 号敌人的坐标是(359.77, -238.15):
为子弹编写击中判定代码
- 在子弹中,编写如下积木(勾选“运行时不刷新屏幕”,这样积木可以瞬间执行完毕):
- 接着,编写子弹的击中判定代码:
- 新建私有变量“击中敌人编号”,开始时设为空内容,表示没有击中敌人。
- 新建私有变量“i”,用来遍历敌人:
- 开始时将 i 设为 0;
- 重复执行,有多少敌人就执行多少次;
- 每次循环开始时,将 i 增加 1。这样每次循环 i 依次取 1、2、3……;
- 判断第 i 个敌人的血量,如果大于 0 才执行里面的动作(子弹不会击中死亡的敌人)
- 首先,判断子弹 x 到敌人的 x 的差是否<28;
- 接着,判断子弹 y > 敌人y - 10 ,且子弹y < 敌人y + 50;
- 接着,为子弹编写击中敌人的判定:
- 每帧检测是否击中敌人,如果得到“击中敌人编号”不是空(击中了敌人),就退出循环;
- 如果击中了敌人,就将对应敌人的血量减少 40(修改“敌人血量”列表中,对应敌人的数据)
c. 接下来,使用下面的代码,判断子弹是否击中了第 i 个敌人:
这样,当子弹坐标落入下面的矩形时,就判断为击中了敌人:(28 为横向半径,50 为到头顶的高度,10为到底部的高度,这些值可以大致设置,然后根据实际效果调整)
最终代码如下:依次遍历所有敌人,如果敌人血量>0,且子弹击中敌人(在判定矩形内),就将“击中敌人编号”设为 i,然后退出自制积木。
这样就实现了自制积木的功能:检测子弹击中的敌人,如果击中了,返回敌人的编号,如果未击中,返回空值,从而实现了子弹精确判断到底击中了哪个敌人。
这样,我们就实现了子弹的击中敌人判定,并给敌人扣血。由于是通过坐标来判断,当子弹在屏幕外时仍能正确击中敌人;而且,获取了被击中敌人的编号,精确地确定了子弹究竟击中了哪个克隆体。
为敌人编写死亡代码
为敌人增加如下代码,当敌人血量归 0 时,切换死亡造型,等待 0.5 秒后渐渐消失,删除自己。
我们可以给敌人添加受击反馈:检测到自己血量减少时,敌人变暗。
- 首先,新建一个私有变量,命名为“上一刻血量”。
- 然后,为敌人编写如下代码:
- 用“上一刻血量”保存敌人上一帧的血量值;
- 如果上一帧血量比这一帧的血量大,说明敌人扣血,此时设置短暂的变暗效果。
这样,我们就实现了子弹击中敌人的效果,以及敌人血量机制!
优化
由于我们没有删除死亡的敌人信息,敌人的信息列表会越来越长。假设列表中记录了 102 个敌人的信息,但实际上只有 10 个存活的敌人,剩下 92 个敌人已经死亡。这样实际有用的信息只有 10 条,剩下的 92 个信息是多余的。
而我们发射的子弹,每帧都会遍历一次敌人列表,假设列表长度有 102 个敌人,由于只有 10 个存活的敌人,剩下的 92 个死亡敌人被跳过了,这非常浪费计算量。
因此我们可以再创建一个列表,命名为“存活敌人列表”,记录存活敌人的编号,这样子弹遍历时,只需要遍历存活的 10 个敌人,而不需要处理 102 个敌人这么多,减少计算量。
- 创建一个列表,命名为“存活敌人列表”。开始时清空这个列表,当敌人生成时,将敌人编号加入“存活敌人列表”
- 当敌人死亡时,从存活列表中删除自己的信息:
- 将子弹中的代码进行如下修改:
- 遍历存活列表,新建私有变量“当前遍历敌人”,用于存放存活列表中,第 i 个敌人的编号。
- 然后,将代码中的 “i” 替换为 “当前遍历敌人”。
这样,子弹在检查敌人列表时,只用检查存活的敌人,而跳过了死亡敌人的信息。
我们假设,现在有 10 个敌人,他们的编号为 1~10,信息存放在列表中。
假设 6 号敌人死亡,我们删除他的信息:
删除后,原来 7~10 号的信息,移到了 6~9 号位置,发生了信息错位!
- 例如,7 号克隆体的血量本来是 60,信息错位后, 7 号读到的数据变成了 100(读取到了原来 8 号克隆体的数据)
因此,像这样直接删除克隆体信息是不行的。
一个解决错位问题的办法是,再建立一个列表“敌人编号”:
当信息删除时,虽然信息前移了,但是敌人编号和敌人数据的对应关系没有改变:
然后,代码需要做如下修改。
这是一种可行的办法。优点是,无用克隆体信息会及时删除,避免冗余信息堆积在列表中。但会让代码编写稍微麻烦一点。而且,每一帧都要查找“敌人编号”在列表的位置,会稍微增大一点计算量。因此本教程不考虑这个方法。
<iframe
width="100%"
height="800px"
scrolling="no"
src="https://www.ccw.site/embed?id=STG202/Arkos/Lec4/03&type=comment"
title="{射击4-让子弹击中敌人}"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
scrolling="0"
></iframe>