Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Physics

Use case: a prop that reacts to hits

The goal: a box that simulates physics, is pushed where it is hit, and breaks after enough damage.

#![allow(unused)]
fn main() {
use bindings::engine::{ECollisionChannel, KismetSystemLibrary, PrimitiveComponentExt, StaticMeshComponent};

#[uclass(parent = Actor)]
pub struct Crate {
    #[component(root)]
    mesh: StaticMeshComponent,

    #[uproperty(EditAnywhere, name = "CurrentHP", default = 30.0)]
    current_hp: f32,
}

#[uclass_impl]
impl Crate {
    #[class_defaults]
    fn class_defaults(&mut self) -> RustealResult<()> {
        let mesh = self.mesh()?.checked()?;
        mesh.set_collision_profile_name(FName::new("BlockAllDynamic").handle(), Some(true));
        mesh.set_simulate_physics(true);
        mesh.set_can_ever_affect_navigation(false);
        Ok(())
    }
}

impl Crate {
    pub fn hit(&mut self, damage: f32, location: DVec3, impulse: DVec3) {
        self.set_current_hp(self.current_hp() - damage);

        if let Ok(mesh) = self.mesh().and_then(|m| m.checked()) {
            let scaled = impulse * f64::from(mesh.get_mass());
            mesh.add_impulse_at_location(&FVector::from_dvec3(scaled), &FVector::from_dvec3(location), None);

            if self.current_hp() <= 0.0 {
                mesh.set_collision_object_type(ECollisionChannel::ECC_Visibility);
            }
        }
    }
}
}

add_impulse_at_location pushes and spins the body from the point that was hit. Multiplying by the mass makes the push the same for a light and a heavy prop; add_impulse(impulse, bone, vel_change = Some(true)) does that for you.

Use case: knockback on a character

A character’s capsule does not simulate physics; its movement component takes the impulse:

#![allow(unused)]
fn main() {
if let Ok(movement) = me.get_character_movement().checked() {
    movement.add_impulse(&FVector::from_dvec3(knockback), Some(true));
}
}

Or replace its velocity at once with launch_character.

Use case: a hit reaction with partial ragdoll

#![allow(unused)]
fn main() {
let mesh = me.get_mesh().checked()?;
mesh.set_physics_blend_weight(0.5);
mesh.set_all_bodies_below_simulate_physics(FName::new("pelvis").handle(), true, Some(false));
}

The combat template blends the ragdoll in when hit and back out on landing (set_physics_blend_weight(0.0) in on_landed).

Use case: a physics constraint

The goal: a training dummy that wobbles on its base.

#![allow(unused)]
fn main() {
use bindings::engine::{PhysicsConstraintComponent, PhysicsConstraintComponentExt};

#[uclass(parent = Actor)]
pub struct Dummy {
    #[component(root)]
    root: SceneComponent,

    #[component(attach = "root", name = "Base Plate")]
    base_plate: StaticMeshComponent,

    #[component(attach = "root")]
    dummy: StaticMeshComponent,

    #[component(attach = "root", name = "Physics Constraint")]
    constraint: PhysicsConstraintComponent,
}

#[uclass_impl]
impl Dummy {
    #[class_defaults]
    fn class_defaults(&mut self) -> RustealResult<()> {
        self.dummy()?.checked()?.set_simulate_physics(true);
        Ok(())
    }

    #[ufunction(Override)]
    fn receive_begin_play(&mut self) {
        if let (Ok(constraint), Ok(base_plate), Ok(dummy)) =
            (self.constraint().and_then(|c| c.checked()), self.base_plate(), self.dummy())
        {
            constraint.set_constrained_components(
                base_plate.upcast_to(),
                FName::NONE.handle(),
                dummy.upcast_to(),
                FName::NONE.handle(),
            );
        }
    }
}
}

The constraint’s limits (swing, twist) are set on the component in the Blueprint child’s Details panel.

A chain is the same thing repeated: the interactables feature’s BP_DangleBlocks has four cubes and four constraints, the first holding the top cube to the world (its Component Name 1 names no component) and each other one a cube to the one above, with where each piece sits and how far each joint swings. Its Rust parent, DangleBlocks, has no code: the Blueprint is all data.

Collision presets and channels

CallWhat it does
set_collision_profile_name(FName::new("BlockAllDynamic").handle(), Some(true))Applies a preset from Project Settings > Collision.
set_collision_enabled(ECollisionEnabled::QueryOnly)Overlaps and traces, no physics.
set_collision_object_type(ECollisionChannel::ECC_WorldDynamic)What the component is, for others’ responses.
set_collision_response_to_all_channels(ECollisionResponse::ECR_Ignore)Ignores everything…
set_collision_response_to_channel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap)…then overlaps pawns.

A component meant to fire overlap events also needs Generate Overlap Events on, which most presets have.