77 lines
3.2 KiB
GDScript
77 lines
3.2 KiB
GDScript
## ZoneManager — 地面效果区域管理(Autoload: ZoneManager)
|
||
## 权威来源:architecture_design.md §8 ADR-R5-N1(P6-N71 tick 用 while + -=)
|
||
## SoA PackedFloat32Array stride=8;基于 SpatialGrid 空间索引(禁用 Area2D)
|
||
## [0]=cx [1]=cy [2]=radius [3]=status_id [4]=duration [5]=tick_interval [6]=tick_accum [7]=owner_id
|
||
##
|
||
## 注:本 GDScript 外壳承载 tick 热循环为 S5 功能验收路径(P-S5-04);
|
||
## C# 子节点 ZoneManagerCs 驱动的压力路径(P-S5-ZM-01,64 区×1000 敌 <1ms)留作后续优化,
|
||
## 与项目既有 C# 回退策略(R-08)一致。
|
||
extends Node
|
||
|
||
const ZONE_STRIDE: int = 8
|
||
const MAX_ZONES: int = 64
|
||
|
||
var _zone_data: PackedFloat32Array = PackedFloat32Array()
|
||
var _active_zones: int = 0
|
||
|
||
func _ready() -> void:
|
||
_zone_data.resize(MAX_ZONES * ZONE_STRIDE)
|
||
|
||
## 生成一个地面效果区域;满员时顶掉最旧(index 0 前移)
|
||
func spawn_zone(cx: float, cy: float, radius: float, status_id: int,
|
||
duration: float, tick_interval: float, owner_id: int) -> void:
|
||
if _active_zones >= MAX_ZONES:
|
||
# 顶掉最旧 Zone(index 0),整体前移(O(N),N≤64)
|
||
for j in range(ZONE_STRIDE, _active_zones * ZONE_STRIDE):
|
||
_zone_data[j - ZONE_STRIDE] = _zone_data[j]
|
||
_active_zones -= 1
|
||
var base: int = _active_zones * ZONE_STRIDE
|
||
_zone_data[base + 0] = cx
|
||
_zone_data[base + 1] = cy
|
||
_zone_data[base + 2] = radius
|
||
_zone_data[base + 3] = float(status_id)
|
||
_zone_data[base + 4] = duration
|
||
_zone_data[base + 5] = maxf(0.01, tick_interval)
|
||
_zone_data[base + 6] = 0.0 # tick_accum
|
||
_zone_data[base + 7] = float(owner_id)
|
||
_active_zones += 1
|
||
|
||
func _physics_process(delta: float) -> void:
|
||
if _active_zones == 0:
|
||
return
|
||
var i: int = 0
|
||
while i < _active_zones:
|
||
var base: int = i * ZONE_STRIDE
|
||
_zone_data[base + 4] -= delta # duration 倒计时
|
||
_zone_data[base + 6] += delta # tick_accum 累积
|
||
|
||
# 速率限制:外层 if 守卫 + 内层 while + -= tick_interval(P6-N71,保留余量精度)
|
||
# SpatialGrid.query_circle 仅在 if 守卫内查询一次,不在 while 内重复查询
|
||
if _zone_data[base + 6] >= _zone_data[base + 5]:
|
||
var cx: float = _zone_data[base + 0]
|
||
var cy: float = _zone_data[base + 1]
|
||
var rad: float = _zone_data[base + 2]
|
||
var sid: int = int(_zone_data[base + 3])
|
||
var oid: int = int(_zone_data[base + 7])
|
||
var targets: PackedInt32Array = SpatialGrid.query_circle(Vector2(cx, cy), rad)
|
||
while _zone_data[base + 6] >= _zone_data[base + 5]:
|
||
_zone_data[base + 6] -= _zone_data[base + 5] # -= tick_interval 保留余量
|
||
for t_id in targets:
|
||
StatusManager.apply(t_id, sid, 1, -1.0, oid) # stacks=1, duration=默认, owner
|
||
|
||
if _zone_data[base + 4] <= 0.0: # duration 耗尽 → swap-and-pop 移除 + VFX 淡出
|
||
VFXManager.play("zone_expire", Vector2(_zone_data[base + 0], _zone_data[base + 1]))
|
||
if i < _active_zones - 1:
|
||
var last: int = (_active_zones - 1) * ZONE_STRIDE
|
||
for k in ZONE_STRIDE:
|
||
_zone_data[base + k] = _zone_data[last + k]
|
||
_active_zones -= 1
|
||
else:
|
||
i += 1
|
||
|
||
func get_active_count() -> int:
|
||
return _active_zones
|
||
|
||
func reset() -> void:
|
||
_active_zones = 0 # 惰性清理:SoA 数据不清零,_active_zones 控制有效范围
|