## EnemyBulletManager — 敌方子弹管理器 Autoload(伤害玩家) ## 平行于 BulletManager(玩家子弹 vs 敌人);本池每帧检测 vs 玩家。 ## SoA 布局(ENEMY_BULLET_STRIDE=7):[ x, y, vx, vy, lifetime, radius, damage ] ## 0 1 2 3 4 5 6 extends Node const ENEMY_BULLET_STRIDE: int = 7 # 禁裸整数 const MAX_ENEMY_BULLETS: int = 1024 const PLAYER_HIT_RADIUS: float = 12.0 # 玩家碰撞半径估算(与玩家标记视觉一致) var _data: PackedFloat32Array = PackedFloat32Array() var _active_count: int = 0 func _ready() -> void: _data.resize(MAX_ENEMY_BULLETS * ENEMY_BULLET_STRIDE) _data.fill(0.0) func _physics_process(delta: float) -> void: var player: Vector2 = PlayerManager.get_position() var i: int = 0 while i < _active_count: var base: int = i * ENEMY_BULLET_STRIDE _data[base + 0] += _data[base + 2] * delta _data[base + 1] += _data[base + 3] * delta _data[base + 4] -= delta if _data[base + 4] <= 0.0: _swap_and_pop(i) continue var dx: float = _data[base + 0] - player.x var dy: float = _data[base + 1] - player.y var rr: float = _data[base + 5] + PLAYER_HIT_RADIUS if dx * dx + dy * dy <= rr * rr: PlayerStats.take_damage(_data[base + 6]) VFXManager.play("hit_spark", Vector2(_data[base + 0], _data[base + 1])) _swap_and_pop(i) continue i += 1 func _swap_and_pop(idx: int) -> void: if _active_count <= 0: return # 防重入:玩家死亡致命一击触发 reset() 已清空池,勿再 -1 var last: int = _active_count - 1 if idx != last: var b_i: int = idx * ENEMY_BULLET_STRIDE var b_l: int = last * ENEMY_BULLET_STRIDE for s in ENEMY_BULLET_STRIDE: _data[b_i + s] = _data[b_l + s] _active_count -= 1 ## 生成敌方子弹,返回 id(满容返回 -1) func spawn_bullet(pos: Vector2, vel: Vector2, lifetime: float, radius: float, damage: float) -> int: if _active_count >= MAX_ENEMY_BULLETS: return -1 var id: int = _active_count var base: int = id * ENEMY_BULLET_STRIDE _data[base + 0] = pos.x _data[base + 1] = pos.y _data[base + 2] = vel.x _data[base + 3] = vel.y _data[base + 4] = lifetime _data[base + 5] = radius _data[base + 6] = damage _active_count += 1 return id func get_active_count() -> int: return _active_count ## 渲染:SoA 位置 → MultiMesh(与 BulletManager.sync_multimesh 同法) func sync_multimesh(mm: MultiMesh) -> void: if mm == null: return var n: int = min(_active_count, mm.instance_count) mm.visible_instance_count = n for i in n: var base: int = i * ENEMY_BULLET_STRIDE var r: float = _data[base + 5] mm.set_instance_transform_2d(i, Transform2D( 0.0, Vector2(r * 2.0, r * 2.0), 0.0, Vector2(_data[base + 0], _data[base + 1]))) func reset() -> void: _active_count = 0 _data.fill(0.0)