Skip to content
This repository was archived by the owner on Oct 7, 2025. It is now read-only.
This repository was archived by the owner on Oct 7, 2025. It is now read-only.

Alt design pattern with In Out Systems #90

@stargazing-dino

Description

@stargazing-dino

Hiyo ! I was playing around with other possible patterns - mostly out of curiosity and as a learning experience and was recently reading a one shot systems related PR and thought that'd be cool. Anyways, here's how I'd think it'd make sense as a utility ai architecture

pub struct BrainPlugin;

impl Plugin for BrainPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Startup, setup)
            .add_systems(Update, (decide_best_action, sleepiness_tick));
    }
}

#[derive(Component)]
pub struct Thinker;

#[derive(Component)]
pub struct Sleepiness(f32);

#[derive(Bundle)]
pub struct MyActorBundle {
    pub thinker: Thinker,
    pub considerations: Considerations,
    pub action_state: ActionState,
    pub sleepiness: Sleepiness,
}

#[derive(Component)]
pub struct Considerations {
    pub considerations: Vec<Consideration>,
}

#[derive(Component)]
pub enum ActionState {
    Idle,
    Executing,
    Done,
}

pub struct Consideration {
    pub name: String,
    pub scorer: SystemId<Entity, f32>,
    pub action: SystemId<ActionState, ActionState>,
}

fn my_sleep_scorer(In(entity): In<Entity>, sleepiness: Query<&Sleepiness>) -> f32 {
    let sleepiness = sleepiness.get(entity).unwrap();
    sleepiness.0 / 100.0
}

fn sleep(In(action_state): In<ActionState>) -> ActionState {
    todo!();
}

fn decide_best_action(world: &mut World) {
    let mut highest_consideration: Option<&Consideration> = None;
    let mut highest_score = 0.0;
    let mut query = world.query::<(Entity, &Considerations)>();

    for (actor_entity, considerations) in query.iter(world) {
        for consideration in &considerations.considerations {
            // This doesn't compile because of multiple borrows :(
            let Ok(score) = world.run_system_with_input(consideration.scorer, actor_entity) else {
                continue;
            };

            if score > highest_score {
                highest_consideration = Some(consideration);
                highest_score = score;
            }
        }
    }

    if let Some(consideration) = highest_consideration {
        let Ok(next) = world.run_system_with_input(consideration.action, ActionState::Idle) else {
            return;
        };

        todo!("set next action state");
    }
}

fn setup(world: &mut World) {
    let scorer = world.register_system(my_sleep_scorer);
    let action = world.register_system(sleep);

    world.spawn(MyActorBundle {
        thinker: Thinker,
        action_state: ActionState::Idle,
        considerations: Considerations {
            considerations: vec![Consideration {
                name: "Sleep".into(),
                scorer: scorer,
                action: action,
            }],
        },
        sleepiness: Sleepiness(0.0),
    });
}

fn sleepiness_tick(mut sleepiness: Query<&mut Sleepiness>) {
    for mut sleepiness in sleepiness.iter_mut() {
        sleepiness.0 += 1.0;
    }
}

I hope you find this interesting ! I thought so and just wanted to share so I could hear your opinion on it :D

I'm a big rust noob so this might be totally wrong/absurd

(also sorry if this was better suited as a discussion ! lol)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions