Creating and Using QSoftBodyNode - erayzesen/godot-quarkphysics GitHub Wiki
QSoftBodyNode objects are a type of QBodyNode designed for soft body dynamics. Working with QSoftBodyNode objects requires much finer tuning and a solid understanding of how they function. This tutorial might be the longest one in this series, and you will gain the fundamental knowledge you need.
How to Create a QSoftBodyNode Object?
You can create a QSoftBodyNode object by adding it to the scene tree just like any other node. If you add child nodes such as QMeshRectNode, QMeshPolygonNode, QMeshCircleNode, or custom-designed QMeshAdvancedNode under it, your QSoftBodyNode will be ready to work in the basic sense. You can also make the necessary adjustments via the Inspector. Additionally, it is possible to add it via code.
var my_soft_body=QSoftBodyNode.new()
my_soft_body.global_position=Vector2(500,500)
add_child(my_soft_body)
var my_mesh=QMeshRectNode.new()
my_mesh.rectangle_size=Vector2(128,128)
my_mesh.grid=Vector2(2,2)
my_soft_body.add_child(my_mesh)
How Does QSoftBodyNode Work?
QSoftBodyNode objects apply constraints (such as QSpringObject
, QAngleConstraintObject
, etc.) between the free-moving particles of their child QMeshNode
objects and, if desired, enforce certain approaches that enable them to move together in an organized manner (such as Area Preserving
, Shape Matching
, Self Collisions
, etc.).
We previously mentioned that QRigidBodyNode objects transform particle positions based on an absolute position and rotation, meaning that full control remains with the QRigidBodyNode object. However, in QSoftBodyNode objects, the control of movement actually shifts to the free particles. QSoftBodyNode objects do not have an absolute position or rotation; absolute positions are stored within the particles.
When you add a QSoftBodyNode to the scene tree and set its position and rotation in the editor, these transformations only determine the initial positions of the mesh-defined particles at the start of the simulation. During the simulation, you will notice that the position and rotation values inherited from Node2D remain unchanged for QSoftBodyNode objects.
For this reason, if you want to control QSoftBodyNode objects effectively during the simulation, you will find yourself working with QMeshNode
methods and QParticleObject
methods.
How Can I Control the Particles?
First, you need to access the QMeshNode children of the QSoftBodyNode. QMeshNode objects allow you to manage and modify particles, springs, angle constraints, UV maps, and, if present, collision polygons. However, in this tutorial, you will primarily learn how to control particles in a preconfigured QSoftBodyNode object.
# Get the first QMeshNode of the body
var mesh: QMeshNode = get_mesh_at(0)
# Or, if you know the scene tree, get any QMeshNode using Godot's node hierarchy
var another_way_for_mesh: QMeshNode = get_node("QMeshRectNode")
# Get the first particle
var particle = mesh.get_particle_at(0)
# Change the position of the first particle
particle.set_global_position(Vector2(16, 16))
# Or, directly apply force to the first particle
particle.apply_force(Vector2(0, -16))
# If you want to loop through the particles of the QMeshNode
for i in range(mesh.get_particle_count()):
var p = mesh.get_particle_at(i)
p.set_enabled(false)
You may also want to have safer control over the particles in the simulation. In other words, to ensure that your position changes and applied forces do not disrupt collisions and constraints in your physics world, we can connect to the pre_step signal of QBodyNode.
All modifications made within the pre_step event of a QBodyNode are executed before the physics engine starts processing collisions and constraint iterations for that frame. This allows you to make the desired manipulations and adjustments without conflicting with the physics engine.
In fact, using this approach, you can even implement your own custom constraints.
func _ready() -> void:
#Connect pre_step signal of the body
connect("pre_step", on_pre_step)
func on_pre_step() :
# Apply a force to all particles of the body
for i in range(get_mesh_count() ) :
var mesh=get_mesh_at(i)
for j in range(mesh.get_particle_count() ):
var particle=mesh.get_particle_at(j)
particle.apply_force(Vector2.RIGHT*2.0 )
If you need to apply the same forces to all particles in a QSoftBodyNode, you can also use the apply_force
method inherited from QBodyNode. This method applies the same force to all particles within the QSoftBodyNode.
#Apply force to all particles of QSoftBodyNode
my_soft_body.apply_force(Vector2.RIGHT*2.0)
Note: The
set_body_position
andset_body_rotation
methods inherited from QBodyNode work on QSoftBodyNode objects. However, in a QSoftBodyNode, these methods reposition all particles to their expected locations as if restarting the simulation. This makes them useful for conveniently moving QSoftBodyNode objects from one place to another in the scene.
position
and global_position
in Particles?
What Is the Difference Between The local positions of the particles are their initially defined positions within the QMeshNode and remain constant unless you modify them. These local positions allow the simulation to remember the initial positions of the particles relative to the center of the mesh they belong to.
The global_position, on the other hand, represents the continuously changing world-space positions of the free-moving particles. If you want to modify a particle's position during the simulation, you need to change its global_position
.
If you want to manipulate the constraints set in Shape Matching or Area Preserving applications, you should modify the local positions of the particles. This is because these constraints analyze the ideal and unchanging local positions of the particles.
The Rigidity of QSoftBodyNode
In a QSoftBodyNode, QSpringObject
constraints are used to enforce distance constraints on particles. You can adjust the stiffness of these constraints using the rigidity property of the QSoftBodyNode.
Additionally, if the passivation_of_internal_springs
property is enabled, QSpringObject constraints defined as internal—typically clustered within the inner particles of an object—can be made passive, massless, and adaptive to the outer particles and their connections.
This is generally applied to QParticleObject and QSpringObject elements with the internal property enabled, which are enclosed within a polygon defined by outer particles under Area Preserving constraints. Examples include QMeshPolygonNode and QMeshRectNode, which create internal grids using grid properties. These internal particles are typically used only for carrying UV maps and do not affect the simulation itself.
What Is the Self Collision Feature?
With Self Collision, the particles within a QSoftBodyNode collide with each other based on the radius value set in the mesh. Alternatively, you can assign a specific radius to the particles that will collide by modifying the self_collision_specified_radius
property.
Additionally, if a polygon is defined, it can also collide with itself. The idea of a polygon colliding with itself may seem confusing, but QSoftBodyNode objects are designed to support more complex polygonal structures. For example, in the Welcome scene of the examples project, soft letter objects are created using a single polygon. The extensions of the Q letter, for instance, collide with other extensions of the same polygon.
What Is Area Preserving Feature?
When the Area Preserving feature is enabled, the polygon formed by the assigned particles behaves like a gas balloon, and an area conservation constraint is applied at each simulation step.
The default target area is calculated based on the initially defined local positions of the particles and the polygon they form. During the simulation, free-moving particles experience constraints that force them to maintain this area.
The area_preserving_rate
property in the Inspector determines the extent to which this target area should be preserved.
The area_preserving_rigidity
property determines the stiffness of the constraint applied to maintain the area.
What Is Shape Matching Feature?
When the Shape Matching feature is enabled, an ideal template is generated based on the initially defined positions (local positions) of the particles that form the mesh. Free-moving particles are then constrained to follow this template during the simulation.
In simple terms, regardless of their position in the physics world, particles are forced to return to their assumed undeformed state, preserving the original shape.
- The
shape_matching_rate
property in the Inspector determines the intensity of this constraint. - If the
shape_matching_fixed_transform
property is enabled, you can manually position and rotate this shape-matching template via code, forcing the particles to align with it.
To learn more about all the methods and properties of QSoftBodyNode, refer to the QSoftBodyNode page.