Is there an more efficient way for spawning enemies in a Procedurally generated map ? unlike this

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

Hey i’m trying to make a 2d game (top down shooter) where maps is procedural generated, also enemies are placed in randomly in x,y locations within the enemy spawn range.

Here is the code and image of nodes connected for Main_a.tscn

Nodes with in Main_a.tscn

extends Control

const FLOOR = -1
const WALL = 0

var noise
var flooro_cap = 1
var floori_cap = Vector2 (0.3, 0.1)
const gridSize = Vector2(160,80)
const gridSize_floor = Vector2(80,50)
var diggers = []
onready var tilemapo = $Mapo
onready var tilemapi = $Mapi
onready var tilemapj = $Map 

var num = 5
var amount = 0
var enemySpawnrange = Vector2(2490,1530)
onready var enemy_container = get_node("enemy_container")
onready var enemy1 = preload("res://enemies/Enemy1.tscn")

func _ready():
	load_game()

func load_game():
	randomize()
	gen_map()
	make_flooro_map()
	$Player.position = map.world_center() + Vector2.ONE * 16
	set_process(true)
	spawn_enemy(num)

func spawn_enemy(num):
	var rand = RandomNumberGenerator.new()
	for i in range(num):
		var enemy = enemy1.instance()
		enemy_container.add_child(enemy)
		enemy.connect("enemy_spawn_amount", self, "_on_enemy_spawn_amount")
		rand.randomize()
		var x = rand.randf_range(0, enemySpawnrange.x)
		rand.randomize()
		var y = rand.randf_range(0, enemySpawnrange.y)
		enemy.set_position(Vector2(x, y))

func _on_enemy_spawn_amount():
	amount += 1
	print(amount)

func _process(delta):
	if Input.is_action_just_pressed("ui_accept"):
		get_tree().reload_current_scene()
		_ready()
	elif Input.is_action_just_pressed("ui_cancel"):
		get_tree().quit()
	if enemy_container.get_child_count() != num:
		var rand = RandomNumberGenerator.new()
		for i in range(amount):
			var enemy = enemy1.instance()
			enemy_container.add_child(enemy)
			enemy.connect("enemy_spawn_amount", self, "_on_enemy_spawn_amount")
			rand.randomize()
			var x = rand.randf_range(0, enemySpawnrange.x)
			rand.randomize()
			var y = rand.randf_range(0, enemySpawnrange.y)
			enemy.set_position(Vector2(x, y))
			amount -= 1

func gen_map()
........	

func make_flooro_map():
.......

Here is the code and image of node within the Enemy1.tscn

Nodes with in the Enemy1.tscn

extends Area2D

signal enemy_spawn_amount

func _ready():
	pass

func _on_Enemygrunt_body_entered(body):
	if body.get_name() == "Player":
		print("player")
	elif body.get_name() == "Mapo":
		print("mapo")
		emit_signal("enemy_spawn_amount")
		queue_free()

Here's what the  game looks

I haven’t mentioned any parameters or code for map generation to make the code short and readable.

So this is how the enemy spawns work. First the map is randomly generated from the center of the gridsize box . and player sprite is placed. enemySpawnrange is set to Vector2(2490,1530) which is almost as same as map size gridSize = Vector2(160,80).

Then spawn_enemy(num) spawns enemies randomly around the map, it also checks _on_Enemygrunt_body_entered(body): function in Enemy1.gd using signals. if the enemy is making contact with Mapo tile map (outside the boundary of randomly generated map) it is queue_free() and is deleted.

The amount is recorded in this function and is used in the _process(delta) function. This loops until amount = 0. there by spawning all enemies inside the randomly generated map.

I feel like there is something wrong with my code, and will cause problems once i start implementing enemy movement .

Is there a better way of spawning enemies, the current method i’m using takes about 10 seconds minimum to put all 5 enemies inside the generated map ( for a 2d game 10 seconds is a pretty long time for loading).

Reducing the spawn boundary would definitely help in placing enemies faster. But i don’t know how to change the location spawn range rectangle.one point of the spawn range rectangle is fixed to the top left corner of the screen. and it only scales or shrinks based on that fixed point.

Also enemies are placed one after the another, is there any way i could place them all at the same time.

:bust_in_silhouette: Reply From: jgodfrey

Your example map is predominantly empty (doesn’t contain map elements) - that’s got to hurt your spawn performance based on your described method. At the very least, why not keep track of the cells that contain actual map elements (during map generation) for use during spawning? Then, rather than blindly picking from all the cells (most of which will fail), only pick from the list of “known valid” cells. In fact, you could remove a cell from that collection as soon as you’ve dropped something on it, so it won’t be picked again for the next spawn cycle. That way, you shouldn’t even have to check for cells being “used” during the spawn operation, as the cells you’re choosing from are 1) known to be valid and 2) known to be empty…

That should be really fast…

1 Like