extends CharacterBody2D ## 小怪:在平台上左右巡逻,并且按固定节奏发射弹幕。 ## **打不死** —— 从上方踩上去也一样会被扣血。 ## 弹道方向是写死的(不追着玩家走),所以每一波的弹道完全一样, ## 玩家可以背下空隙走位躲过去,或者用「扔钥匙 + W 传送」直接穿过去。 const BULLET := preload("res://scenes/prefabs/enemies/bullet.tscn") @export_category("巡逻") @export var speed: float = 28.0 ## 巡逻方向:-1 向左,1 向右 @export var start_direction: int = -1 ## 掉头后的冷却时间:防止贴着墙时每帧都掉头、结果原地抖动 @export var turn_cooldown: float = 0.25 ## 接触玩家扣几颗心 @export var damage: int = 1 ## 速度设成 0 就变成固定炮台(弹幕就完全静止可背板了) @export_category("弹幕") @export var fires_bullets: bool = true ## 发射间隔(秒) @export var fire_interval: float = 2.0 ## 开场后第一次发射前的延迟 @export var fire_delay: float = 1.0 ## 每次发射几颗 @export var bullet_count: int = 3 ## 扇面张开角度 @export var spread_deg: float = 60.0 ## 扇面中心朝向(度):0=右 90=下 180=左 270=上 @export var base_angle_deg: float = 0.0 ## 开火方向跟随巡逻朝向(打开时 base_angle_deg 只当上下偏移用) @export var aim_along_facing: bool = true @export var bullet_speed: float = 70.0 @export var bullet_color: Color = Color(1.0, 0.35, 0.7) var direction: int = -1 var _turn_timer: float = 0.0 var _fire_timer: float = 1.0 @onready var sprite: Sprite2D = $Sprite @onready var floor_probe: RayCast2D = $FloorProbe @onready var touch_area: Area2D = $TouchArea func _ready() -> void: add_to_group("enemy") direction = -1 if start_direction < 0 else 1 _apply_direction() _fire_timer = fire_delay touch_area.body_entered.connect(_on_touch) func _physics_process(delta: float) -> void: if _turn_timer > 0.0: _turn_timer -= delta if not is_on_floor(): velocity += get_gravity() * delta velocity.x = float(direction) * speed # 撞到墙 或 前方脚下没有地板 -> 掉头(带冷却,掉头后先走一段再说) if _turn_timer <= 0.0: if is_on_wall() or (is_on_floor() and not floor_probe.is_colliding()): direction = -direction _turn_timer = turn_cooldown _apply_direction() move_and_slide() if fires_bullets: _fire_timer -= delta if _fire_timer <= 0.0: _fire_timer = maxf(fire_interval, 0.05) fire() ## 按固定扇面发射一批子弹。方向是算死的,不参考玩家位置, ## 所以每一波的弹道都一样,玩家能背下来找空隙。 func fire() -> void: var n: int = maxi(bullet_count, 1) var base_deg: float = base_angle_deg if aim_along_facing: base_deg = 0.0 if direction > 0 else 180.0 var base := deg_to_rad(base_deg) var spread := deg_to_rad(spread_deg) for i in n: var t: float = 0.0 if n == 1 else (float(i) / float(n - 1) - 0.5) var ang := base + spread * t var bullet := BULLET.instantiate() # 这两个属性必须在 add_child 之前设好,子弹的 _ready 会拿它们生成贴图 bullet.bullet_color = bullet_color bullet.speed = bullet_speed get_parent().add_child(bullet) bullet.global_position = global_position + Vector2(0.0, -2.0) bullet.launch(Vector2(cos(ang), sin(ang)), bullet_speed) func _apply_direction() -> void: if sprite != null: sprite.flip_h = direction > 0 if floor_probe != null: floor_probe.position.x = float(direction) * 6.0 func _on_touch(body: Node2D) -> void: # 打不死:踩上去也一样只是扣血 if body.has_method("hurt"): body.hurt(damage)