Skip to content

Collision hand grab-regrab race condition #880

Description

@Ritter7124

in grab.gd

func release() -> void:
	# Clear any hand pose
	_clear_hand_pose()

	# Remove collision exceptions with a small delay
	if is_instance_valid(collision_hand) and not _collision_exceptions.is_empty():
		# Use RIDs instead of the objects directly in case they get freed while
		# we are waiting for the object to fall away
		var copy : Array[RID] = _collision_exceptions.duplicate()
		_collision_exceptions.clear()

		# Delay removing our exceptions to give the object time to fall away
		collision_hand.get_tree().create_timer(0.5).timeout \    #<------------------
			.connect(_remove_collision_exceptions \
				.bind(copy) \
				.bind(collision_hand.get_rid()))

	# Report the release
	print_verbose("%s> released by %s", [what.name, by.name])
	what.released.emit(what, by)

there is a timer that calls a static function of grab.gd after a half second timer which then deletes the copy of exceptions we pass it through, and there is a comment on this static function that has an idea about restarting the timer if we still have collisions with OTHER objects. But there is a nasty problem if the player drops and regrabs the same object within this .5 interval. Colliding with a grabbed object is very bad as a whole sleuth of stuff can happen depending on your vr setup. For me it was causing my hand to fly away and break a leash radius and drop the object, which is very tough ahaha. Anyway here is my fix, I didn't want to create a pull request because my version is very behind and I didn't want to clone a fresh project and do all of that, since I'm not sure my solution is the best one. Anyways here is what I cooked up.

func release() -> void:
	# Clear any hand pose
	_clear_hand_pose()

	# Remove collision exceptions with a small delay
	if is_instance_valid(collision_hand) and not _collision_exceptions.is_empty():
		# We need to make a copy of our array else it will be passed by reference.
		var copy : Array[PhysicsBody3D]
		for exc in _collision_exceptions:
			copy.push_back(exc)
		_collision_exceptions.clear()
		print(collision_hand, " in grab.gd!!")
		
		collision_hand.remove_collision_exceptions_delay(copy, what)

	# Report the release
	print_verbose("%s> released by %s", [what.name, by.name])
	what.released.emit(what, by)

then in collision_hand.gd

const DELAY_DURATION: float = .5
func remove_collision_exceptions_delay(
	collision_exceptions: Array[PhysicsBody3D], 
 	picakble: XRToolsPickable):
		
	await get_tree().create_timer(DELAY_DURATION).timeout
	if (!is_instance_valid(self)):
		return  #we don't exist so who cares
		
	if (is_instance_valid(picakble) and picakble.is_picked_up()):
		collision_exceptions.remove_at(collision_exceptions.find(picakble))

		
	for body: PhysicsBody3D in collision_exceptions:
		if (is_instance_valid(body)):
			self.remove_collision_exception_with(body)
			body.remove_collision_exception_with(self)

I figured if anything should deal with the exceptions it should be the hand, and if we had a problem with the single object just pass it to before the grab.gd is destroyed. I see some warnings that collisionhand is deprecated anyways? But I didn't see another solution and it works great besides this one little issue I had with it. Anyways cheers for reading through this and have a good one!

Tanner

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions