diff --git a/scripts/autoloads/spatial_grid.gd b/scripts/autoloads/spatial_grid.gd index fe2230a..c802941 100644 --- a/scripts/autoloads/spatial_grid.gd +++ b/scripts/autoloads/spatial_grid.gd @@ -14,12 +14,6 @@ var _cs_node: Node = null # SpatialGridCs C# 子节点(就绪后挂载) var _grid: Array = [] # Array[Array[int]] var _entity_cell: Dictionary = {} # { entity_id → cell_idx } -## query_circle 复用结果缓冲(避免每次调用新建 PackedInt32Array) -## 由 clear()+append 就地填充后返回;调用方按值(CoW)读取。 -## 若调用方在遍历返回值时触发嵌套 query_circle(如命中→SpellEvaluator 区域查询), -## 下一次 clear() 因引用计数>1 触发写时复制,旧结果不被破坏,语义与新建数组等价。 -var _query_result: PackedInt32Array = PackedInt32Array() - func _ready() -> void: _grid.resize(GRID_COLS * GRID_ROWS) for i in _grid.size(): @@ -45,7 +39,11 @@ func insert(entity_id: int, pos: Vector2) -> void: func query_circle(center: Vector2, radius: float) -> PackedInt32Array: if _cs_node and _cs_node.has_method("QueryCircle"): return _cs_node.call("QueryCircle", center, radius) - _query_result.clear() + # 注意:必须返回新建数组,不可复用成员缓冲。 + # 调用方(BulletManager._check_collision)在遍历返回值期间可能触发嵌套 query_circle + # (命中→SpellEvaluator.execute_sub 的区域法术),GDScript 的 PackedArray 返回值是活引用别名, + # 复用成员缓冲会被嵌套调用就地覆写、破坏外层遍历(已实测复现)。 + var result := PackedInt32Array() var min_cx: int = clamp(int((center.x - radius + GRID_OFFSET) / CELL_SIZE), 0, GRID_COLS - 1) var max_cx: int = clamp(int((center.x + radius + GRID_OFFSET) / CELL_SIZE), 0, GRID_COLS - 1) var min_cy: int = clamp(int((center.y - radius + GRID_OFFSET) / CELL_SIZE), 0, GRID_ROWS - 1) @@ -53,8 +51,8 @@ func query_circle(center: Vector2, radius: float) -> PackedInt32Array: for cy in range(min_cy, max_cy + 1): for cx in range(min_cx, max_cx + 1): for eid in _grid[cx + cy * GRID_COLS]: - _query_result.append(eid) - return _query_result + result.append(eid) + return result ## 每帧由 EnemyManager._physics_process 驱动(dirty-list 方案) func rebuild() -> void: