Newer
Older
/*!
`big-brain` is a [Utility AI](https://en.wikipedia.org/wiki/Utility_system)
library for games, built for the [Bevy Game Engine](https://bevyengine.org/)
It lets you define complex, intricate AI behaviors for your entities based on
their perception of the world. Definitions are heavily data-driven, using
plain Rust, and you only need to program Scorers (entities that look at your
game world and come up with a Score), and Actions (entities that perform
actual behaviors upon the world). No other code is needed for actual AI
behavior.
See [the documentation](https://docs.rs/big-brain) for more details.
* Highly concurrent/parallelizable evaluation.
* Integrates smoothly with Bevy.
* Proven game AI model.
* Highly composable and reusable.
* State machine-style continuous actions/behaviors.
* Action cancellation.
As a developer, you write application-dependent code to define
[`Scorers`](#scorers) and [`Actions`](#actions), and then put it all together
like building blocks, using [`Thinkers`](#thinkers) that will define the
actual behavior.
`Scorer`s are entities that look at the world and evaluate into [`Score`](scorers::Score) values. You can think of them as the "eyes" of the AI system. They're a highly-parallel way of being able to look at the `World` and use it to make some decisions later.
use bevy::prelude::*;
use big_brain::prelude::*;
pub struct Thirsty;
pub fn thirsty_scorer_system(
thirsts: Query<&Thirst>,
mut query: Query<(&Actor, &mut Score), With<Thirsty>>,
) {
for (Actor(actor), mut score) in query.iter_mut() {
if let Ok(thirst) = thirsts.get(*actor) {
score.set(thirst.thirst);
}
}
}
```
`Action`s are the actual things your entities will _do_. They are connected to
[`ActionState`](actions::ActionState)s that represent the current execution
state of the state machine.
use bevy::prelude::*;
use big_brain::prelude::*;
pub struct Drink;
fn drink_action_system(
mut thirsts: Query<&mut Thirst>,
mut query: Query<(&Actor, &mut ActionState), With<Drink>>,
) {
for (Actor(actor), mut state) in query.iter_mut() {
if let Ok(mut thirst) = thirsts.get_mut(*actor) {
match *state {
ActionState::Requested => {
thirst.thirst = 10.0;
*state = ActionState::Success;
}
ActionState::Cancelled => {
*state = ActionState::Failure;
}
_ => {}
}
}
}
}
```
Finally, you can use it when define the [`Thinker`](thinker::Thinker), which you can attach as a
regular Component:
cmd.spawn().insert(Thirst::new(70.0, 2.0)).insert(
Thinker::build()
#### App
Once all that's done, we just add our systems and off we go!
```rust
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(BigBrainPlugin)
.add_startup_system(init_entities)
.add_system(thirst_system)
.add_system_to_stage(BigBrainStage::Actions, drink_action_system)
.add_system_to_stage(BigBrainStage::Scorers, thirsty_scorer_system)
.run();
```
### Contributing
1. Install the latest Rust toolchain (stable supported).
2. `cargo run --example thirst`
3. Happy hacking!
This project is licensed under [the Apache-2.0 License](LICENSE.md).
/*!
Convenience module with the core types you're most likely to use when working with Big Brain. Mean to be used like `use big_brain::prelude::*;`
*/
pub use super::BigBrainStage;
pub use actions::{ActionBuilder, ActionState, Concurrently, Steps};
pub use scorers::{
AllOrNothing, FixedScore, Score, ScorerBuilder, SumOfScorers, WinningScorer,
};
pub use thinker::{Actor, Thinker, ThinkerBuilder};
/**
Core [`Plugin`] for Big Brain behavior. Required for any of the [`Thinker`](thinker::Thinker)-related magic to work.
### Example
```no_run
use bevy::prelude::*;
use big_brain::prelude::*;
App::build()
.add_plugins(DefaultPlugins)
.add_plugin(BigBrainPlugin)
// ...insert entities and other systems.
.run();
*/
pub struct BigBrainPlugin;
impl Plugin for BigBrainPlugin {
use CoreStage::*;
app.add_stage_after(First, BigBrainStage::Scorers, SystemStage::parallel());
app.add_system_set_to_stage(
BigBrainStage::Scorers,
SystemSet::new()
.with_system(scorers::fixed_score_system)
.with_system(scorers::all_or_nothing_system)
.with_system(scorers::sum_of_scorers_system)
.with_system(scorers::winning_scorer_system)
.with_system(scorers::evaluating_scorer_system),
);
app.add_stage_after(
BigBrainStage::Scorers,
BigBrainStage::Thinkers,
SystemStage::parallel(),
);
app.add_system_to_stage(BigBrainStage::Thinkers, thinker::thinker_system);
app.add_stage_after(PreUpdate, BigBrainStage::Actions, SystemStage::parallel());
app.add_system_set_to_stage(
BigBrainStage::Actions,
SystemSet::new()
.with_system(actions::concurrent_system),
);
app.add_stage_after(Last, BigBrainStage::Cleanup, SystemStage::parallel());
app.add_system_set_to_stage(
BigBrainStage::Cleanup,
SystemSet::new()
.with_system(thinker::thinker_component_attach_system)
.with_system(thinker::thinker_component_detach_system)
.with_system(thinker::actor_gone_cleanup),
);
/**
BigBrainPlugin execution stages. Use these to schedule your own actions/scorers/etc.
*/
#[derive(Clone, Debug, Hash, Eq, PartialEq, StageLabel)]
pub enum BigBrainStage {
/// Scorers are evaluated in this stage.
Scorers,
/// Actions are executed in this stage.
Actions,
/// Thinkers run their logic in this stage.
Thinkers,
/// Various internal cleanup items run in this final stage.
Cleanup,
}