feat(boss): 新增 EnemyBulletManager 敌方子弹子系统(SoA 池 + vs 玩家碰撞)
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
## 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:
|
||||
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)
|
||||
@@ -0,0 +1 @@
|
||||
uid://ctot52kayppgs
|
||||
Reference in New Issue
Block a user