// In this game, the player moves around in 2D with the arrow keys, but if they get too close to the // enemy, the enemy moves towards them, until the player moves back out of range use bevy::prelude::*; use seldom_state::prelude::*; fn main() { App::new() .add_plugins((DefaultPlugins, StateMachinePlugin)) // This plugin is required for `seldom_state` .add_systems(Startup, init) .add_systems(Update, (follow, move_player)) .run(); } // Setup the game fn init(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); // Simple player entity let player = commands .spawn(( SpriteBundle { texture: asset_server.load("player.png"), ..default() }, Player, )) .id(); // This is our trigger, which is a Bevy system that returns a `bool`, `Option`, or `Result`. We // define the trigger as a closure within this function so it can use variables in the scope // (namely, `player`). For the sake of example, we also define this trigger as an external // function later. // // Triggers are reinitialized after each transition, so they won't read events that occurred in a // previous state, `Local`s are reset between transitions, etc. let near_player = move |In(entity): In, transforms: Query<&Transform>| { let distance = transforms .get(player) .unwrap() .translation .truncate() .distance(transforms.get(entity).unwrap().translation.truncate()); // Check whether the target is within range. If it is, return `Ok` to trigger! match distance <= 300. { true => Ok(distance), false => Err(distance), } }; // The enemy commands.spawn(( SpriteBundle { transform: Transform::from_xyz(500., 0., 0.), texture: asset_server.load("enemy.png"), ..default() }, // This state machine handles the enemy's transitions. Transitions defined earlier have // priority, but triggers after the first accepted one may still be checked. StateMachine::default() // Add a transition. When they're in `Idle` state, and the `near_player` trigger occurs, // switch to this instance of the `Follow` state .trans::( near_player, // Transitions accept specific instances of states Follow { target: player, speed: 100., }, ) // Add a second transition. When they're in the `Follow` state, and the `near_player` // trigger does not occur, switch to the `Idle` state. `.not()` is a combinator that // negates the trigger. `.and(other)` and `.or(other)` also exist. .trans::(near_player.not(), Idle) // Enable transition logging .set_trans_logging(true), // The initial state is `Idle` Idle, )); } // Now let's define our states! States must implement `Component` and `Clone`. `EntityState` is // implemented automatically for valid states. Feel free to mutate/insert/remove states manually, // but don't put your entity in multiple or zero states, else it will panic. Manually inserted/ // removed states will not trigger `on_enter`/`on_exit` events registered to the `StateMachine`. // Entities in the `Idle` state do nothing #[derive(Clone, Component)] #[component(storage = "SparseSet")] struct Idle; // Entities in the `Follow` state move toward the given entity at the given speed #[derive(Clone, Component)] #[component(storage = "SparseSet")] struct Follow { target: Entity, speed: f32, } // Let's define some behavior for entities in the follow state fn follow( mut transforms: Query<&mut Transform>, follows: Query<(Entity, &Follow)>, time: Res