Sollimann / bonsai

Rust implementation of AI behavior trees.
MIT License
327 stars 15 forks source link

why args.dt is not set to 0 at first tick? #18

Closed PudgeKim closed 2 years ago

PudgeKim commented 2 years ago

I resolved issue by myself. sorry.

Sollimann commented 2 years ago

@PudgeKim Hey, okey, that's great! I saw your posting initally, but didn't have the time to answer before now.

dt and args.dt which you referred to in the 3D example are the same because args = UpdateArgs { dt }, so you can use either.

Also, dt specifies how much into the future you want your behavior tree to advance for a particular tick. Typically, you will set the dt time to be the same time as the 1 / rate_of_you_program e.g 1 / 100Hz = 0.01s. The different processes/nodes in your behavior tree will consume of the value dt, so this value should be strictly decreasing as time goes.

e.g when a node returns, it should return whatever status the node resulted in and the input dt time minus the time it took to process the node (Success, dt-process_time) (see concecpts page for more info, specifially the Events section). The tree will then have a remaining dt = dt - process_time to process other nodes for this tick.

If you have any more questions, just let me know :)

PudgeKim commented 2 years ago

Thanks for answering even though I closed the issue. After looking at your answer, I have a question. Here's my simple example code.

#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq)]
enum Npc {
    Walk,
    Run,
}

fn game_tick(dt: f64, bt: &mut BT<Npc, String, serde_json::Value>) {
    let e: Event = UpdateArgs { dt }.into();

    bt.state.tick(&e, &mut |mut args: bonsai_bt::ActionArgs<Event, Npc>| {
        match *args.action {
            Npc::Walk => {
                println!("walk! dt: {}", args.dt);
                (bonsai_bt::Success, args.dt)
            },
            Npc::Run => {
                println!("run! dt: {}", args.dt);
                (bonsai_bt::Success, args.dt)
            }
        }
    });
}

fn main() {
    let blackboard = HashMap::new();

    let walk = Action(Walk);
    let run = Action(Run);
    let seq = Sequence(vec![walk, run, Wait(200.0)]);
    let behavior = While(Box::new(WaitForever), vec![seq]);

    let mut bt = BT::new(behavior, blackboard);

    loop {
        game_tick(70.0, &mut bt);
        sleep(Duration::from_secs(1));
        println!("1 sec");
    }

}

Here's the output of the code.

walk! dt: 70
run! dt: 70
1 sec
1 sec
walk! dt: 10
run! dt: 10
1 sec
1 sec
1 sec
walk! dt: 20
run! dt: 20
1 sec
1 sec
1 sec
walk! dt: 30
run! dt: 30
1 sec
1 sec
1 sec
...

My expectation of args.dt: tick1: Wait 200 - 70 = 130 tick2: Wait 130 - 70 = 60 tick3: Wait 60 - 70 = -10 tick4: now behavior tree is finished so args.dt would be set to 80(remaining dt 10 + UpdateArgs{dt} )

But tick4 sets args.dt to 10. So args.dt is not updated even though behavior tree is finished. UpdateArgs{dt}.into works after args.dt is set to 0?

Sollimann commented 2 years ago

If you BT runs at intervals of 70, and you wait for 200, then you will be overdue of (200 - 210) = -10 as you say. However, I don't portray overdue time as a negative value because that messes with the timing in the following nodes. so it is basically abs(Wait 60 - 70) = Abs (Wait -10 ) = 10

see implementation here: https://github.com/Sollimann/bonsai/blob/42081d26c7344723d6aa0fd4f1e5a8db57bc591d/bonsai/src/state.rs#L144

Sollimann commented 2 years ago

Here is another question related to time as well, in case that gives you some more clarification: https://github.com/Sollimann/bonsai/issues/10