Discussion
Loading...

Post

Log in
  • About
  • Code of conduct
  • Privacy
  • Users
  • Instances
  • About Bonfire
kat
kat
@zkat@toot.cat  ·  activity timestamp 4 months ago

FYI: As part of a larger move away from GitHub, I've archived Big Brain on it and moved all future development over to Codeberg, at https://codeberg.org/zkat/big-brain

Please use that repo from now on.

Additionally: I am in the process of trying a major rewrite of the crate that I'm hoping will be much simpler, and much more efficient, thanks to features now available in recent version of Bevy! I'm very excited :)

#GiveUpGitHub #GitHub #Bevy #Rust #RustLang #Codeberg

  • Copy link
  • Flag this post
  • Block
kat
kat
@zkat@toot.cat replied  ·  activity timestamp 4 months ago

Seeking feedback from Bevy and Big Brain users: how does this new API look?

Under the hood, this would be a high-performance, low-overhead API powered by Observers, but this is all you would actually need to write yourself.

I'm pretty sure I can get this to work with Bevy's current featureset, including the Very Fun async actions situation here.

#Bevy #BigBrain #GameAI #Rust #RustLang

#[derive(Debug, Default, Clone, Copy, Component)]
pub struct Thirsty;

#[scorer_for(Thirsty)]
pub fn score_thirsty(score: Score, thirsts: Query<&Thirst>) -> Score {
    score.update(thirsts.get(score.actor()).unwrap().thirst)
}

#[derive(Debug, Clone, Copy, Component)]
pub struct Drink { rate: f32, per: Duration }
impl Default for Drink {
    fn default() -> Self {
        Self { rate: 0.5, per: Duration::from_millis(500), }
    }
}

#[action_for(Drink)]
pub async fn drink_action(
    action: Action<Drink>,
    mut thirsts: Query<&mut Thirst>
) -> Result<(), ActionFailure> {
    while let Ok(mut thirst) = thirsts.get(action.actor()) && thirst.thirst > 10.0 {
        action.check_cancelled()?;
        thirst.thirst -= action.data().rate;
        action.sleep(action.data().per).await;
    }
    Ok(())
}

fn spawn_entity(cmd: &mut Commands) {
    cmd.spawn((
        Thirst(70.0, 2.0),
        Thinker::new()
            .picker(FirstToScore { threshold: 0.8 })
            .when<Thirsty, Drink>(),
    ));
}
#[derive(Debug, Default, Clone, Copy, Component)] pub struct Thirsty; #[scorer_for(Thirsty)] pub fn score_thirsty(score: Score, thirsts: Query<&Thirst>) -> Score { score.update(thirsts.get(score.actor()).unwrap().thirst) } #[derive(Debug, Clone, Copy, Component)] pub struct Drink { rate: f32, per: Duration } impl Default for Drink { fn default() -> Self { Self { rate: 0.5, per: Duration::from_millis(500), } } } #[action_for(Drink)] pub async fn drink_action( action: Action<Drink>, mut thirsts: Query<&mut Thirst> ) -> Result<(), ActionFailure> { while let Ok(mut thirst) = thirsts.get(action.actor()) && thirst.thirst > 10.0 { action.check_cancelled()?; thirst.thirst -= action.data().rate; action.sleep(action.data().per).await; } Ok(()) } fn spawn_entity(cmd: &mut Commands) { cmd.spawn(( Thirst(70.0, 2.0), Thinker::new() .picker(FirstToScore { threshold: 0.8 }) .when<Thirsty, Drink>(), )); }
#[derive(Debug, Default, Clone, Copy, Component)] pub struct Thirsty; #[scorer_for(Thirsty)] pub fn score_thirsty(score: Score, thirsts: Query<&Thirst>) -> Score { score.update(thirsts.get(score.actor()).unwrap().thirst) } #[derive(Debug, Clone, Copy, Component)] pub struct Drink { rate: f32, per: Duration } impl Default for Drink { fn default() -> Self { Self { rate: 0.5, per: Duration::from_millis(500), } } } #[action_for(Drink)] pub async fn drink_action( action: Action<Drink>, mut thirsts: Query<&mut Thirst> ) -> Result<(), ActionFailure> { while let Ok(mut thirst) = thirsts.get(action.actor()) && thirst.thirst > 10.0 { action.check_cancelled()?; thirst.thirst -= action.data().rate; action.sleep(action.data().per).await; } Ok(()) } fn spawn_entity(cmd: &mut Commands) { cmd.spawn(( Thirst(70.0, 2.0), Thinker::new() .picker(FirstToScore { threshold: 0.8 }) .when<Thirsty, Drink>(), )); }
  • Copy link
  • Flag this comment
  • Block
Federation Bot
Federation Bot
@Federation_Bot replied  ·  activity timestamp 4 months ago

Big Brain, btw, is designed to be data-oriented, so I might provide an API that lets you define game AI in .kdl files that look like this. It could also, for example, eventually use a hypothetical node editor (thing Blender Geometry Nodes), to define the same data.

#KDL #Bevy #BigBrain #GameAI

Thinker {
    picker FirstToScore threshold=0.8
    when Thirsty {
        GoToWater
        Drink
    }
    when Hungry {
        Eat
    }
    or Bored Paranoid {
        Thinker {
            when EnemySpotted {
                ShootEnemy
            }
            when FlowersSpotted {
                WalkToFlowers
                PickAFlower
                SmellFlower
            }
            otherwise {
                Parallel {
                    Wander
                    HumATune
                }
            }
        }
    }
}
Thinker { picker FirstToScore threshold=0.8 when Thirsty { GoToWater Drink } when Hungry { Eat } or Bored Paranoid { Thinker { when EnemySpotted { ShootEnemy } when FlowersSpotted { WalkToFlowers PickAFlower SmellFlower } otherwise { Parallel { Wander HumATune } } } } }
Thinker { picker FirstToScore threshold=0.8 when Thirsty { GoToWater Drink } when Hungry { Eat } or Bored Paranoid { Thinker { when EnemySpotted { ShootEnemy } when FlowersSpotted { WalkToFlowers PickAFlower SmellFlower } otherwise { Parallel { Wander HumATune } } } } }
  • Copy link
  • Flag this comment
  • Block
Charles Eckman
Charles Eckman
@cceckman@hachyderm.io replied  ·  activity timestamp 4 months ago

@zkat I know nothing about game programming so I want to ask: does this say the actor can be shooting at an enemy, but get thirsty enough that they just stop and go drink some water?

  • Copy link
  • Flag this comment
  • Block
kat
kat
@zkat@toot.cat replied  ·  activity timestamp 4 months ago

oooh, how about this for defining Thinkers themselves?

fn spawn_entity(cmd: &mut Commands) {
    cmd.spawn((
        Thirst(70.0, 2.0),
        Thinker::build()
            .picker(FirstToScore { threshold: 0.8 })
            .when<Thirsty>
            .then<GoToWater>()
            .then<Drink>()
            .when<Hungry>()
            .then<Eat>()
            .when<Bored>().or<Paranoid>()
            .then<Wander>()
            .and_also_with(|_: actor: Entity, _w: &World| Thinker::build()
                .picker(FirstToScore { threshold: 1.0 })
                .when<EnemySpotted>()
                .then<Shoot>()
                .when<FlowersSpotted>()
                .then<WalkToFlowers>()
                .then<PickAFlower>()
                .then<SmellFlower>()
            )
    ));
}
fn spawn_entity(cmd: &mut Commands) { cmd.spawn(( Thirst(70.0, 2.0), Thinker::build() .picker(FirstToScore { threshold: 0.8 }) .when<Thirsty> .then<GoToWater>() .then<Drink>() .when<Hungry>() .then<Eat>() .when<Bored>().or<Paranoid>() .then<Wander>() .and_also_with(|_: actor: Entity, _w: &World| Thinker::build() .picker(FirstToScore { threshold: 1.0 }) .when<EnemySpotted>() .then<Shoot>() .when<FlowersSpotted>() .then<WalkToFlowers>() .then<PickAFlower>() .then<SmellFlower>() ) )); }
fn spawn_entity(cmd: &mut Commands) { cmd.spawn(( Thirst(70.0, 2.0), Thinker::build() .picker(FirstToScore { threshold: 0.8 }) .when<Thirsty> .then<GoToWater>() .then<Drink>() .when<Hungry>() .then<Eat>() .when<Bored>().or<Paranoid>() .then<Wander>() .and_also_with(|_: actor: Entity, _w: &World| Thinker::build() .picker(FirstToScore { threshold: 1.0 }) .when<EnemySpotted>() .then<Shoot>() .when<FlowersSpotted>() .then<WalkToFlowers>() .then<PickAFlower>() .then<SmellFlower>() ) )); }
  • Copy link
  • Flag this comment
  • Block

bonfire.cafe

A space for Bonfire maintainers and contributors to communicate

bonfire.cafe: About · Code of conduct · Privacy · Users · Instances
Bonfire social · 1.0.2-alpha.7 no JS en
Automatic federation enabled
Log in
  • Explore
  • About
  • Members
  • Code of Conduct