Skip to content

Combine LinearVelocity and AngularVelocity under Velocity (and similarly with MaxSpeed and Damping)#958

Open
Jondolf wants to merge 9 commits into
mainfrom
velocity-component
Open

Combine LinearVelocity and AngularVelocity under Velocity (and similarly with MaxSpeed and Damping)#958
Jondolf wants to merge 9 commits into
mainfrom
velocity-component

Conversation

@Jondolf
Copy link
Copy Markdown
Member

@Jondolf Jondolf commented Mar 18, 2026

Objective

Currently, velocity is split into LinearVelocity and AngularVelocity:

fn setup(mut commands: Commands) {
	commands.spawn((
	    RigidBody::Dynamic,
	    LinearVelocity(Vec3::new(2.0, 0.0, 0.0)),
	));
}


fn accelerate_linear(mut query: Query<&mut LinearVelocity>, time: Res<Time>) {
    let delta_secs = time.delta_secs();
    for mut linear_velocity in &mut query {
        // Accelerate towards +X
        linear_velocity.x += 2.0 * delta_secs;
        // Damping
        linear_velocity.0 *= 1.0 / (1.0 + delta_secs * 0.9);
    }
}

This has some nice aspects:

  • Granular, systems processing linear and angular components separately can run in parallel.
  • Modifying velocity is very ergonomic, as you can access x, y, and z directly.

However, there are also some prominent downsides:

  • Documentation is more scattered around and duplicated. Linear and angular velocity are fundamentally both "velocity", just different components of it.
  • Queries are longer, especially if you need both linear and angular velocity.
  • Accessing the inner vector with .0 is "ugly" and doesn't feel like a polished official API (subjectively speaking).
  • No at_point helper method, since point velocity needs both linear and angular velocity. This is something users have frequently complained about!

Solution

Replace LinearVelocity and AngularVelocity with a single Velocity component:

#[derive(Component)]
pub struct Velocity {
    pub linear: Vector,
    pub angular: AngularVector,
}

I suspect that this will be somewhat contentious, but let's take a look at some examples of what the API looks like.

The earlier example would look like this:

fn setup(mut commands: Commands) {
	commands.spawn((
	    RigidBody::Dynamic,
	    Velocity::from_linear(Vec3::new(2.0, 0.0, 0.0)),
	));
}


fn accelerate_linear(mut query: Query<&mut Velocity>, time: Res<Time>) {
    let delta_secs = time.delta_secs();
    for mut velocity in &mut query {
        // Accelerate towards +X
        velocity.linear.x += 2.0 * delta_secs;
        // Damping
        velocity.linear *= 1.0 / (1.0 + delta_secs * 0.9);
    }
}

There are several helpers for constructing Velocity:

// Velocity with linear and angular components
Velocity {
    linear: Vec3::new(10.0, 0.0, 0.0),
    angular: Vec3::new(0.0, 0.0, 1.5),
},
Velocity::new(Vec3::new(10.0, 0.0, 0.0), Vec3::new(0.0, 0.0, 1.5)),

// Velocity with only the linear component
Velocity::from_linear(Vec3::new(10.0, 0.0, 0.0)),
Velocity::from_linear_xyz(10.0, 0.0, 0.0),

// Velocity with only the angular component
Velocity::from_angular(Vec3::new(0.0, 0.0, 1.5)),
Velocity::from_angular_xyz(0.0, 0.0, 1.5),

Point velocity can be computed using at_point:

// Compute the velocity at a point 1 unit to the right of the center of mass.
let point_velocity = velocity.at_point(Vec3::new(1.0, 0.0, 0.0));

I believe this kind of combined API has several benefits:

  • Documentation for all velocity data is on a single component.
  • More concise when both linear and angular velocity are needed.
  • Looks cleaner and more first-party (subjectively speaking), no accessing .0.
  • More similar to Transform APIs (translation and rotation on the same component).
  • We can have a point velocity helper.

I think the main downside is that systems operating on linear and angular velocity separately cannot run in parallel, but IME this is not really a problem in practice, and Avian itself doesn't currently benefit from that kind of parallelism.

Similarly, I combined MaxLinearSpeed and MaxAngularSpeed as MaxSpeed, and LinearDamping and AngularDamping as Damping:

#[derive(Component)]
pub struct MaxSpeed {
    pub linear: Scalar,
    pub angular: Scalar,
}

#[derive(Component)]
pub struct Damping {
    pub linear: Scalar,
    pub angular: Scalar,
}

This makes the API more consistent and again focuses documentation for concepts like "maximum speed" and "damping" under a single component instead of spreading it out over multiple types.

I recommend looking at the PR code diff to get a better idea of what the APIs look like in practice.

Follow-Up Work

  • Combine Position and Rotation under PhysicsTransform
  • Add helpers for position and velocity integration on the components themselves

@Jondolf Jondolf added this to the 0.7 milestone Mar 18, 2026
@Jondolf Jondolf added A-Dynamics Relates to rigid body dynamics: motion, mass, constraint solving, joints, CCD, and so on M-Migration-Guide A breaking change to Avian's public API that needs to be noted in a migration guide X-Contentious There are nontrivial implications that should be thought through C-Usability A quality-of-life improvement that makes Avian easier to use S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Mar 18, 2026
@Jondolf Jondolf marked this pull request as ready for review March 19, 2026 01:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Dynamics Relates to rigid body dynamics: motion, mass, constraint solving, joints, CCD, and so on C-Usability A quality-of-life improvement that makes Avian easier to use M-Migration-Guide A breaking change to Avian's public API that needs to be noted in a migration guide S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged X-Contentious There are nontrivial implications that should be thought through

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant