Hello everyone, hope this is the right section!
I recently started using Godot and I am “developing” a top down shooter (think similar to Hotline Miami).
So far I encountered some minor challenges that I could solve either on my own or reading the documentation.
Now I’m trying to have random spawn points for my mobs, but every once in a while the will basically spawn inside the Tiles of the tilemap (currently only walls, but they could easily spawn into objects as well).
is there a way to know if a Tile/Node is in the randomly generated position?
What I could come up with is:
func _physics_process(delta: float) -> void:
if current_mobs < max_mobs:
var spawn_pos = Vector2(rng.randf_range(10,1000.0),rng.randf_range(-10, 500.0))
var spawn_area = Area2D.new()
var collision = CollisionShape2D.new()
var shape = RectangleShape2D.new()
shape.extents = Vector2(64, 64)
collision.shape = shape
add_child(spawn_area)
spawn_area.add_child(collision)
spawn_area.global_position = spawn_pos
if spawn_area.has_overlapping_bodies():
print('taken')
spawn_pos = Vector2(rng.randf_range(10,1000.0),rng.randf_range(-10, 500.0))
spawnMob(spawn_pos)
spawn_area.queue_free()
else:
print('available')
spawnMob(spawn_pos)
spawn_area.queue_free()
So basically in physics process I check if a mob should be spawn, if so I generate a random position (spawn_pos), an Area2D (spawn_area), give it a Collision shape 62x62 and add it to the main node.
What I would expect is that “spawn_area.has_overlapping_bodies()” would return True if a tile/object is in that position and re-run the spawnMob function.
BUt this does not appear to be the case.
Is there an easier way to approach this?
I was even thinking about Marker2D or raycasting, but not sure how I would implement it…
Thank you for you r help and again, sorry if this is silly
So, what I would do is create an array of all spawnable tiles on the tilemap, exclude the walls by using layers, place only viable spawn tiles on a certain layer. Use get_used_tiles() on that layer. Those are your base spawn points, now perhaps write a tracking script, or in each entity on the map have them send a signal to let your map know where they are (if the map spawns them in it can store their starting position), you will store these coords in an array. Whenever the entities move they will send their previous position and their new, the array will update removing previous position and adding new.
Now, for spawning you can do a for loop through your viable cell array, and inside do a for loop through occupied cells, if it is occupied continue, but if not place it in a temporary array.
Now, you can get the size of that temp array and choose a random number between 0 and size - 1, this will be the index you use to find the coord to spawn at.
A bit complicated, more of a pragmatic approach rather than optimized. Hopefully someone more experienced then me can give you an easier solution.
Edit: you’ll have to also translate from map coords to local/global coords or vice versa.
Keep in mind areas update only one time in the physics frame, so has a chance of your area don’t have updated info because you added them while the physics frame is already runnuing. I recommend you use ShapeCast2D with ShapeCast2D.force_shapecast_update() instead.
This could be the right approach indeed, at least by testing the shapeCasting2D and using the “is_colliding()” method, I can see that when a position is not available it will generate a new one!
I guess I’ll tweak the logic a bit in the next few days, meanwhile thank you very much!
Sounds good hope it works, as an improvement to my previous method, rather than constantly updating occupied array you could just query all entities at moment of spawning, using a group. They would then send back their current cords which would be used as occupied.
I see what you mean, for now what I did was using a Circle Shape (as those update faster apparently) and place it as a Shapecast on a set of coordinates in the _physics_process.
I changed the “if” with a “while” loop as that felt like it made more sense.
I also created an Autoload that will handle all “Global” variables and methods so that I don’t have to repeat the same code over multiple scenes/level and it looks like it’s perfectly working.
For the time being I’m happy with the result THANKS everybody!