I am trying to make a game using Godot 4.5 where enemies spawn off-screen, but I faced this bug where they spawn on-screen instead:
The white squares are the spawned enemies and the red is the player.
Here's the minimal reproducible example:
extends Node2D @export var root: Node2D @export var max_entities: int @export var spawn_spots: PackedVector2Array var _entities: Array[ColorRect] static func spawn_indices_generator(indices_to_append_to: Array, is_y: bool = true, counter: float = 11, limit: float = 1500, step: float = 8): while counter < (DisplayServer.window_get_size().y if is_y else DisplayServer.window_get_size().x) + limit: indices_to_append_to.append(counter) counter += step static func va_pick_random(arr: Variant): return arr[randi() % arr.size()] func external_spawn(): return spawn(va_pick_random(spawn_spots) + %player.position) func spawn(pos: Vector2): var node = preload("res://dummy.tscn").instantiate() as ColorRect %ui.call_deferred("add_child", node) node.position = pos return node func _ready() -> void: var arr_x: Array[float] var arr_y: Array[float] spawn_indices_generator(arr_x, false, -DisplayServer.window_get_size().x, DisplayServer.window_get_size().x, 1) spawn_indices_generator(arr_y, true, -DisplayServer.window_get_size().y, DisplayServer.window_get_size().y, 1) var i := 0 while i < min(arr_x.size(), arr_y.size()): var vector := Vector2(arr_x[i], arr_y[i]) if not Rect2( Vector2.ZERO, Vector2( DisplayServer.window_get_size() ) ).has_point(vector): spawn_spots.append(vector) i += 1 print(spawn_spots) for _j in range(10): external_spawn() external_spawn() external_spawn() Scene tree:
- ┖╴root
- ┠╴ui
- ┠╴spawner (code is on above)
- ┖╴player
- ┖╴ColorRect
I cannot change the spawn_indices_array() because I extracted it from some part of my code, and I fear it will break things that are not intended here. I need resolution-independent code.
My original code was (you can ignore this):
class_name VirusSpawner extends EntitySpawner func external_spawn() -> Virus: print("Accepted") return spawn( Virus.VirusInfo.new( AlgebraicMath.Objectal.va_pick_random(spawn_spots) + player.position, #Position root, # Root Scene node player, # Player 5 # Health ) ) func spawn(info) -> Virus: var v = preload("res://scenes/virus.scn").instantiate() as Virus root.call_deferred("add_child", v) v.change(info) return v func _ready() -> void: var arr_x: Array[float] var arr_y: Array[float] AlgebraicMath.Objectal.spawn_indices_generator(arr_x, false, -DisplayServer.window_get_size().x, DisplayServer.window_get_size().x, 1) AlgebraicMath.Objectal.spawn_indices_generator(arr_y, true, -DisplayServer.window_get_size().y, DisplayServer.window_get_size().y, 1) var i := 0 while i < min(arr_x.size(), arr_y.size()): var vector := Vector2(arr_x[i], arr_y[i]) if not Rect2( Vector2.ZERO, Vector2( DisplayServer.window_get_size() ) ).has_point(vector): spawn_spots.append(vector) i += 1 print(spawn_spots) for _j in range(10): external_spawn() external_spawn() external_spawn() 