Is there a way to manually reset a RigidBody2D's gravity after transforming within a space with overridden gravity?

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

I am creating a marble simulation using Godot. The marbles are RigidBody2Ds. They travel through a level that is filled with obstacles. The level includes Area2D’s that override the space’s gravity (for things like marbles on the ceiling and walls), as well as Area2D’s that teleport the RigidBodies back to the start of the level upon contact (damaging zones basically).

When a marble hits one of the damaging zones, I use _integrate_forces to manually set its transform back to the start of the level.

The issue is that sometimes I have to manually set the transform while the marble is also inside an Area2D that is overriding the space’s gravity. After moving the marble outside of the Area2D which is overriding the space’s gravity, the marble behaves as if it is still inside the space i.e. its gravity is still being overridden. Here is a GIF of what I’m talking about.

And here is a github repo containing a reproduction of my issue.

I cannot figure out how to fix this problem. Is there a way to reset a RigidBody’s gravity in code? Perhaps I am setting the marble’s position incorrectly using _integrate_forces? Here is my teleporting code for the Marble.

class_name Marble
extends RigidBody2D

var isTeleportQueued: bool = false
var isTeleportPrepared: bool = false
var isTeleportComplete: bool = false

var teleportPosition:Vector2 = Vector2(0, 0)
var teleportLinearVelocity: Vector2 = Vector2(0, 0)
var teleportAngularVelocity: float = 0.0

func _process(delta):
	self._handle_teleport()

func _integrate_forces(state):
	if self.custom_integrator:
		self._teleport_marble(state)

func queue_teleport(newPosition: Vector2) -> void:
	self.isTeleportQueued = true
	self.teleportPosition = newPosition
	self.teleportLinearVelocity = self.linear_velocity
	self.teleportAngularVelocity = self.angular_velocity

func _handle_teleport() -> void:
	if self.isTeleportComplete:
		self._finish_teleport()
		
	else:	
		if self.isTeleportQueued:
			if not self.isTeleportPrepared:
				self._prepare_teleport()

func _prepare_teleport() -> void:
	self.set_use_custom_integrator(true)
	self.isTeleportPrepared = true
	
func _teleport_marble(state) -> void:
	var temp = state.get_transform()
	
	temp.origin  = Vector2(self.teleportPosition.x, self.teleportPosition.y)	
	state.set_transform(temp)

	self.isTeleportQueued = false
	self.isTeleportPrepared = false
	self.isTeleportComplete = true

func _finish_teleport() -> void:
	self.set_use_custom_integrator(false)
	self.isTeleportComplete = false

Many thanks, please let me know if I can specify anything else.

UPDATE: After investigating a little, I seems that this is an open bug with Godot. Still, if anyone can think of a workaround I would appreciate it.

When you change the transform, what happens when you also change its velocity? e.g.

state.linear_velocity = Vector2()

spaceyjase | 2023-06-26 15:50

It doesn’t change anything unfortunately.

stephencz | 2023-06-26 15:57

How about changing its sleeping property?

PhysicsDirectBodyState2D — Godot Engine (4.0) documentation in English

(or even on the body directly)

spaceyjase | 2023-06-26 17:34