Replace randomly placed objects on collision

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By technotica
:warning: Old Version Published before Godot 3 was released.

Hello,

I created a script to place random objects randomly on a map.
I have a function that checks if the player moves over the object and that detects if the object is inside a wall.

Unfortunately I haven’t figured out yet how to move the object after the wall collision has been detected.

From the _on_Area2D_body_enter function, how could I tell which item node was colliding with the wall?

Or is it too late at that point?

My code:

extends Sprite

var xwg = 0
var ywg = 0

func _ready():
	# Called every time the node is added to the scene.
	# Initialization here
	set_fixed_process(true)
	

func place_random_item(nodename, amount, xwidth, ywidth):
	xwg = xwidth
	ywg = ywidth
	var itemnumber
	var itemname
	var iposx = 0
	var iposy = 0
	var iterate = 0
	
	while(iterate<amount):
		randomize()
		itemnumber = randi()%4+1
		itemname = str(itemnumber)
		iposx = randi()%int(xwidth)-100
		iposy = randi()%int(ywidth)-100
		var new_item = Area2D.new() 
		var new_sprite = Sprite.new()
		var new_collision = CollisionShape2D.new()
		var shape = RectangleShape2D.new()
	
		get_node(nodename).add_child(new_item)
		new_item.add_child(new_sprite)
		new_item.add_child(new_collision)
	
		new_item.set_owner(get_node(nodename))
		new_item.set_name(str(itemnumber))
		new_item.set_pos(Vector2(iposx,iposy))
	
		new_sprite.set_owner(get_node(new_item.get_path()))
		new_sprite.set_texture(load(item_database.get_item_icon(itemnumber)))
		new_sprite.set_scale(Vector2(0.5,0.5))
	
		new_collision.set_owner(get_node(new_item.get_path()))
		new_collision.set_shape(shape)
		shape.set_extents(Vector2(1,1))
		#new_collision.set_trigger(true)
		new_collision.set_scale(Vector2(1,1))
		new_item.connect("body_enter", self, "_on_Area2D_body_enter")
		iterate += 1

func _on_Area2D_body_enter( body ):
	if(body.get_name() == "Player"):
		print("Player")
	elif(body.get_name() == "Walls"):
		var iposx= randi()%int(xwg)-100
		var iposy= randi()%int(ywg)-100
		#new_item.set_pos(Vector2(iposx,iposy))

Why would you detect if it’s in a wall? Is this a flaw in your random spawn function?
I think you could do a shape itersection query before you spawn the object.

Also, I see so many people fall into this trap:
var new_collision = CollisionShape2D.new()
Don’t do this from code. It won’t work after you export your game. CollisionShape2D is an editor-only class and is unavailable outside of the editor. Use the shape API instead: CollisionObject2D — Godot Engine (stable) documentation in English

Zylann | 2017-06-02 11:36

I am just placing items at random coordinates on the map, that seemed easiest, but it won’t prevent them from being spawned inside the walls. The items can tell me if they are spawned inside a wall though so I thought I could just move them in that event.

The location my items spawn is basically just this:

 new_item.set_pos(Vector2(iposx,iposy))

this then tells me if they are in an inaccessible place:

new_item.connect("body_enter", self, "_on_Area2D_body_enter")
        iterate += 1

func _on_Area2D_body_enter( body ):
    if(body.get_name() == "Player"):
        print("Player")
    elif(body.get_name() == "Walls"):
        var iposx= randi()%int(xwg)-100
        var iposy= randi()%int(ywg)-100
        #new_item.set_pos(Vector2(iposx,iposy))

The way I have it now all I’d need is a way to change the items position inside the function _on_Area2D_body_enter( body ):
But of course the new_item does not point to the right item at that point.
Is there a way to place items on tiles instead of coordinates and preclude any tiles covered by wall tiles?

Thanks for the tip with CollisionShape2D though!

technotica | 2017-06-02 11:43

I’m not sure what are you trying to do, but if it’s simple collision, isn’t making your player character a kinematicBody2D enough?
Because for what I see, you have a solid setup with collision nodes and shapes, I don’t see how your character isn’t responding to the collisions, or do you want to make a different kind of interaction and not simple collisions?

Santiago | 2017-06-02 12:10

Hi!

No the thing is collisions do work, if my player moves over the item it gets registered. If the item gets spawned inside a wall that also gets registered.

But now I want to tell the engine to move the item that was spawned inside a wall somewhere else.

The notification of a collision works with the function _on_Area2D_body_enter( body ): but unfortunately once I am inside that function I don’t see a way to figure out which item called that function. It gets the body of the thing (player, walls) that ran into the item but not which item started the function. The function was made with this:

new_item.connect("body_enter", self, "_on_Area2D_body_enter")

Can I somehow not only transmit the body that ran into the item but also the item that was run into by calling this function?
Am I doing this totally wrong? I guess the shape_intersection Zylann pointed out works in a different way?

technotica | 2017-06-02 12:17

:bust_in_silhouette: Reply From: Zylann

In order to find wether you can spawn an object before actually spawning it, you can do a shape intersection query to ask the collision engine: “hey, is there anything here if I put this shape?”

To do that you need to use the direct space state 2D.
The doc explains how to use it here for raycasts: Ray-casting — Godot Engine (stable) documentation in English

In order to do this with a shape instead of a ray, you can do this (untested):

var space_state = get_world().get_direct_space_state()
var query = Physics2DShapeQueryParameters.new()
var shape = RectangleShape2D.new()
shape.set_extents(Vector2(1,1))
query.set_shape(shape)
var hits = space_state.intersect_shape(query)
if hits.size() != 0:
	print("There is something here!")

The only limitation to this method is that you should execute it inside _fixed_process, because it’s the only time where physics are in sync with scripts (your signal is also executed in a deferred way for this reason).

Thank you, I’ll try that out soon!

technotica | 2017-06-02 18:35