Files
sijcdu defdf202e6 Bullet field: pierce-through and damaging, rings spaced 3 cells apart
Solid bullets were blocking the patrolling enemies, so the field now defaults to solid = false: each bullet is a plain StaticBody2D with no collision layer, plus an Area2D that only detects the player, so nothing is physically obstructed while contact still costs a heart. Ring radii changed from [5, 10, 15] to [3, 6, 9]. Damage is now applied every frame to whatever is overlapping, not only on body_entered, so standing inside a bullet keeps hurting instead of only the first touch. Also picks up the editor change that removed Walker_03.
2026-10-07 22:38:07 +08:00

143 lines
4.3 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends Node2D
## 静态弹幕障碍:以自己为圆心,一圈一圈地摆上**固定不动、不会发射**的子弹。
##
## 两种形态:
## - solid = true :实心障碍,挡路、能踩上去(注意:会连小怪一起挡住)
## - solid = false:可以穿透,只做伤害判定 —— 配合 damage > 0 就是普通弹幕
##
## 落在实心地砖里、或跑出关卡范围的子弹会自动跳过。
const BULLET_SCRIPT := preload("res://scripts/bullet.gd")
@export var enabled: bool = true
## 每一圈的半径(单位:格)。默认 3 格、6 格、9 格
@export var radii_cells: Array[int] = [3, 6, 9]
## 每圈摆几颗
@export var per_ring: int = 20
## 一格多少像素(图块是 18)
@export var cell_size: float = 18.0
## 起始角度偏移(度),用来微调子弹摆在哪个方向
@export var angle_offset_deg: float = 0.0
## 子弹半径
@export var bullet_radius: int = 3
@export var bullet_color: Color = Color(1.0, 0.45, 0.85)
## 实心障碍:挡路、能踩上去。关掉的话就是可以穿透的纯弹幕
@export var solid: bool = false
## 碰到扣几颗心。0 = 不扣血
@export var damage: int = 1
## 超出这个范围的子弹不生成(默认整个关卡 468x270)
@export var bounds: Rect2 = Rect2(0, 0, 468, 270)
## 最多铺几圈
@export var max_rings: int = 3
## 跳过埋在地砖里的子弹
@export var skip_solid_ground: bool = true
var made: int = 0
var skipped_out: int = 0
var skipped_ground: int = 0
var _hurt_areas: Array = []
func _ready() -> void:
if not enabled:
return
# 等一帧,确保 TileMapLayer 都已经就绪
_build.call_deferred()
func _build() -> void:
var layers := _tile_layers()
var tex := BULLET_SCRIPT.circle_texture(bullet_radius, bullet_color)
made = 0
skipped_out = 0
skipped_ground = 0
_hurt_areas.clear()
for i in mini(radii_cells.size(), max_rings):
var radius: float = float(radii_cells[i]) * cell_size
for k in per_ring:
var ang := deg_to_rad(angle_offset_deg + 360.0 * float(k) / float(per_ring))
var pos := global_position + Vector2(cos(ang), sin(ang)) * radius
if not bounds.has_point(pos):
skipped_out += 1
continue
if skip_solid_ground and _in_solid(layers, pos):
skipped_ground += 1
continue
_make(pos, tex)
made += 1
## 每帧检查一次:站在子弹里也会持续扣血
## (不能只靠 body_entered —— 玩家本来就在子弹里的话不会触发)
func _physics_process(_delta: float) -> void:
if damage <= 0 or _hurt_areas.is_empty():
return
for area in _hurt_areas:
if not is_instance_valid(area):
continue
for body in area.get_overlapping_bodies():
if body.has_method("hurt"):
body.hurt(damage)
func _tile_layers() -> Array:
var root: Node = owner if owner != null else get_tree().current_scene
if root == null:
return []
return root.find_children("*", "TileMapLayer", true, false)
## 这个点是不是埋在实心地砖里
func _in_solid(layers: Array, pos: Vector2) -> bool:
for l in layers:
var cell: Vector2i = l.local_to_map(l.to_local(pos))
var sid: int = l.get_cell_source_id(cell)
if sid == -1:
continue
var source := l.tile_set.get_source(sid) as TileSetAtlasSource
if source == null:
continue
var data: TileData = source.get_tile_data(l.get_cell_atlas_coords(cell), 0)
if data != null and data.get_collision_polygons_count(0) > 0:
return true
return false
func _make(pos: Vector2, tex: Texture2D) -> void:
var body := StaticBody2D.new()
if solid:
body.collision_layer = 1
body.collision_mask = 1
else:
# 不实心:谁都挡不住,只靠下面的伤害区域起作用
body.collision_layer = 0
body.collision_mask = 0
add_child(body)
body.global_position = pos
var sprite := Sprite2D.new()
sprite.texture = tex
sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
body.add_child(sprite)
if solid:
var shape := CollisionShape2D.new()
var circle := CircleShape2D.new()
circle.radius = float(bullet_radius)
shape.shape = circle
body.add_child(shape)
if damage > 0:
var area := Area2D.new()
area.collision_layer = 4
area.collision_mask = 2
var hit := CollisionShape2D.new()
var circle2 := CircleShape2D.new()
circle2.radius = float(bullet_radius) + 1.0
hit.shape = circle2
area.add_child(hit)
body.add_child(area)
_hurt_areas.append(area)