2 unstable releases

0.3.0 Aug 11, 2024
0.1.0 Jul 22, 2024

#1750 in Game dev

MIT license

80KB
1K SLoC

bevy_dogoap - Integration of dogoap into Bevy's ECS model

License Crates.io Downloads Docs ci

Full Bevy + bevy_dogoap example:

use bevy::prelude::*;
use bevy_dogoap::prelude::*;

// Define a DatumComponent as a struct with one member that can be either bool, f64 or u64
#[derive(Component, Clone, DatumComponent)]
struct IsHungry(bool);

// Define a ActionComponent as a struct without any fields
#[derive(Component, Reflect, Clone, Default, ActionComponent)]
struct EatAction;

fn startup(mut commands: Commands) {
    // Set our goal to be that we shouldn't be hungry
    let goal = Goal::from_reqs(&[IsHungry::is(false)]);

    // Create our action from EatAction
    let eat_action = EatAction::new()
        // Mutators define what happens when this action is executed
        // In this case, we set IsHungry to false
        .add_mutator(IsHungry::set(false));

    // Create our planner + required DatumComponents
    let (planner, components) = create_planner!({
        actions: [(EatAction, eat_action)],
        // We put our starting state to be IsHungry = true
        state: [IsHungry(true)],
        goals: [goal],
    });

    // Spawn a entity with our Planner component + the DatumComponents
    commands.spawn((Name::new("Planner"), planner, components));
}

// Create our system that handles executing of the EatAction
fn handle_eat_action(
    mut commands: Commands,
    mut query: Query<(Entity, &EatAction, &mut IsHungry)>,
) {
    for (entity, _eat_action, mut need) in query.iter_mut() {
        // We set IsHungry to false
        need.0 = false;
        // And remove the action from our entity as we're done with this action
        commands.entity(entity).remove::<EatAction>();
    }
}

fn main() {
    let mut app = App::new();

    // We need to register our components as DatumComponent, otherwise planner won't be able to find them
    // as we're using bevy_trait_query to be able to query for components implementing a trait
    register_components!(app, vec![IsHungry]);

    app.add_plugins(MinimalPlugins)
        // !!! Don't forget to add the plugin ;)
       .add_plugins(DogoapPlugin)
       .add_systems(Startup, startup)
       .add_systems(FixedUpdate, handle_eat_action);

    // Run a couple of updates to run forward the world
    for _i in 0..3 {
        app.update();
    }

    // Lets inspect the final state of our (one) Planner we have in the World
    let mut query = app.world_mut().query::<&Planner>();
    let planner = query.get_single(&app.world()).unwrap();

    // This should confirm that is_hungry and is_tired have been set to `false`
    println!("Final state in our planner:");
    println!("{:#?}", planner.state);
}

With this example, it should take about 2-3 frames until IsHungry is now set to false as the planner came up with a plan, added the EatAction component, our system handled the action and changed the DatumComponent

More Examples

  • bevy_basic.rs - Quickstart - Basic setup possible for integration between dogoap and Bevy
  • miner.rs - Long plans - How to setup more complicated interactions
  • sneaky.rs - Player Interactions - How to use dogoap for NPCs in a world with a player controlled entity
  • villages.rs - Nested Planners - How you can nest planners to achieve something smarter
  • lemonade_stand.rs - Co-operating Planners - Shows how you can have two entities with two very different planners and "simulate" co-operation

Bevy Version Support

bevy bevy_dogoap
0.14 0.2.0

Dependencies

~47–84MB
~1.5M SLoC