AndrewCS149 / bevy_third_person_camera

A third person camera crate written for Bevy.
Apache License 2.0
67 stars 16 forks source link

This crate doesnt work well, with any type of physics engine. #20

Open Sirmadeira opened 3 months ago

Sirmadeira commented 3 months ago

The scenario is simple. The samples given translate well to the common type of movements that only utilize bevy. But anything that utilizes rapier and XPBD will certainly be broken. Since the fn sync_player_camera doesn't run after the system sets that affect translation and so on. A simple solution would be to make it so the sync_player_camera fn, runs after the physic sets being used. Be it rapier or xpbd. If you read this give me the okay and I will write the pull request.

PS: Love your videos Andrew they helped me a lot. Getting started

Sample on how it should run in rapier:

    app.add_systems(PostUpdate, sync_player_camera.after(PhysicsSet::StepSimulation));
AndrewCS149 commented 3 months ago

Hey! Thanks for bringing this to my attention. By all means open a PR. I use rapier with one of my projects and I haven't experienced any issues with the crate. But if youre experiencing problems and know the fix then im completely cool with a PR

AndrewCS149 commented 3 months ago

Also, glad my videos helped!! 😁

KyWinston commented 2 months ago

Not sure if this is still an issue, but I found that setting this line for rapier as the plugin RapierPhysicsPlugin::::in_fixed_schedule(RapierPhysicsPlugin::::default())

stops the frame jitter in my personal project, and works pretty well as a workaround. downside is how messy it looks IMO

AndrewCS149 commented 2 months ago

@KyWinston hey thanks for the input. I still need to look at this. I've been super busy with work and personal house projects. But I'll try this out when I get around to actually reviewing this. Also if you want, you can totally feel free to make a PR :)

AndrewCS149 commented 2 months ago

@KyWinston Is this project of yours open source? I'd love to take a look if possible.

Sirmadeira commented 2 months ago

Sorry for the delay, to see the jitter you need to apply a force to the collider itself. Not move it using the transform method. As that will teleport it

AndrewCS149 commented 2 months ago

gotcha. 👍

AndrewCS149 commented 2 months ago

@Sirmadeira

So I 'think' I was able to repro the error, but in my case, the tearing/jittering is super minimal. I see it looks pretty bad from the GIF you shared, but I also think the GIF makes it look way worse than it is. Heres my code I used to repro. I tried using ExternalForce as well as ExternalImpulse.

use bevy::prelude::*;
use bevy_rapier3d::{
    dynamics::{Damping, ExternalForce, ExternalImpulse, RigidBody},
    geometry::Collider,
    plugin::{NoUserData, RapierPhysicsPlugin},
    render::RapierDebugRenderPlugin,
};
use bevy_third_person_camera::{
    camera::Zoom, ThirdPersonCamera, ThirdPersonCameraPlugin, ThirdPersonCameraTarget,
};

fn main() {
    App::new()
        .add_plugins((
            DefaultPlugins,
            ThirdPersonCameraPlugin,
            RapierPhysicsPlugin::<NoUserData>::default(),
            // RapierPhysicsPlugin::in_fixed_schedule(RapierPhysicsPlugin::<NoUserData>::default()),
            RapierDebugRenderPlugin::default(),
        ))
        .add_systems(Startup, (camera, player, light, ground))
        .add_systems(Update, player_movement_system)
        .run();
}

fn ground(
    mut cmds: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    cmds.spawn((
        PbrBundle {
            material: materials.add(Color::GREEN),
            mesh: meshes.add(Mesh::from(Plane3d::default().mesh().size(30.0, 30.0))),
            ..default()
        },
        Collider::cuboid(30.0, 0.0, 30.0),
    ));
}

fn camera(
    mut cmds: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    cmds.spawn((
        ThirdPersonCamera {
            zoom: Zoom::new(5.0, 10.0),
            ..default()
        },
        Camera3dBundle::default(),
    ));

    cmds.spawn(PbrBundle {
        mesh: meshes.add(Cuboid::new(2.0, 2.0, 2.0)),
        material: materials.add(Color::RED),
        transform: Transform::from_xyz(0.0, 1.0, -5.0),
        ..default()
    });
}

#[derive(Component)]
struct Player;

fn player(
    mut cmds: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    cmds.spawn((
        PbrBundle {
            mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)),
            material: materials.add(Color::DARK_GRAY),
            transform: Transform::from_xyz(0.0, 0.7, 0.0),
            ..default()
        },
        Player,
        Collider::cuboid(0.5, 0.5, 0.5),
        RigidBody::Dynamic,
        ExternalForce::default(),
        // ExternalImpulse::default(),
        Damping {
            linear_damping: 10.0,
            ..default()
        },
        ThirdPersonCameraTarget,
    ));
}

fn light(mut cmds: Commands) {
    cmds.spawn(DirectionalLightBundle {
        directional_light: DirectionalLight {
            illuminance: 1000.0,
            shadows_enabled: true,
            ..default()
        },
        transform: Transform::from_rotation(Quat::from_euler(
            EulerRot::YXZ,
            150.0f32.to_radians(),
            -40.0f32.to_radians(),
            0.0,
        )),
        ..default()
    });
}

fn player_movement_system(
    keyboard_input: Res<ButtonInput<KeyCode>>,
    mut force_q: Query<&mut ExternalForce, With<Player>>,
    // mut force_q: Query<&mut ExternalImpulse, With<Player>>,
    time: Res<Time>,
) {
    let mut impulse = Vec3::ZERO;

    if keyboard_input.pressed(KeyCode::KeyS) {
        impulse += Vec3::Z;
    }
    if keyboard_input.pressed(KeyCode::KeyW) {
        impulse -= Vec3::Z;
    }
    if keyboard_input.pressed(KeyCode::KeyA) {
        impulse -= Vec3::X;
    }
    if keyboard_input.pressed(KeyCode::KeyD) {
        impulse += Vec3::X;
    }

    for mut forces in force_q.iter_mut() {
        forces.force += impulse * time.delta_seconds() * 55.0;
        // forces.impulse += impulse * time.delta_seconds() * 55.0;
    }
}
AndrewCS149 commented 2 months ago

Also the fix that @KyWinston seemed to have no effect for me.

Sirmadeira commented 2 months ago

Might be minimal in calculation of forces BUT when applying huge forces or constant velocities it gets louco. I think I found a solution I create a state that will run in PostUpdate after XPBD and RAPIER make their calculations avoiding sup crates

Sirmadeira commented 2 months ago

I am gonna make that after i finish my physical modular character, SO i am gonna say on sunday i open a pull request

KyWinston commented 1 month ago

Also the fix that @KyWinston seemed to have no effect for me.

I ended up not using the workaround for too long, as it caused other timing issues on my end, so it's not as good of an option as I thought. The ongoing PR looks great though.

@KyWinston Is this project of yours open source? I'd love to take a look if possible.

I have some crates from the project on crates.io, but it's a bit cluttered atm :sweat_smile: