physme 0.2.4

A simple 2d and 3d physics engine for bevy
Documentation
use bevy::prelude::*;
use physme::prelude3d::*;

pub struct CharacterController {
    on_ground: bool,
    jump: bool,
    camera: Entity,
}

impl CharacterController {
    pub fn new(camera: Entity) -> Self {
        Self {
            on_ground: false,
            jump: false,
            camera,
        }
    }
}

fn main() {
    let mut builder = App::build();
    builder
        .add_default_plugins()
        .add_plugin(Physics3dPlugin)
        .add_resource(GlobalFriction(0.90))
        .add_resource(GlobalStep(0.5))
        .add_startup_system(setup.system());
    let character_system = CharacterControllerSystem::default().system(builder.resources_mut());
    builder.add_system(character_system);
    builder.run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let cube = meshes.add(shape::Cube { size: 0.5 }.into());
    let bigcube = meshes.add(shape::Cube { size: 8.0 }.into());
    let smallcube = meshes.add(shape::Cube { size: 0.2 }.into());
    let mut camera = None;
    let mut anchor = None;
    let mut target = None;
    commands
        .spawn(LightComponents {
            transform: Transform::from_translation(Vec3::new(0.0, 5.0, 5.0)),
            ..Default::default()
        })
        .spawn((Transform::identity(), GlobalTransform::identity()))
        .with_children(|parent| {
            parent.spawn(Camera3dComponents {
                transform: Transform::from_translation_rotation(
                    Vec3::new(0.0, 8.0, 8.0),
                    Quat::from_rotation_x(-45.0_f32.to_radians()),
                ),
                ..Default::default()
            });
        })
        .for_current_entity(|e| camera = Some(e))
        .spawn(PbrComponents {
            mesh: cube,
            material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
            ..Default::default()
        })
        .with(
            RigidBody::new(Mass::Real(1.0))
                .with_status(Status::Semikinematic)
                .with_position(Vec3::new(0.0, 2.0, 0.0)),
        )
        .with(Up::default())
        .with(UpRotation::default())
        .with(CharacterController::new(camera.unwrap()))
        .with_children(|parent| {
            parent.spawn((Shape::from(Size3::new(1.0, 1.0, 1.0)),));
        })
        .for_current_entity(|e| anchor = Some(e))
        .spawn(PbrComponents {
            mesh: smallcube,
            material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
            ..Default::default()
        })
        .with(
            RigidBody::new(Mass::Real(0.5))
                .with_status(Status::Semikinematic)
                .with_position(Vec3::new(10.0, 10.0, 10.0)),
        )
        .with_children(|parent| {
            parent.spawn((Shape::from(Size3::new(0.2, 0.2, 0.2)),));
        })
        .for_current_entity(|e| target = Some(e))
        .spawn((SpringJoint::new(anchor.unwrap(), target.unwrap())
            .with_rigidness(0.5)
            .with_offset(Vec3::new(2.0, 2.0, 0.0)),))
        .spawn(PbrComponents {
            mesh: cube,
            material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
            ..Default::default()
        })
        .with(
            RigidBody::new(Mass::Real(1.0))
                .with_status(Status::Semikinematic)
                .with_position(Vec3::new(5.0, 5.0, 5.0)),
        )
        .with_children(|parent| {
            parent.spawn((Shape::from(Size3::new(1.0, 1.0, 1.0)),));
        })
        .spawn(PbrComponents {
            mesh: bigcube,
            material: materials.add(Color::rgb(0.2, 0.8, 0.2).into()),
            ..Default::default()
        })
        .with(
            RigidBody::new(Mass::Infinite)
                .with_status(Status::Static)
                .with_position(Vec3::new(0.0, -8.0, 0.0)),
        )
        .with_children(|parent| {
            parent.spawn((Shape::from(Size3::new(16.0, 16.0, 16.0)),));
        })
        .spawn(PbrComponents {
            mesh: bigcube,
            material: materials.add(Color::rgb(0.5, 0.5, 0.5).into()),
            ..Default::default()
        })
        .with(
            RigidBody::new(Mass::Infinite)
                .with_status(Status::Static)
                .with_position(Vec3::new(0.0, -7.0, -16.0))
                .with_rotation(Quat::from_rotation_x(10.0_f32.to_radians())),
        )
        .with_children(|parent| {
            parent.spawn((Shape::from(Size3::new(16.0, 16.0, 16.0)),));
        })
        .spawn(PbrComponents {
            mesh: smallcube,
            material: materials.add(Color::rgb(0.2, 0.8, 0.2).into()),
            ..Default::default()
        })
        .with(
            RigidBody::new(Mass::Infinite)
                .with_status(Status::Static)
                .with_position(Vec3::new(-3.0, 0.0, -3.0)),
        )
        .with_children(|parent| {
            parent.spawn((Shape::from(Size3::new(0.4, 0.4, 0.4)),));
        });
}

#[derive(Default)]
pub struct CharacterControllerSystem {
    reader: EventReader<Manifold>,
}

impl CharacterControllerSystem {
    pub fn system(self, res: &mut Resources) -> Box<dyn System> {
        let system = character_system.system();
        res.insert_local(system.id(), self);
        system
    }
}

fn character_system(
    mut state: Local<CharacterControllerSystem>,
    time: Res<Time>,
    input: Res<Input<KeyCode>>,
    manifolds: Res<Events<Manifold>>,
    up: Res<GlobalUp>,
    ang_tol: Res<AngularTolerance>,
    mut query: Query<(Mut<CharacterController>, Mut<RigidBody>, Mut<UpRotation>)>,
    camera: Query<Mut<Transform>>,
) {
    let delta_time = time.delta.as_secs_f32();
    for manifold in state.reader.iter(&manifolds) {
        let dot = up.0.dot(manifold.normal);
        let angle = (-dot).acos();
        let angle2 = dot.acos();
        if angle >= 0.0 && angle < ang_tol.0 {
            if let Ok(mut controller) = query.get_mut::<CharacterController>(manifold.body1) {
                controller.on_ground = true;
            }
        } else if angle2 >= 0.0 && angle2 < ang_tol.0 {
            if let Ok(mut controller) = query.get_mut::<CharacterController>(manifold.body2) {
                controller.on_ground = true;
            }
        }
    }

    for (mut controller, mut body, mut rotation) in &mut query.iter() {
        if input.just_pressed(KeyCode::Space) {
            controller.jump = true;
        }
        if controller.on_ground {
            if controller.jump {
                body.apply_force(Vec3::new(0.0, 500.0, 0.0));
                controller.jump = false;
            }
        }
        if input.pressed(KeyCode::Q) {
            rotation.0 += 1.0 * delta_time;
        }
        if input.pressed(KeyCode::E) {
            rotation.0 -= 1.0 * delta_time;
        }
        if input.pressed(KeyCode::W) {
            let impulse = body.rotation * Vec3::new(0.0, 0.0, -0.5);
            body.apply_linear_impulse(impulse);
        }
        if input.pressed(KeyCode::S) {
            let impulse = body.rotation * Vec3::new(0.0, 0.0, 0.5);
            body.apply_linear_impulse(impulse);
        }
        if input.pressed(KeyCode::A) {
            let impulse = body.rotation * Vec3::new(-0.5, 0.0, 0.0);
            body.apply_linear_impulse(impulse);
        }
        if input.pressed(KeyCode::D) {
            let impulse = body.rotation * Vec3::new(0.5, 0.0, 0.0);
            body.apply_linear_impulse(impulse);
        }
        controller.on_ground = false;

        let pitch = rotation.0;
        if let Ok(mut transform) = camera.get_mut::<Transform>(controller.camera) {
            transform.set_translation(body.position);
            transform.set_rotation(Quat::from_rotation_y(pitch));
        }
    }
}