How to spawn scenes on floor? (2D platformer)

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

Hi,

I’d like to spawn instances of my enemies at a random position, but always on the floor (2D platformer)

I tried to Spawn the enemy at random position, and if it does not touch the ground, kill it and respawn another one, but this is very inefficient

Would there be a solution to this?

I’d rather not add 2D position to the map and choose among them, as it would remove the “random” part and remove flexibility (if I want to make flying enemies for examples)

:bust_in_silhouette: Reply From: Inces

You can use TileMap methods to get places just above ground ( if tile.index == -1 and tile + Vector2(0,1) is solid tile ). Iterate through get_used_cells() like this, and collect tiles in some array. Next You will be able to pick random element from this array, or even random position within borders of one tile.

Thanks for the answer, that start to make sense!!

I’m pretty new to Godot so I’m not really familiar with all the functions yet

How would you check if a tile is solid?

Jeannen | 2022-02-23 09:42

get_cell() returns index of your tile. Each index is a type of tile You designed when creating tilemap, for example - You made 4 grass tiles facing all directions and 1 sky tile. So tile indexed 0,1,2 and 3 are your grass , 4 is your sky. If get_cell is any number from 0-3 it will be solid. You know best, which tiles You want to be solid :). Index -1 means there is no tile at that position.

Inces | 2022-02-23 10:06

Aaah, I see, make sense, so, in this case all tiles with a -1 tile above them are suitable for the spawn

I have trouble using PoolVector2Array tho

usedTiles = get_used_cells() gives me the list when I print it, but print(usedTiles[0]) gives me:

"Invalid get Index ‘0’ (on base: Array)

However, var array = PoolVector2Array([Vector2(1,2)]) + print(array[0]) do print 1,2 in the console

Jeannen | 2022-02-23 10:25

Pool Vector array is pretty much the same as any other array, only it doesn’t some specific methods, and it is passed as real value. You must get an error because your get_used_cells() is empty. Get_used_cells returns all tiles that are already placed on the map. Maybe You are calling it too early, before TileMap is ready ?

Inces | 2022-02-23 11:38

It seems to be the problem indeed!

The _ready function load before the TileMap is loaded somehow. I was able to see the array because I have a duplicate TileMap for background, but the “real” one is loaded after the ready function somehow, I have to find a way to avoid this

EDIT: The problem was because I had a copy of the TileMap, I just added a variable “MainTerrain” to check that.

Jeannen | 2022-02-23 12:29

:bust_in_silhouette: Reply From: Jeannen

Well, it was tedious, but I found the solution thanks to @Inces !!
Here is the code for those who might want to do something similar (the script is in the Tileset node)

func _ready():
    	if MainTerrain:
    		select_tiles()
    		get_ground_tiles()
    
    func select_tiles():
    	usedTiles = get_used_cells()
    	
    func get_ground_tiles():
    	var n = 0
    	while n < usedTiles.size():
    		selectedTile = usedTiles[n]
    		if is_ground_above(selectedTile.x,selectedTile.y):
    			usedTiles.erase(n)
    		n += 1
    	Global.GroundTiles = usedTiles
    	
    func is_ground_above(x,y):
    	if get_cell(x,y-1) == -1:
    		return true
    	else:
    		return false