最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

rust - Calculating acceleration to stop at a destination point in a given amount of time - Stack Overflow

programmeradmin2浏览0评论

I'm programming a game in the Bevy engine, and I'm trying to move an object from a starting point to a destination point in a certain amount of time. The starting position, destination position, initial speed, and time the move should take are all passed in to build_move_to. That function builds a MoveTo struct that holds the destination point and velocity, both Vec2s, and an acceleration vector that is calculated when the MoveTo struct is built.

pub struct MoveTo {
    pub destination: Vec2,
    pub velocity: Vec2,
    pub acceleration: Vec2,
}

pub fn build_move_to(builder: MoveToBuilder) -> MoveTo {
    let acceleration = find_accel_to_stop_at_destination(
        builder.start,
        builder.destination,
        builder.speed,
        builder.time
    );
    let diff = builder.destination - builder.start;
    let angle = diff.y.atan2(diff.x);
    let movement_direction = Vec3::new(angle.cos(), angle.sin(), 0.0);
    MoveTo {
        destination: builder.destination,
        velocity: builder.speed * movement_direction.truncate(),
        acceleration,
    }
}

fn find_accel_to_stop_at_destination(start: Vec2, dest: Vec2, speed: f32, time: f32) -> Vec2 {
    let displacement = dest - start;
    let velocity = displacement.normalize() * speed;
    let acceleration = (2.0 * (displacement - (velocity * time))) / (time * time);
    acceleration
}

Finally, I have a do_move function that runs every frame and moves the object's Transform (its position in the world) towards the destination point:

impl MovementPattern for MoveTo {
    fn do_move(&mut self, transform: &mut Transform, time: &Res<Time>) -> () {
        // stop object when it gets close enough to the destination
        if transform.translation.truncate().abs_diff_eq(self.destination, 1.0) {
            self.velocity = Vec2::ZERO;
            return;
        }

        let delta_time = time.delta_secs();
        self.velocity += self.acceleration * delta_time;
        let translation_delta = self.velocity * delta_time;
        transform.translation += translation_delta.extend(0.0);
    }
}

The problem is that the velocity is well above zero when the object reaches the destination. For example, say builder.start is [-128.0, 241.0], the builder.destination is [-128.0, 166.0], builder.speed is 100, and the builder.time is 10.0 (in seconds). The acceleration is calculated as [0, 18.5], and the velocity starts at [-0, -100] (or very close to it). I believe the acceleration is correct, but the object reaches the destination point way too soon, after only two seconds or so, and it's velocity then is [-0, -85]. I want it to reach the destination at [0, 0] velocity at exactly ten seconds. What am I doing wrong?

I'm programming a game in the Bevy engine, and I'm trying to move an object from a starting point to a destination point in a certain amount of time. The starting position, destination position, initial speed, and time the move should take are all passed in to build_move_to. That function builds a MoveTo struct that holds the destination point and velocity, both Vec2s, and an acceleration vector that is calculated when the MoveTo struct is built.

pub struct MoveTo {
    pub destination: Vec2,
    pub velocity: Vec2,
    pub acceleration: Vec2,
}

pub fn build_move_to(builder: MoveToBuilder) -> MoveTo {
    let acceleration = find_accel_to_stop_at_destination(
        builder.start,
        builder.destination,
        builder.speed,
        builder.time
    );
    let diff = builder.destination - builder.start;
    let angle = diff.y.atan2(diff.x);
    let movement_direction = Vec3::new(angle.cos(), angle.sin(), 0.0);
    MoveTo {
        destination: builder.destination,
        velocity: builder.speed * movement_direction.truncate(),
        acceleration,
    }
}

fn find_accel_to_stop_at_destination(start: Vec2, dest: Vec2, speed: f32, time: f32) -> Vec2 {
    let displacement = dest - start;
    let velocity = displacement.normalize() * speed;
    let acceleration = (2.0 * (displacement - (velocity * time))) / (time * time);
    acceleration
}

Finally, I have a do_move function that runs every frame and moves the object's Transform (its position in the world) towards the destination point:

impl MovementPattern for MoveTo {
    fn do_move(&mut self, transform: &mut Transform, time: &Res<Time>) -> () {
        // stop object when it gets close enough to the destination
        if transform.translation.truncate().abs_diff_eq(self.destination, 1.0) {
            self.velocity = Vec2::ZERO;
            return;
        }

        let delta_time = time.delta_secs();
        self.velocity += self.acceleration * delta_time;
        let translation_delta = self.velocity * delta_time;
        transform.translation += translation_delta.extend(0.0);
    }
}

The problem is that the velocity is well above zero when the object reaches the destination. For example, say builder.start is [-128.0, 241.0], the builder.destination is [-128.0, 166.0], builder.speed is 100, and the builder.time is 10.0 (in seconds). The acceleration is calculated as [0, 18.5], and the velocity starts at [-0, -100] (or very close to it). I believe the acceleration is correct, but the object reaches the destination point way too soon, after only two seconds or so, and it's velocity then is [-0, -85]. I want it to reach the destination at [0, 0] velocity at exactly ten seconds. What am I doing wrong?

Share Improve this question asked Feb 21 at 4:34 MaxMax 1,3726 gold badges17 silver badges33 bronze badges 4
  • 1 It looks to me like you don't have a good grip on the physics. You need to use the four equations of motion and remember that for any given starting conditions you need to calculate the values you don't have. E.g. To decelerate from a give speed to finish at a given spot you'll need to calculate the acceleration and time. You can't plug in whatever numbers you want. The equations will give you those values. – Tangentially Perpendicular Commented Feb 21 at 4:46
  • What do you mean? I'm plugging in the values that are known at the start. I know the distance I'm trying to go, the time I want to go in, and the velocity I'm starting with. That should imply a specific acceleration, no? – Max Commented Feb 21 at 5:01
  • 1 you don't need to turn diff to angle to movement_direction, you can just diff.normalize() to get the direction vector. – 啊鹿Dizzyi Commented Feb 21 at 6:33
  • 2 You have too many requirements in your case to use a constant acceleration, it is over-determined. You cannot fix distance, start and end speed, and time interval and expect a single constant acceleration. This is meant by "[no] good grip on the physics." VTC because this is not about programming but physics. – the busybee Commented Feb 21 at 7:09
Add a comment  | 

1 Answer 1

Reset to default 1

In constant acceleration motion, you have variable: distance, initial velocity, final velocity, acceleration, duration. Due to the mathematical property of the motion, you can only constrain 3 variables to make a motion.

by constraining distance (start and end), inital velocity and time, you gave up control over final velocity.

Example

depend on your desired outcome, you can pick two more variables to constrain (since start and end already exists). i.e. from_dt_v0, from_dt_vf, from_v0_vf.

use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, startup)
        .add_systems(Update, update)
        .run();
}

#[derive(Debug, Component)]
pub struct MoveTo {
    pub direction: Vec3,
    pub velocity: f32,
    pub acceleration: f32,

    pub duration: f32,
    pub elapsed: f32,
}

impl MoveTo {
    // create a movement by constraining duration and inital velocity
    fn from_dt_v0(src: Transform, dst: Transform, dt: f32, v0: f32) -> MoveTo {
        let elapsed = 0.0;

        let diff = dst.translation - src.translation;
        let dist = diff.length();
        let direction = diff.normalize();

        let velocity = v0;
        let duration = dt;
        let acceleration = (dist - velocity * dt) * 2.0 / dt / dt;

        MoveTo {
            direction,
            velocity,
            acceleration,
            duration,
            elapsed,
        }
    }

    // create a movement by constraining inital and final velocity
    fn from_dt_vf(src: Transform, dst: Transform, dt: f32, vf: f32) -> MoveTo {
        let elapsed = 0.0;

        let diff = dst.translation - src.translation;
        let dist = diff.length();
        let direction = diff.normalize();

        let velocity = dist * 2.0 / dt - vf;
        let duration = dt;
        let acceleration = (vf - velocity) / dt;

        MoveTo {
            direction,
            velocity,
            acceleration,
            duration,
            elapsed,
        }
    }

    // create a movement by constraining duration and final velocity
    fn from_v0_vf(src: Transform, dst: Transform, v0: f32, vf: f32) -> MoveTo {
        let elapsed = 0.0;

        let diff = dst.translation - src.translation;
        let dist = diff.length();
        let direction = diff.normalize();

        let velocity = v0;
        let duration = dist * 2.0 / (vf + v0);
        let acceleration = (vf - v0) / duration;

        MoveTo {
            direction,
            velocity,
            acceleration,
            duration,
            elapsed,
        }
    }
}

fn src() -> Transform {
    Transform::from_xyz(-200.0, -160.0, 0.0)
}
fn dst() -> Transform {
    Transform::from_xyz(200.0, 160.0, 0.0)
}

fn startup(
    mut cmd: Commands,
    mut meshs: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    cmd.spawn(Camera2d::default());
    // spawn destination marker
    cmd.spawn((
        Mesh2d(meshs.add(Circle::new(10.0))),
        MeshMaterial2d(materials.add(Color::hsl(0.0, 0.8, 0.8))),
        dst(),
    ));
    // spawn source marker
    cmd.spawn((
        Mesh2d(meshs.add(Circle::new(10.0))),
        MeshMaterial2d(materials.add(Color::hsl(0.0, 0.8, 0.8))),
        src(),
    ));
    

    // cmd.spawn((
    //     Mesh2d(meshs.add(Circle::new(5.0))),
    //     MeshMaterial2d(materials.add(Color::hsl(180.0, 0.8, 0.8))),
    //     src(),
    //     MoveTo::from_dt_v0(src(), dst(), 5.0, 10.0),
    // ));

    cmd.spawn((
        Mesh2d(meshs.add(Circle::new(5.0))),
        MeshMaterial2d(materials.add(Color::hsl(180.0, 0.8, 0.8))),
        src(),
        MoveTo::from_dt_vf(src(), dst(), 5.0, 0.0),
    ));

    // cmd.spawn((
    //     Mesh2d(meshs.add(Circle::new(5.0))),
    //     MeshMaterial2d(materials.add(Color::hsl(180.0, 0.8, 0.8))),
    //     src(),
    //     MoveTo::from_v0_vf(src(), dst(), 50.0, 0.0),
    // ));
}

fn update(
    mut cmd: Commands,
    mut query: Query<(&mut Transform, &mut MoveTo, Entity)>,
    time: Res<Time>,
) {
    let dt = time.delta_secs();
    for (mut t, mut m, id) in query.iter_mut() {
        if m.elapsed > m.duration {
            cmd.entity(id).despawn();
            continue;
        }
        m.velocity += m.acceleration * dt;
        t.translation += m.direction * m.velocity * dt;
        m.elapsed += dt;
    }
}
发布评论

评论列表(0)

  1. 暂无评论