继续我们未完成的内容:**1.自动生成敌人并寻路 2.炮台多种功能等
1.自动生成及寻路功能
我们如果想要敌人按照我们的规划路径移动,首先肯定得先有个路径。那么新建个path2d,随便先画下路线,然后我们需要有个敌人生成器,新建场景-node2d-添加timer(用于计算倒计时结束出现敌人),再实例化至场景内。新建脚本。

在这个脚本内我们需要写什么呢,我们要实现的功能点为1.获取路径以及每个路径点的位置 2.生成敌人并给予位置目标
ok,我们先来获取路径及点位位置,利用@onready来获取path2d,并声明curve(描述 2D 空间的贝塞尔曲线,也就是path2d的形状),再声明个vector2的数组来储存各个点位的位置,这里用到的是PackedVector2Array()(专门设计用于存放vector2的数组),再通过for循环来实现遍历每个节点,get_point_position()来获取点位位置,最后再利用.to_global将其转为全局坐标。
1 | extends Node2D |
接下来就是生成敌人的函数了,新建个函数用于实现功能,首先就是实例化我们的敌人,帮将其放入场景内。但是这里的points仅在敌人生成器中有效,敌人脚本中还没有这个points,因此肯定无法实现我们的功能,因此打开我们的enemy脚本(PS:由于我们不需要拖拽进行测试了,因此将相关代码全部注释或删掉,并将类型改为CharacterBody2D,由于我们之前检测进入信号中用的是area_entered,因此也需要将area修改为body)
1 | func spawn_enemy(): |
首先我们需要获取到我们的敌人生成器中获取的点位位置,因此我们在这里也需要新建PackedVector2Array(),以便之后在敌人生成器脚本中调用,再将我们的enemy的全局坐标设置为获取点位的全局坐标,默认以第一个点位为目标。
1 | extends CharacterBody2D |
接下来就是让他移动了,我们想让敌人的位置距离第一个点位<1时,将目标设为下一个点位置,并需要利用的是(distance_to :返回两个向量之间的距离),同时将velocity的值设为两个向量差值*速度,再move_and_slide()即可,但是两个向量的差值会随着移动而改变,这会导致我们的速度是先快后慢的,因此我们还需要用到一个normalized()来将向量缩放至单位长度,这时就可以保证我们的速度一致性。
1 | func _physics_process(_delta): |
最后在enemy脚本中的spawn_enemy()函数调用points:enemy.points = points,这样就可以实现了第一个点到第二个点的移动

从这里看到我们实现了敌人沿着路径点移动了,接下来只要声明个路径点组的序号,并让他累加1即可,但是目前还有几个问题没有解决,第一个是敌人移动到终点不会消失,这肯定步行,我们还需要让他到终点时消失。第二个是敌人数量没有限制,我们需要作出限制。
1 | func _physics_process(delta): |
当序列到最后一个时,让它消失即可。同时在敌人生成器脚本中,增加敌人数量的变量,并每生成一个就-1,当为0时则不执行生成函数。
1 | @export var enemy_amount = 10 |

这样就可以完成我们的敌人生成并寻路的功能。
2.其他炮台功能
仅有这两个炮台太单调了,我们再增加个可以减速功能的冰冻塔。首先还是像我们上一篇一样,将炮台新建继承场景。
再拓展我们的炮台代码,首先我希望敌人进入区域时,速度可以/2,并且颜色变为蓝色。离开区域时速度恢复且颜色恢复(这里拓展下,如果我们在拓展函数内使用同名函数增加内容,那么就是对函数的覆盖。如果我想要这个函数下的功能,那么直接复制代码可以吗?这样也是可以的,但是会有两段重复代码,如果你想要修改的话就要两个一起修改,这里最好用super()去调用我们正在覆盖的函数的父函数)
1 | func _area_entered(body: PhysicsBody2D): |
同时_ready()内也可以利用super()来调用,但是父函数有个发射函数怎么办呢,我们只需将发射函数用return覆盖即可。同时增加让他不能转动的函数。
1 | func _ready() -> void: |

最后我们就可以实现我们预期的功能了!