Node always spawn twice without care of overlapping check

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

Hello,

I have simple scene with Spatial as root, StaticBody as ground, and Area as Marker.
The goal of Marker is to check overlapping with other Areas in the scene.
What i try to making is spawn multiple node (Brick) while left mouse button is pressed, but preventing spawn if another area is under the mouse. That work at one exception :
The brick spawn twice before marker detect overlapping. I trying some (stupid) things, but i dont understand why…

Code of root node :

extends Spatial

export (NodePath) var marker_node

var pointer : Vector3
var marker : Node
var clic : bool = false
var hit : Dictionary = {}

onready var camera = $View/Arm/Camera

func _ready():
	marker = get_node(marker_node)

func _unhandled_input(event):
	if event is InputEventMouseMotion:
		var from = camera.project_ray_origin(event.position)
		var to = from + camera.project_ray_normal(event.position) * 1000;
		var space_state = get_world().get_direct_space_state()
		hit = space_state.intersect_ray( from, to )
	if Input.is_action_pressed("primary"):
		clic = true
	if Input.is_action_just_released("primary"):
		clic = false
		
func _physics_process(delta):
	if not hit.empty(): 
		pointer = Vector3(floor(hit.position.x) + .5, 0.01, floor(hit.position.z) + .5)
		marker.global_transform.origin = pointer
	if clic and marker.get_overlapping_areas().empty():
		var brick = Brick.new()
		brick.global_transform.origin = pointer
		add_child(brick)

Code of the Brick.gd :

extends Area
class_name Brick

var mesh = PlaneMesh.new()
var mi = MeshInstance.new()
var cs = CollisionShape.new()
var shape = BoxShape.new()

func _init():
	mesh.size = Vector2(1,1)
	shape.extents = Vector3(0.499, 1, 0.499)
	cs.shape = shape
	mi.mesh = mesh
	add_child(cs)
	add_child(mi)

The Marker is just Area with CollisionShape (BosShape).
If i press mouse left button, Brick spawn twice. If i press and drag,
Brick spawn 4 or 5 time…

For now, i use Godot Beta 3.5. But have tested in Godot 3.4, with same result.

I’m not sure at first glance what would be wrong, but here are some notes:

if Input.is_action_pressed("primary"):

Keep in mind _unhandled_input is called for every input event. That means it will be called when you put the button down, and also when you release the button. And also each time you move the mouse. Which is why usually, it is kinda suspicious to see Input.something inside an _input* function, since you should only rely on the event that’s being passed to you. Try using event.is_action_pressed or event.is_action_release instead. Might not solve your issue but would be cleaner: InputEvent — Godot Engine (stable) documentation in English

From what I see in your code, until _unhandled_input is called, your clic variable will remain true until you release your mouse. I would not call it clic, but instead place_brick_action, and then set it to false once the action is consumed inside _physics_process. This, assuming you also do the change I mentionned above about not using Input (otherwise clic could come back to true randomly just because some other input event occured like a key press while your mouse button is still down or whatever).

Then, something you could try is verify when your area detects the brick if you let it run. I suspect you’ll find out it starts being detected 2 physic frames later (depending on the physics engine you use), which could explain the issue. If that is the case I’m not sure how you could solve it, apart from waiting two physic frames maybe.
If it is still not detected after that, perhaps your collision layers or collision shapes or area monitoring are not being setup correctly?

Zylann | 2022-02-02 13:52

Thank you for the Input precision.

I have tried to set the boolean to false after consuming action in _physics_process. Same result.
According to the doc, problem is probably linked to :

For performance reasons (collisions are all processed at the same
time) this list is modified once during the physics step, not
immediately after objects are moved. Consider using signals instead.

Like you said, it take two frame to detect the collision. Cause when i add child, the collider list is updated next frame, and after consuming event second time.
So, what i find for now, delta of _physics_processis fixe, so i add a variable to hold delta and consume the event only if my variable is equal to delta * 2.

So, wait one more frame to check overlapping. But it seem very ugly solution.

 func _physics_process(delta):
        time += delta
    	if clic and marker.get_overlapping_areas().empty() and time == delta * 2:
    		var brick = Brick.new()
    		brick.transform.origin = marker.transform.origin
    		add_child(brick)
    	if time == delta * 2:
    		time = 0

Edit : clic stay true, it’s what i want. I want to be able to drop multiple brick when i move mouse.

BigBadWouf | 2022-02-02 14:58