extends Area2D ## 弹幕子弹:直线飞行,碰到玩家扣血。 ## 贴图是代码生成的实心圆,不需要额外美术素材。 @export var speed: float = 70.0 @export var damage: int = 1 @export var lifetime: float = 8.0 @export var radius: int = 3 @export var bullet_color: Color = Color(1.0, 0.35, 0.7) ## 撞到地形/绳子之类的障碍就消失。 ## 关掉(默认)的话子弹会**穿过一切**,只靠存活时间和出界来回收 @export var stop_on_obstacle: bool = false ## 掉到这个世界 y 以下就自动回收(默认在关卡底部之外) @export var despawn_y: float = 340.0 var direction: Vector2 = Vector2.LEFT var _age: float = 0.0 var _probe: RayCast2D = null static var _cache: Dictionary = {} ## 生成(并缓存)一个实心圆贴图 static func circle_texture(r: int, color: Color) -> ImageTexture: var key := "%d|%s" % [r, color.to_html()] if _cache.has(key): return _cache[key] var size: int = r * 2 + 1 var img := Image.create_empty(size, size, false, Image.FORMAT_RGBA8) img.fill(Color(0.0, 0.0, 0.0, 0.0)) var limit := float(r * r) + float(r) * 0.6 for y in size: for x in size: var dx := float(x - r) var dy := float(y - r) if dx * dx + dy * dy <= limit: img.set_pixel(x, y, color) var tex := ImageTexture.create_from_image(img) _cache[key] = tex return tex func _ready() -> void: add_to_group("bullet") var sprite := get_node_or_null("Sprite") as Sprite2D if sprite != null: sprite.texture = circle_texture(radius, bullet_color) var hit := get_node_or_null("HitBox") as CollisionShape2D if hit != null and hit.shape is CircleShape2D: var circle := (hit.shape as CircleShape2D).duplicate() as CircleShape2D circle.radius = float(radius) hit.shape = circle _probe = get_node_or_null("Probe") as RayCast2D if _probe != null: _probe.enabled = stop_on_obstacle body_entered.connect(_on_body_entered) func launch(dir: Vector2, spd: float = -1.0) -> void: direction = dir.normalized() if spd > 0.0: speed = spd func _physics_process(delta: float) -> void: _age += delta if _age >= lifetime or global_position.y > despawn_y: queue_free() return var step := direction * speed * delta if stop_on_obstacle and _probe != null: _probe.target_position = step * 1.5 _probe.force_raycast_update() if _probe.is_colliding(): queue_free() return position += step func _on_body_entered(body: Node2D) -> void: if body.has_method("hurt"): body.hurt(damage) queue_free()