Module joints

Source
Expand description

Joints are a way to connect rigid bodies in a way that restricts their movement relative to each other. They act as constraints that restrict different Degrees of Freedom depending on the joint type.

§Degrees of Freedom (DOF)

In 2D, rigid bodies can normally translate along the x and y axes and rotate about the z axis. Therefore, they have 2 translational DOF and 1 rotational DOF, a total of 3 DOF.

x-axis translation z-axis rotation y-axis translation

Joints limit the degrees of freedom that bodies can have. For example, a RevoluteJoint or hinge prevents any relative movement between two bodies, except for rotation about a single axis at an anchor point.

Below is a table containing all joints that are currently implemented.

JointAllowed 2D DOFAllowed 3D DOF
FixedJointNoneNone
DistanceJoint1 Translation, 1 Rotation2 Translations, 3 Rotations
PrismaticJoint1 Translation1 Translation
RevoluteJoint1 Rotation1 Rotation

§Using Joints

In Avian, joints are modeled as components. Each joint is spawned as its own entity, providing the Entity identifiers of the bodies it should constrain.

use avian2d::prelude::*;
use bevy::prelude::*;

fn setup(mut commands: Commands) {
    let body1 = commands.spawn(RigidBody::Dynamic).id();
    let body2 = commands.spawn(RigidBody::Dynamic).id();
     
    // Connect the bodies with a fixed joint.
    commands.spawn(FixedJoint::new(body1, body2));
}

By default, the attached bodies can still collide with each other. This behavior can be disabled with the JointCollisionDisabled component.

§Joint Frames

By default, joints use body transforms as their reference for how to constrain the connected bodies. For example, a RevoluteJoint aims to make the positions of the two bodies coincide in world space, while allowing the bodies to rotate freely around a common axis.

However, it can often be useful to define the attachment point or orientation separately from the body transform. For example, you may want the RevoluteJoint to be attached to the corner of a body instead of its center, and that the other body is rotated by 90 degrees relative to the first body.

This can be done by configuring the JointFrame associated with each body. Each joint frame is expressed by a local JointAnchor and JointBasis relative to the transforms of the bodies. The anchor determines the attachment point, while the basis determines the orientation of the joint frame relative to the body transform.

anchor body1 body2 basis

Storing the frames in local space allows the initial configuration to be preserved even when the bodies are moved. The frames can also be specified in global coordinates using JointFrame::global, but they are automatically converted to local frames during the next simulation step.

Below is an example of configuring JointFrames for a RevoluteJoint.

// Connect two bodies with a revolute joint.
// Set the global anchor point and rotate the first frame by 45 degrees about the local z axis.
commands.spawn((
    RevoluteJoint::new(body1, body2)
        .with_anchor(Vec2::new(5.0, 2.0))
        .with_local_basis1(PI / 4.0),
));

§Damping

By default, no work is done to dampen the movement of bodies connected by a joint. A pendulum will swing indefinitely, unless explicitly stopped or it loses energy due to simulation inaccuracies.

It can often be desirable to dampen the relative velocities of bodies connected by a joint to slow them down over time. This can be done using the JointDamping component.

// Connect two bodies with a distance joint.
// Apply linear and angular damping to the joint.
commands.spawn((
    DistanceJoint::new(body1, body2),
    JointDamping {
        linear: 0.1,  // Linear damping
        angular: 0.1, // Angular damping
    },
));

§Reading Joint Forces

Joints apply forces and torques to constrain the bodies they are attached to. These forces can be read by adding the JointForces component to the joint entity:

// Connect two bodies with a revolute joint.
// Read the forces applied by the joint.
commands.spawn((
    RevoluteJoint::new(body1, body2),
    JointForces::new(),
));

and querying for it in a system:

fn read_joint_forces(query: Query<&JointForces>) {
    for joint_forces in &query {
        println!("Joint force: {}", joint_forces.force());
    }
}

This can often be useful for determining when to “break” a joint with the JointDisabled component when its forces exceed a certain threshold. An example of this can be found in the next section on disabling joints.

§Disabling Joints

It can sometimes be useful to temporarily disable a joint without removing it from the world. This can be done by adding the JointDisabled component to the joint entity.

A common use case is to “break” a joint when its JointForces exceed a certain threshold. This could be done with a system like the following:

const BREAK_THRESHOLD: f32 = 500.0; // Example threshold

fn break_joints(
    mut commands: Commands,
    query: Query<(Entity, &JointForces), Without<JointDisabled>>,
) {
    for (entity, joint_forces) in &query {
        if joint_forces.force().length() > BREAK_THRESHOLD {
            // Break the joint by adding the `JointDisabled` component.
            // Alternatively, you could simply remove the joint component or despawn the entity.
           commands.entity(entity).insert(JointDisabled);
        }
    }
}

Disabled joints can be re-enabled by removing the JointDisabled component.

§Other Configuration

Different joints may have different configuration options. They may allow you to change the axis of allowed translation or rotation, and can have distance or angle limits for those axes.

Take a look at the documentation and methods of each joint to see all the different configuration options.

Structs§

AngleLimit
A limit that indicates that angles should be between alpha and beta.
DistanceJoint
A distance joint maintains an upper and/or lower bound on the distance between anchor points on two bodies.
DistanceLimit
A limit that indicates that the distance between two points should be between min and max.
FixedJoint
A fixed joint prevents any relative movement between two bodies, effectively locking them together.
JointCollisionDisabled
A marker component that disables collision between rigid bodies connected by a joint. Must be on the same entity as the joint.
JointDamping
A component for applying damping to the relative linear and angular velocities of bodies connected by a joint.
JointDisabled
A marker component that indicates that a joint is disabled and should not constrain the bodies it is attached to. Must be on the same entity as the joint.
JointForces
A component for reading the force and torque exerted by a joint.
JointFrame
The reference frame of a body that is being constrained by a joint.
JointPlugin
A plugin for managing and initializing joints.
PrismaticJoint
A prismatic joint prevents any relative movement between two bodies, except for translation along the slider_axis.
RevoluteJoint
A revolute joint or hinge prevents any relative movement between two bodies, except for rotation about a pivot point defined by the joint anchor.

Enums§

JointAnchor
The translation of a JointFrame, defining the anchor point where the bodies are attached to each other.
JointBasis
The rotation of a JointFrame, defining the basis of the joint frame relative to the body transform.
JointSystems
System sets for joints.

Traits§

EntityConstraint
A trait for constraints between entities.