|
| 1 | +//! # Chapter 0 - Overview |
| 2 | +//! |
| 3 | +//! Big Brain is a highliy-concurrent [Utility |
| 4 | +//! AI](https://en.wikipedia.org/wiki/Utility_system) library for games, built |
| 5 | +//! for the [Bevy Game Engine](https://bevyengine.org/). The building blocks |
| 6 | +//! for Big Brain can be highly-generic and highly-composable, letting you |
| 7 | +//! reuse logic across your game by defining them in small pieces and then |
| 8 | +//! just putting them all together in a declarative way. |
| 9 | +//! |
| 10 | +//! ### High-level Overview |
| 11 | +//! |
| 12 | +//! As a developer, you write application-dependent code to define [`Scorer`]s |
| 13 | +//! and [`Action`]s, and then put it all together like building blocks, using |
| 14 | +//! [`Thinker`]s that will define the actual behavior. |
| 15 | +//! |
| 16 | +//! We'll go over each of these at a high-level, and cover them in more |
| 17 | +//! details later. |
| 18 | +//! |
| 19 | +//! #### Scorers |
| 20 | +//! |
| 21 | +//! [`Scorer`]s are entities that look at the world and evaluate into |
| 22 | +//! [`Score`] values. You can think of them as the "eyes" of the AI system. |
| 23 | +//! They're a highly-parallel way of being able to look at the `World` and use |
| 24 | +//! it to make some decisions later. |
| 25 | +//! |
| 26 | +//! They are constructed using [`ScorerBuilder`]s, for which there's a handy |
| 27 | +//! shortcut `derive` macro. |
| 28 | +//! |
| 29 | +//! ```rust |
| 30 | +//! use bevy::prelude::*; |
| 31 | +//! use big_brain::prelude::*; |
| 32 | +//! # #[derive(Component, Debug)] |
| 33 | +//! # struct Thirst { thirst: f32 } |
| 34 | +//! |
| 35 | +//! #[derive(Debug, Clone, Component, ScorerBuilder)] |
| 36 | +//! pub struct Thirsty; |
| 37 | +//! |
| 38 | +//! pub fn thirsty_scorer_system( |
| 39 | +//! thirsts: Query<&Thirst>, |
| 40 | +//! mut query: Query<(&Actor, &mut Score), With<Thirsty>>, |
| 41 | +//! ) { |
| 42 | +//! for (Actor(actor), mut score) in query.iter_mut() { |
| 43 | +//! if let Ok(thirst) = thirsts.get(*actor) { |
| 44 | +//! score.set(thirst.thirst); |
| 45 | +//! } |
| 46 | +//! } |
| 47 | +//! } |
| 48 | +//! ``` |
| 49 | +//! |
| 50 | +//! #### Actions |
| 51 | +//! |
| 52 | +//! [`Action`]s are the actual things your entities will _do_. They are |
| 53 | +//! connected to [`ActionState`]s that represent the current execution state |
| 54 | +//! of the state machine. You can think of them as deconstructed async |
| 55 | +//! functions. |
| 56 | +//! |
| 57 | +//! They are constructed using [`ActionBuilder`]s, for which there's a handy |
| 58 | +//! shortcut `derive` macro. |
| 59 | +//! |
| 60 | +//! ```rust |
| 61 | +//! use bevy::prelude::*; |
| 62 | +//! use big_brain::prelude::*; |
| 63 | +//! # #[derive(Component, Debug)] |
| 64 | +//! # struct Thirst { thirst: f32 } |
| 65 | +//! |
| 66 | +//! #[derive(Debug, Clone, Component, ActionBuilder)] |
| 67 | +//! pub struct Drink; |
| 68 | +//! |
| 69 | +//! fn drink_action_system( |
| 70 | +//! mut thirsts: Query<&mut Thirst>, |
| 71 | +//! mut query: Query<(&Actor, &mut ActionState), With<Drink>>, |
| 72 | +//! ) { |
| 73 | +//! for (Actor(actor), mut state) in query.iter_mut() { |
| 74 | +//! if let Ok(mut thirst) = thirsts.get_mut(*actor) { |
| 75 | +//! match *state { |
| 76 | +//! ActionState::Requested => { |
| 77 | +//! thirst.thirst = 10.0; |
| 78 | +//! *state = ActionState::Success; |
| 79 | +//! } |
| 80 | +//! ActionState::Cancelled => { |
| 81 | +//! *state = ActionState::Failure; |
| 82 | +//! } |
| 83 | +//! _ => {} |
| 84 | +//! } |
| 85 | +//! } |
| 86 | +//! } |
| 87 | +//! } |
| 88 | +//! ``` |
| 89 | +//! |
| 90 | +//! #### Thinkers |
| 91 | +//! |
| 92 | +//! Finally, you can use it when define the [`Thinker`], which you can attach |
| 93 | +//! as a regular [`Component`]: |
| 94 | +//! |
| 95 | +//! ```rust |
| 96 | +//! # use bevy::prelude::*; |
| 97 | +//! # use big_brain::prelude::*; |
| 98 | +//! # #[derive(Debug, Component)] |
| 99 | +//! # struct Thirst(f32, f32); |
| 100 | +//! # #[derive(Debug, Clone, Component, ScorerBuilder)] |
| 101 | +//! # struct Thirsty; |
| 102 | +//! # #[derive(Debug, Clone, Component, ActionBuilder)] |
| 103 | +//! # struct Drink; |
| 104 | +//! fn spawn_entity(cmd: &mut Commands) { |
| 105 | +//! cmd.spawn(( |
| 106 | +//! Thirst(70.0, 2.0), |
| 107 | +//! Thinker::build() |
| 108 | +//! .picker(FirstToScore { threshold: 0.8 }) |
| 109 | +//! .when(Thirsty, Drink), |
| 110 | +//! )); |
| 111 | +//! } |
| 112 | +//! ``` |
| 113 | +//! |
| 114 | +//! #### App |
| 115 | +//! |
| 116 | +//! Once all that's done, we just add our systems and off we go! |
| 117 | +//! |
| 118 | +//! ```no_run |
| 119 | +//! # use bevy::prelude::*; |
| 120 | +//! # use big_brain::prelude::*; |
| 121 | +//! # fn init_entities() {} |
| 122 | +//! # fn thirst_system() {} |
| 123 | +//! # fn drink_action_system() {} |
| 124 | +//! # fn thirsty_scorer_system() {} |
| 125 | +//! fn main() { |
| 126 | +//! App::new() |
| 127 | +//! .add_plugins(DefaultPlugins) |
| 128 | +//! .add_plugins(BigBrainPlugin::new(PreUpdate)) |
| 129 | +//! .add_systems(Startup, init_entities) |
| 130 | +//! .add_systems(Update, thirst_system) |
| 131 | +//! .add_systems(PreUpdate, drink_action_system.in_set(BigBrainSet::Actions)) |
| 132 | +//! .add_systems(PreUpdate, thirsty_scorer_system.in_set(BigBrainSet::Scorers)) |
| 133 | +//! .run(); |
| 134 | +//! } |
| 135 | +//! ``` |
| 136 | +//! |
| 137 | +//! #### What's next? |
| 138 | +//! |
| 139 | +//! You can read the general [crate docs][crate], or continue with the [next |
| 140 | +//! chapter of the tutorial][_tutorial::chapter_1] for an expanded guide on |
| 141 | +//! how to build out and organize the AI for your game using Big Brain!. |
| 142 | +#[allow(unused_imports)] |
| 143 | +use bevy::prelude::*; |
| 144 | + |
| 145 | +#[allow(unused_imports)] |
| 146 | +use crate::prelude::{Action, ActionBuilder, ActionState, Score, Scorer, ScorerBuilder, Thinker}; |
| 147 | + |
| 148 | +pub use super::chapter_1 as next; |
| 149 | +pub use crate::_tutorial as table_of_contents; |
0 commit comments