How to generate spawn positions outside of the players camera2D.

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By UserWaitingForGodot

I have a world map (rectangle 1920px, and 1280 high).
I have a player (Area2D) with a Camera2D (240px wide, 160px high) as a child node.

I want to instantiate many mobs at random positions within the world map but also I don’t want them to spawn inside the Camera2D view rectangle (so the player can see enemies popping out form nowhere)

I have this code:

func _spawn_enemies(many: int) -> void:
for i in range(many):
var new_mob: = Mob.instance()
	new_mob.global_position = _get_random_spawn_position()
	add_child(new_mob)

func _get_random_spawn_position() -> Vector2:
	var spawn_pos: = Vector2(
	rand_range(0, world.rect_size.x),
	rand_range(0, world.rect_size.y))
	return spawn_pos

The spawing seems to work just right but sometimes a mob appears exactly on top of the player, killing it instantly.

Any suggestions? I thought of while loops and hard code dimensions but I think there just might be a better way.

Thank you very much for reading!!

var me: = "happy :)"
:bust_in_silhouette: Reply From: nonchip

i think that’s to be expected, because if I can assume the player is somewhere inside the world (so between 0,0 and world.rect_size), that means that randomly picking a spawn position inside that range can end up picking one that just happens to coincide with the player’s position.

an easy way of preventing this would be something like:

var player_pos: Vector2 = #get this somehow from the player object
const safe_range: int = #define this as the minimum distance from the player
func _get_random_spawn_position() -> Vector2:
    var spawn_pos: = Vector2(
    rand_range(0, world.rect_size.x),
    rand_range(0, world.rect_size.y))
    if abs(spawn_pos - player_pos) < safe_range: # if we are too close
      spawn_pos = _get_random_spawn_position() # just get a new one instead
    return spawn_pos

Wow! thank you very much, I have not thought about the safe_range solution. Elegant, simple and effective :slight_smile:

UserWaitingForGodot | 2020-04-26 11:18

I had a problem but it seem to work just fine with (spawn_pos - player_pos).lenght() instead of abs(...)

UserWaitingForGodot | 2020-04-26 12:27

:bust_in_silhouette: Reply From: Undorobo

This works for me

const safe_range: int = 400

func _on_SpawnTimer_timeout():
	var new_enemy = Enemy.instance()
	new_enemy.global_position = player.position + Vector2(safe_range, 0).rotated(rand_range(0, 2*PI))
	get_parent().add_child(new_enemy)