Files
sijcdusmode/scripts/bullet_field.gd
T
sijcdu 35174b67ab Replace the firing bullet emitter with static bullet obstacles ringed around (12,12)
The user did not want anything firing: the bullets should simply exist in the air as fixed obstacles. bullet_ring (the emitter) is deleted and bullet_field takes its place. bullet_field generates concentric rings of frozen bullets centred on itself, radii given in cells (default 5/10/15), and skips bullets that fall outside the level bounds or land inside solid tiles so nothing is buried in the terrain. Each bullet is a StaticBody2D on the world layer, so it blocks the player and can be stood on; damage defaults to 0 (pure obstacle) and can be raised to turn them into hazards.
2026-10-07 22:29:12 +08:00

127 lines
3.7 KiB
GDScript
Raw 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
## 静态弹幕障碍:以自己为圆心,一圈一圈地摆上**固定不动、不会发射**的子弹。
## 默认只是纯障碍(实心,撞得到、能踩上去),把 damage 调大于 0 就变成碰到扣血。
## 落在实心地砖里的子弹会自动跳过(不然会埋在地里)。
const BULLET_SCRIPT := preload("res://scripts/bullet.gd")
@export var enabled: bool = true
## 每一圈的半径(单位:格)。默认 5 格、10 格、15 格
@export var radii_cells: Array[int] = [5, 10, 15]
## 每圈摆几颗
@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 = true
## 碰到扣几颗心。0 = 只是障碍不扣血
@export var damage: int = 0
## 超出这个范围的子弹不生成(默认整个关卡 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
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
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
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)
area.body_entered.connect(_on_hit)
func _on_hit(body: Node2D) -> void:
if body.has_method("hurt"):
body.hurt(damage)