Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom gravity integration into physics pipeline #543

Open
robinlacey opened this issue Oct 28, 2023 · 2 comments
Open

Custom gravity integration into physics pipeline #543

robinlacey opened this issue Oct 28, 2023 · 2 comments

Comments

@robinlacey
Copy link

Hello,

I have a basic gravity system in Bevy that applies a force the player when they are close to a Planet object.

I'm currently using sensors to update a list of planets_in_range near by to reduce overhead as there are a lot of planets.

I'd like to simulate the trajectory of a Player in this system. This would provide a dotted line showing where the player will go.

Ideally this would be running the .step function a number of times. However the gravity system (below) is external to rapier and within the ECS.

QUESTION: How could I nicely integrate this custom logic into the Rapier physics pipeline?

Thanks!

pub fn apply_gravity_off_planet(
    mut players: Query<(&mut ExternalForce, &mut Transform, &mut Velocity)>,
    planets_in_range: ResMut<PlanetsInRange>,
    landed_on_planet: ResMut<LandedOnPlanet>,
) {

    if  landed_on_planet.0.is_some(){
        return;
    }
    let exerting_planets =  planets_in_range.0.as_ref().cloned().unwrap_or_else(Vec::new);
    for (mut external_force, player_transform, mut _velocity) in players.iter_mut() {
        let player_pos = player_transform.translation;
        for planet in &exerting_planets {
            let planet_position = planet.world_position;
            let distance_to_center_of_planet = (player_pos - planet_position).length();
            let direction_to_planet = (planet_position - player_pos).normalize();
            let gravity_distance = planet.get_max_gravity_distance()-planet.radius;
            let current_planet_gravity_force =  1.0- ((distance_to_center_of_planet-planet.radius)/gravity_distance).clamp(0.0,1.0);
            let current_gravity_multiplier = current_planet_gravity_force * planet.get_gravity_strength_multiplier();
            let force_magnitude = 10.0 / distance_to_center_of_planet.powi(2);
            let force = direction_to_planet * force_magnitude * current_gravity_multiplier;
            external_force.force += Vec2 { x: force.x, y: force.y };
        }
    }
}
@LKwakernaak
Copy link

LKwakernaak commented Oct 31, 2023

As I understand it, there is no elegant way to calculate such a trajectory with rapier as it is focused mostly on figuring out (arguably the much more difficult) rigid body contact and joint constraints as quickly as possible. I don't believe there is a way to calculate the forces on bodies every physics step based on a potential (such as due to gravity). There are just the user-definable external forces that you are updating in a Bevy system which should be good enough for your simulations (but might lead to instability if the external force is not updated frequently enough compared to the rate of physics steps in rapier).

One thing you could do, to calculate the trajectory, is to run a separate rapier physics pipeline with a limited number of bodies and crucially, including your constant updates of the external forces. That would be most accurate to what rapier would calculate and even capture the trajectory after a collision with a planets Sounds like a lot of work though.

The simpler thing you could do, is to integrate the position of the body yourself. As long as there won't be any collisions and the planets don't move or just move very predictably, this should be easy enough.
Rapier uses a symplectic-Euler integration method for calculating the positions and velocities of bodies over time, but for a short trajectory, explicit-Euler (the simplest of the integrators) should work. There are plenty of resources online that explain Euler integration very well but the basic algorithm is just this:

pos_new = pos_old + dt * vel_old;

vel_new = vel_old + dt * force(pos_old)/mass;

calculate that for a bunch of small time-steps and you're set. You've already solved the hard part yourself in calculating the forces.

Another option, specifically for planetary gravity, is to solve the Kepler orbit directly for just the nearest planet or around the center of mass of a nearby cluster of planets.

Ideally, there would be a query for this in rapier. Currently you can only query straight trajectories for objects moving at constant velocities to figure out when it is going to hit an object (which is quite a difficult problem). Querying parabolic trajectories is currently not possible. Funnily enough tough, as solving this problem for cannonballs is arguably what led to the development of modern calculus and physics.

@robinlacey
Copy link
Author

This is really helpful, thank you. I'll have a go implementing and post results here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants