Create portals in Godot with _integrate_forces
14. January 2024
My 3D Godot game Ball2Box needs some variety, so I created some portals for new levels. This was not trivial to realize, as expected, and still needs some fine-tuning. But the proof of concept finally works now, and it revealed quite easy to realize, if using the correct Godot functions.
Note: These are not portals like you might know from the game "Portal", where you can see through the portal to the other side. These portals simply teleport a moving body from one point to another.
First attempts
The goal of the game is to get the ball into the box with only one shot. If the attempt fails, the ball gets reset to its initial position. So technically, I already had a "teleport" of the ball from its final position to the initial position.
Here, the old code of the "reset to initial position" function. It simply changes the mode of the RigidBody to static, so it can be moved by changing the translation Vector. Then it also resets all velocity and rotations, to make sure the ball remains still.
extends RigidBody
class_name Ball
# ...omitted code
mode = RigidBody.MODE_STATIC
translation = initial_position
linear_velocity = Vector3.ZERO
angular_velocity = Vector3.ZERO
rotation = Vector3.ZERO
Of course I ignored the warnings about changing game physics outside the physic process, because well, it worked.
_integrate_forces(), the forgotten solution
Obviously, I thought this would work also for portals, where the ball gets teleported without resting the velocity and rotation. After a few attempts for making it work, I tried searching the web, and luckily found some hint on using _integrate_forces.
Surely somehow it would have worked without it, after banging my head for some hours, but using this function was the correct way.
The documentation of the _integrate_forces function writes
Safely is here the keyword! It means that there will be no strange glitches or non expected movements
Here the final working code, that not only teleports the ball from portal to portal, but also to its initial position.
extends RigidBody
class_name Ball
var do_teleport:bool = false
var do_reset:bool = false
var do_static:bool = false
var initial_position:Transform
var next_transform:Transform
next_transform = p_transform
do_teleport = true
do_reset = true
# change the mode to static on the next iteration
# otherwise ball doesn't move to new position
if do_static:
do_static = false
mode = RigidBody.MODE_STATIC
if do_teleport:
state.transform = next_transform
do_teleport = false
if do_reset:
state.transform = initial_position
state.angular_velocity = Vector3.ZERO
state.linear_velocity = Vector3.ZERO
rotation = Vector3.ZERO
do_reset = false
do_static = true
An important difference here, is that instead of moving the ball directly, a flag gets set. The actual movement happens then during the next _integrate_forces step.
For completion, here the code where the teleport function gets called, with the transform variable of the corresponding portal.
ball.
After some refactoring of Pocket Broomball, I found out that I already used _integrate_forces there to move the ball. So, I was already sitting on the solution, but forgot about it, as usual.
Find the full documentation of _integrate_forces here.
The full source code of Ball2box is available on Github and Codeberg.
Every feedback is welcome
Feel free to write me an email at info@simondalvai.org and comment on Mastodon or HackerNews.