extends Node2D ## 固定弹幕源:每隔一段时间以自己为中心,按**固定角度**围一圈发射子弹。 ## 每一波的角度都算死(可选每波整体旋转),所以弹道可以背下来, ## 玩家靠走位找空隙,或者用「扔钥匙 + W 传送」直接穿过去。 const BULLET := preload("res://scenes/prefabs/enemies/bullet.tscn") const BULLET_SCRIPT := preload("res://scripts/bullet.gd") @export var enabled: bool = true ## 发射间隔(秒) @export var interval: float = 2.0 ## 开场后第一次发射前的延迟 @export var start_delay: float = 1.0 ## 每波几颗 @export var count: int = 12 ## 扇形张开角度:360 = 围一整圈 @export var arc_deg: float = 360.0 ## 整圈的中心朝向(度):0=右 90=下 180=左 270=上 @export var base_angle_deg: float = 0.0 ## 每发一波整体旋转多少度(0 = 弹道完全固定,可以背板) @export var rotate_per_shot_deg: float = 0.0 @export var bullet_speed: float = 55.0 @export var bullet_color: Color = Color(1.0, 0.45, 0.85) ## 中心的圆点半径(只是给玩家一个视觉提示) @export var core_radius: int = 4 var _timer: float = 0.0 var _angle: float = 0.0 func _ready() -> void: add_to_group("emitter") _timer = start_delay _angle = base_angle_deg var core := get_node_or_null("Core") as Sprite2D if core != null: core.texture = BULLET_SCRIPT.circle_texture(core_radius, bullet_color) func _physics_process(delta: float) -> void: if not enabled: return _timer -= delta if _timer > 0.0: return _timer = maxf(interval, 0.05) fire() func fire() -> void: var n: int = maxi(count, 1) var base := deg_to_rad(_angle) var full_circle := is_equal_approx(arc_deg, 360.0) var step := deg_to_rad(arc_deg) / float(n) if full_circle else deg_to_rad(arc_deg) for i in n: var ang: float if full_circle: ang = base + step * float(i) elif n <= 1: ang = base else: ang = base + step * (float(i) / float(n - 1) - 0.5) _spawn(ang) if not is_zero_approx(rotate_per_shot_deg): _angle = fmod(_angle + rotate_per_shot_deg, 360.0) func _spawn(ang: float) -> void: 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 bullet.launch(Vector2(cos(ang), sin(ang)), bullet_speed)