← Back to list

Bevy: A Rust Game Engine. Basic Concepts in SUPER DETAIL.

Step 0 to Create Our First Game In Rust!

Itsuki in Stackademic · 2025-09-12 23:09 · 25 claps · 13.0 min read
#rust #rust-programming-language #bevy #rust-bevy #game-development
Open on Medium ↗
Wiki topics: 💻 · Programming

Bevy: A Rust Game Engine. Basic Concepts in SUPER DETAIL.

Step 0 to Create Our First Game In Rust!

Why Game in Rust?

  • Emphasis on low-level memory safe programming
  • Less debugging time
  • Better end results

What is Bevy Then?

It is an open-source, simple, modular, data-driven game engine built in Rust.

HOWEVER!

Do note that Bevy is still in the early stages of development with breaking changes to the API approximately once every 3 months!

This is my favorite part though! I love breaking changes! I personally think that if we don’t dump old ones into the trash, we cannot actually move forward!

But!

If the migration is not something you want to waste your time on, you can also check out *Godot Engine*, a much more feature-complete and stable option, that is also free, open-source and scriptable with Rust.

Sounds good? Let’s start!

We will begin with an high level overview of the basic concepts (ECS, Plugins, Resources, and etc), and then dive into each one of them with actual code in super detail!

Set Up

Let’s start with adding ***Bevy*** to our project!

It is available on crates.io, so we could just simply run cargo add bevy but please let me point this out up at front!

The build can be really (like REALLY) slow and CPU-heavy! Even with just a hello world, I can hear the fan on my computer going crazy!

So!

There are couple things we can do here!

  1. Install it with the dynamic_linking feature by running cargo add bevy -F dynamic_linking
  2. Add development profile to Cargo.toml
# Enable a small amount of optimization in the dev profile.
[profile.dev]
opt-level = 1

# Enable a large amount of optimization in the dev profile for dependencies.
[profile.dev.package."*"]
opt-level = 3

Above make enough improvements for me, but if you are looking for further optimizations, please check out the ***Enable Fast Compiles*** section on the official documentation!

⭐⭐⭐⭐ Basic Concepts ⭐⭐⭐⭐

A high level overview of some of the key concepts first!

We will then put those together and dive into each one of them more in the next section!

App

[App](https://docs.rs/bevy/latest/bevy/app/struct.App.html) is the main structure / entry point of a Bevy program that automates the setup of a *standard lifecycle and provides interface glue for [plugins](https://docs.rs/bevy/latest/bevy/prelude/trait.Plugin.html)*.

use bevy::prelude::*;

fn main() {
    App::new().add_systems(Update, hello_world_system).run();
}

fn hello_world_system() {
    println!("hello world");
}

(This syntax makes me think of a *axum* app (Router) where we add routes, handlers, and middlewares! And there are actually more similarities as we will see!)

Give it a run and you should see hello world print out to the terminal!

Let’s ignore that system for now and take a little look at what does this [App](https://docs.rs/bevy/latest/bevy/app/struct.App.html) structure actually contains?

A single [App](https://docs.rs/bevy/latest/bevy/prelude/struct.App.html) can contain multiple [SubApp](https://docs.rs/bevy/latest/bevy/prelude/struct.SubApp.html) instances with a RunnerFn, just a function that will manage the app’s lifecycle.

Within the [SubApp](https://docs.rs/bevy/latest/bevy/prelude/struct.SubApp.html), there is the world field stores all of our game's data, the update_schedule holds the systems that operate on this data (and the order in which they do so), along with some other ones!

pub struct App {
    pub(crate) sub_apps: SubApps,
    /// The function that will manage the app's lifecycle.
    ///
    /// Bevy provides the [`WinitPlugin`] and [`ScheduleRunnerPlugin`] for windowed and headless
    /// applications, respectively.
    ///
    /// [`WinitPlugin`]: https://docs.rs/bevy/latest/bevy/winit/struct.WinitPlugin.html
    /// [`ScheduleRunnerPlugin`]: https://docs.rs/bevy/latest/bevy/app/struct.ScheduleRunnerPlugin.html
    pub(crate) runner: RunnerFn,
}

type RunnerFn = Box<dyn FnOnce(App) -> AppExit>;

pub struct SubApps {
    /// The primary sub-app that contains the "main" world.
    pub main: SubApp,
    /// Other, labeled sub-apps.
    pub sub_apps: HashMap<InternedAppLabel, SubApp>,
}

pub struct SubApp {
    /// The data of this application.
    world: World,
    /// List of plugins that have been added.
    pub(crate) plugin_registry: Vec<Box<dyn Plugin>>,
    /// The names of plugins that have been added to this app. (used to track duplicates and
    /// already-registered plugins)
    pub(crate) plugin_names: HashSet<String>,
    /// Panics if an update is attempted while plugins are building.
    pub(crate) plugin_build_depth: usize,
    pub(crate) plugins_state: PluginsState,
    /// The schedule that will be run by [`update`](Self::update).
    pub update_schedule: Option<InternedScheduleLabel>,
    /// A function that gives mutable access to two app worlds. This is primarily
    /// intended for copying data from the main world to secondary worlds.
    extract: Option<ExtractFn>,
}

Entity-Component-System (ECS)

I hope you are familiar with this term, but ECS is basically just a software architectural pattern that separates data (components) from logic (systems) and utilizes composition over inheritance to build complex entities from simpler components.

And!

All app logic in Bevy uses this Entity-Component-System!

Here is a quick overview on the concept along with how this pattern is implemented in Bevy.

Entity

A unique identifier, a container for an object (a group of components) within the game world that doesn’t hold data or behavior.

In Bevy, [Entity](https://docs.rs/bevy/latest/bevy/ecs/entity/struct.Entity.html) is a simple struct with the identifier implemented using a ***generational index***: a combination of an index and a generation.

// Alignment repr necessary to allow LLVM to better output
// optimized codegen for `to_bits`, `PartialEq` and `Ord`.
#[repr(C, align(8))]
pub struct Entity {
    // Do not reorder the fields here. The ordering is explicitly used by repr(C)
    // to make this struct equivalent to a u64.
    #[cfg(target_endian = "little")]
    index: u32,
    generation: NonZero<u32>,
    #[cfg(target_endian = "big")]
    index: u32,
}

Instead of creating an instance of this structure directly, we will be using the [spawn](https://docs.rs/bevy/latest/bevy/prelude/struct.Commands.html#method.spawn) command to add entities to our app as we will see shortly!

Components

A plain data structure (no inherent functionality, no specific behavior) that represents a specific attribute or feature of an entity, such as position, health, or velocity.

To create a component in Bevy, all we have to do is to derive the [Component](https://docs.rs/bevy/latest/bevy/ecs/component/trait.Component.html) trait on a custom struct.

#[derive(Component)]
struct Position {
    x: f32,
    y: f32,
}

System

A function or set of functions that processes entities with a specific combination of components.

We have already saw it above, but systems in Bevy ECS are just normal Rust functions!

fn hello_world_system() {
    println!("hello world");
}

Plugin

Plugin is just a collection (a bundle) of code that modify an [App](https://docs.rs/bevy/latest/bevy/app/struct.App.html).

There are ones provided by Bevy officially such as the [UiPlugin](https://docs.rs/bevy/latest/bevy/ui/struct.UiPlugin.html) for rending UI, or the [WindowPlugin](https://docs.rs/bevy/latest/bevy/prelude/struct.WindowPlugin.html) that defines an interface for windowing support, there are third party ones like those at the Assets page, and of course, we can also create our own by simply implementing the [Plugin](https://docs.rs/bevy/latest/bevy/prelude/trait.Plugin.html) trait!

Resources

The [Resource](https://docs.rs/bevy/latest/bevy/ecs/resource/trait.Resource.html) trait is Bevy’s way of implementing globally unique data. That can be the Elapsed Time, a collection of assets, those renderers!

A lot of the plugins will have specific resources added for us to use. For example, the [TimePlugin](https://docs.rs/bevy/latest/bevy/time/struct.TimePlugin.html) will add the [Time](https://docs.rs/bevy_time/latest/bevy_time/struct.Time.html) resource, [InputPlugin](https://docs.rs/bevy/latest/bevy/input/struct.InputPlugin.html) will have [ButtonInput](https://docs.rs/bevy/latest/bevy/prelude/struct.ButtonInput.html) included.

We can also create our own resources by derive the [Resource](https://docs.rs/bevy/latest/bevy/ecs/resource/trait.Resource.html) trait.

#[derive(Resource)]
struct NotificationTimer(Timer);

⭐⭐⭐⭐ Putting Together ⭐⭐⭐⭐

That’s it for the high level overview!

Let’s get our hand on some code to get a little better understanding of what each bit actually does!

Basic Components

Start with couple simple components.

#[derive(Component)]
struct User {
    pub name: Name,
}

#[derive(Component)]
struct Name;

We might want to just set the name field to String here, but other entities might have names as well so we have separated it out into its own components here.

Create Entity

As I have mentioned above, instead of directly initializing the [Entity](https://docs.rs/bevy/latest/bevy/ecs/entity/struct.Entity.html) structure, we will be using the [spawn](https://docs.rs/bevy/latest/bevy/prelude/struct.Commands.html#method.spawn) command to add the components to the [World](https://docs.rs/bevy/latest/bevy/ecs/world/struct.World.html).

fn create_user_system(mut commands: Commands) {
    commands.spawn(User {
        name: Name("Itsuki".to_string()),
    });
}

What is this [Commands](https://docs.rs/bevy/latest/bevy/ecs/system/struct.Commands.html) here?

It is a queue to modify the [World](https://docs.rs/bevy/latest/bevy/prelude/struct.World.html). For example, we can use it to

  • spawn or de-spawn entities
  • insert components on new or existing entities
  • insert resources

Above, we have create a new entity with a single component, our User. We can also create one with two components using a tuple bundle.

commands.spawn((ComponentA(2), ComponentB(1)));

or with a component bundle.

#[derive(Bundle)]
struct ExampleBundle {
    a: ComponentA,
    b: ComponentB,
}

//...
commands.spawn(ExampleBundle {
    a: ComponentA(3),
    b: ComponentB(2),
});

Register System

As you might already be able to tell from my function name above, it is a system, which means we will be registering it to our app with the [add_systems](https://docs.rs/bevy/latest/bevy/prelude/struct.App.html#method.add_systems) function!

fn main() {
    App::new()
        .add_systems(Startup, create_user_system)
        .run();
}

If we run our little main above, we will still only get that hello world print out because we are not doing anything to the User we have created yet! We will make it a little more interesting in couple seconds, but before that, there are couple points I would like to point out here!

First of all, which [World](https://docs.rs/bevy/latest/bevy/prelude/struct.World.html) does the [Command](https://docs.rs/bevy/latest/bevy/ecs/system/struct.Commands.html) modify, ie: which [World](https://docs.rs/bevy/latest/bevy/prelude/struct.World.html) are we creating our User in?

I meant, a single [App](https://docs.rs/bevy/latest/bevy/prelude/struct.App.html) can contain multiple [SubApp](https://docs.rs/bevy/latest/bevy/prelude/struct.SubApp.html) instances!

When we use those methods on [App](https://docs.rs/bevy/latest/bevy/prelude/struct.App.html) method, it will only affect the main one.

The next thing you might be wondering here is what is that Startup or Update we have when calling the [add_systems](https://docs.rs/bevy/latest/bevy/prelude/struct.App.html#method.add_systems)?

It is the ***schedule*** to add the system to! [Startup](https://docs.rs/bevy/latest/bevy/prelude/struct.Startup.html) runs once when the app starts and [Update](https://docs.rs/bevy/latest/bevy/prelude/struct.Update.html) runs once per render frame.

Here is a ***full list of possible schedules*** we can specify!

Basic Query

We have registered our user so let’s say hello to them really quick!

To do so, we will be using [Query](https://docs.rs/bevy/latest/bevy/ecs/system/struct.Query.html), a *system parameter that provides selective access to the [Component](https://docs.rs/bevy/latest/bevy/prelude/trait.Component.html) data as well as [entity identifiers](https://docs.rs/bevy/latest/bevy/prelude/struct.Entity.html)* stored in a [World](https://docs.rs/bevy/latest/bevy/prelude/struct.World.html) without requiring direct access.

It is a generic data structure that accepts two type parameters:

Let’s start simple here by only specifying the data type we want, our User!

fn greeting_system(query: Query<&User>) {
    for user in &query {
        println!("hello {}!", user.name.0);
    }
}

This is basically just telling the system to iterate over every User component for entities.

Let’s add it to our app and run it again!

fn main() {
    App::new()
        .add_systems(Startup, (create_user_system, greeting_system).chain())
        .add_systems(Update, hello_world_system)
        .run();
}

(I am adding the greeting_system on Update here on purpose, it should be at Startup, we all know that!)

And we should see the following output to the terminal!

hello Itsuki!
hello world

You might find that hello world showing up in different order than what we had above. This is because systems run in parallel by default whenever possible.

And that’s why I have added to Update instead of Startup above, because we can only greet user after they are created!

Let’s solve this little problem and move the greeting_system to where it should be! Back to Startup!

Configure System Schedule

To have multiple system to run in the exact same order as we specify, we can use the [chain](https://docs.rs/bevy/latest/bevy/ecs/prelude/trait.IntoScheduleConfigs.html#method.chain) function.

fn main() {
    App::new()
        .add_systems(Startup, (create_user_system, greeting_system).chain())
        .add_systems(Update, hello_world_system)
        .run();
}

There are also other functions provided by the [IntoScheduleConfigs](https://docs.rs/bevy/latest/bevy/ecs/prelude/trait.IntoScheduleConfigs.html#provided-methods) trait that we can use to configure the schedule of the system to run on. For example, we can use [after](https://docs.rs/bevy/latest/bevy/ecs/prelude/trait.IntoScheduleConfigs.html#method.after) to run a specific system after all systems in the set.

Here is a ***full list for the ones provided***!

We will be checking out on how we can use [run_if](https://docs.rs/bevy/latest/bevy/ecs/prelude/trait.IntoScheduleConfigs.html#method.run_if) to run (a) system(s) only if a [Condition](https://docs.rs/bevy/latest/bevy/prelude/trait.Condition.html) is true in couple seconds!

Query With Filter

Now, let’s get back to our [Query](https://docs.rs/bevy/latest/bevy/ecs/system/struct.Query.html) for something more interesting!

First of all, let’s add another component.

#[derive(Component)]
struct Pokemon {
    pub name: Name,
}

And spawn some Users either with or without Pokemon.

 fn create_user_system(mut commands: Commands) {
    commands.spawn(User {
        name: Name("Itsuki Only".to_string()),
    });

    commands.spawn((
        User {
            name: Name("Itsuki + pikachu".to_string()),
        },
        Pokemon {
            name: Name("Pikachu".to_string()),
        },
    ));
}

Before we do anything to the query, let’s run the same App as above again just to see what we are getting from our greeting_system above!

hello Itsuki Only!
hello Itsuki + pikachu!
hello world

Now, let’s create another system that only greet User that has Pokemon associated with them!

fn greeting_user_with_pokemon_system(query: Query<&User, With<Pokemon>>) {
    for user in &query {
        println!("hello {}", user.name.0);
    }
}

This [Query](https://docs.rs/bevy/latest/bevy/ecs/system/struct.Query.html) basically tells the system to iterate over every User component for entities that also have a Pokemon component.

Let’s replace the greeting_system with it and here is what we get this time!

hello Itsuki + pikachu
hello world

Here are some of the possible filters we can use when constructing the query.

Mutable Query

Mutable query, constructed by adding a mut before Query and a mut before the component, allows us to modify the component.

fn update_user_name_system(mut query: Query<&mut User>) {
    for mut user in &mut query {
        user.name = Name("itsuki updated".to_string())
    }
}

A Little Additional On Query

Query<&User, With<Pokemon>> will only give us access to the User component. What if we want all User component for entities that also have a Pokemon component, and access to the Pokemon as well?

We don’t even have to use a Query filter in this case.

fn greeting_user_with_pokemon_system(query: Query<(&User, &Pokemon)>) {
    for (user, pokemon) in &query {
        println!("Hello {}! Hello {}!", user.name.0, pokemon.name.0);
    }
}

As I have mentioned, we can access *entity identifiers* with [Query](https://docs.rs/bevy/latest/bevy/ecs/system/struct.Query.html) as well! By simply querying the [Entity](https://docs.rs/bevy/latest/bevy/ecs/entity/struct.Entity.html) together with the component we want.

fn greeting_system(
    mut query: Query<(Entity, &mut User)>,
)

Add Provided Plugins

Now, our app above run and ends, which is probably not the what we want for most of the games!

We could implement a custom app runner, but for our purpose here, let’s check out some of the plugins provided by Bevy!

There are couple [PluginGroup](https://docs.rs/bevy/latest/bevy/app/trait.PluginGroup.html) (just a group of [Plugin](https://docs.rs/bevy/latest/bevy/prelude/trait.Plugin.html)s) provided,

  1. [DefaultPlugins](https://docs.rs/bevy/latest/bevy/struct.DefaultPlugins.html)
  2. [MinimalPlugins](https://docs.rs/bevy/latest/bevy/struct.MinimalPlugins.html)
  3. [DefaultPickingPlugins](https://docs.rs/bevy/latest/bevy/prelude/struct.DefaultPickingPlugins.html)

The [DefaultPlugins](https://docs.rs/bevy/latest/bevy/struct.DefaultPlugins.html) includes most of the common features we would want for a game engine, such as a 2D / 3D renderer, asset loading, a UI system, windows, and input.

In we want to have absolute control over the plugins used, we can also use the [MinimalPlugins](https://docs.rs/bevy/latest/bevy/struct.MinimalPlugins.html) instead which only includes only the absolute minimum plugins for a bevy App. We can then further add additional plugins that we actually need.

***Here is a list of plugins available***.

To make our [App](https://docs.rs/bevy/latest/bevy/prelude/struct.App.html) (or [SubApp](https://docs.rs/bevy/latest/bevy/prelude/struct.SubApp.html)) use a plugin (or plugins), all we have to do is to add it with the [add_plugins](https://docs.rs/bevy/latest/bevy/prelude/struct.App.html#method.add_plugins) method.

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        // ...
        .run();
}

We can also further configure a specific [Plugin](https://docs.rs/bevy/latest/bevy/prelude/trait.Plugin.html) within the [PluginGroup](https://docs.rs/bevy/latest/bevy/app/trait.PluginGroup.html) using the [set](https://docs.rs/bevy/latest/bevy/app/trait.PluginGroup.html#method.set) method, if the [Plugin](https://docs.rs/bevy/latest/bevy/prelude/trait.Plugin.html) exists.

.add_plugins(MinimalPlugins.set(ScheduleRunnerPlugin::run_loop(
    // each loop is 1 sec, ie: Run 1 times per second.
    Duration::from_secs_f64(1.0),
)))

We can also combine the [PluginGroup](https://docs.rs/bevy/latest/bevy/app/trait.PluginGroup.html) with other [Plugin](https://docs.rs/bevy/latest/bevy/prelude/trait.Plugin.html) we might want to add.

App::new()
    .add_plugins((
        ScheduleRunnerPlugin {
            run_mode: RunMode::Once,
        },
        TimePlugin,
    ))

This is basically identical to call add_plugins twice.

App::new()
    .add_plugins(
        ScheduleRunnerPlugin {
            run_mode: RunMode::Once,
        })
.add_plugins(TimePlugin)

In this article, let’s use the [DefaultPlugins](https://docs.rs/bevy/latest/bevy/struct.DefaultPlugins.html)!

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, (create_user_system, greeting_system).chain())
        .add_systems(Update, hello_world_system)
        .run();
}

Let’s give it a run and this time, we should see

Create Custom Plugin

Now that we get a general idea of what those plugins do, let’ create one ourselves by implementing the [Plugin](https://docs.rs/bevy/latest/bevy/prelude/trait.Plugin.html) trait! It is really useful for better organization!

For example, let’s create a [Plugin](https://docs.rs/bevy/latest/bevy/prelude/trait.Plugin.html) that does all the initializations for our App World!

pub struct InitPlugin;

impl Plugin for InitPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(
            Startup,
            (
                hello_world_system,
                (create_user_system, greeting_system).chain(),
            ),
        );
    }
}

We can then add it to our App in the exactly same way as what we did with the provided ones!

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(InitPlugin)
        .run();
}

Use Resources

[Resource](https://docs.rs/bevy/latest/bevy/ecs/resource/trait.Resource.html)s , in my opinion, is what make our [World](https://docs.rs/bevy/latest/bevy/prelude/struct.World.html) (systems) interesting!

We access resource data in systems using the [Res](https://docs.rs/bevy/latest/bevy/prelude/struct.Res.html) and [ResMut](https://docs.rs/bevy/latest/bevy/prelude/struct.ResMut.html) system parameters which provide read and write access (respectively) to resources.

And again, we can either use ones pre-defined by Bevy, or we could create one ourselves.

Here is a ***full list of structs that implement the resources***!

Let’s see check it out with couple examples here!

Notification Timer

Let’s say we want to send some notifications to our user at a specific time interval.

We can achieve this by using a combination of

#[derive(Resource)]
struct NotificationTimer(Timer);

To access the resources in a system, as I have mentioned above, we will be using [Res](https://docs.rs/bevy/latest/bevy/prelude/struct.Res.html) and [ResMut](https://docs.rs/bevy/latest/bevy/prelude/struct.ResMut.html).


fn notification_system(time: Res<Time>, mut timer: ResMut<NotificationTimer>, query: Query<&User>) {
    if timer.0.tick(time.delta()).just_finished() {
        for user in &query {
            println!("notification to {}!", user.name.0);
        }
    }
}

There are two things we are doing here.

  1. Call [tick](https://docs.rs/bevy/latest/bevy/prelude/struct.Timer.html#method.tick) to advance the [Timer](https://docs.rs/bevy/latest/bevy/time/struct.Timer.html) with the time elapsed since the last update obtained with [Time::delta](https://docs.rs/bevy_time/latest/bevy_time/struct.Time.html#method.delta)
  2. Check if the timer reached its duration with [just_finished](https://docs.rs/bevy/latest/bevy/time/struct.Timer.html#method.just_finished). The duration will be defined when we add the resource to our App , let’s say 2.0 seconds. And in the case of a repeating timer, ie: TimerMode::Repeating, [finished](https://docs.rs/bevy/latest/bevy/prelude/struct.Timer.html#method.finished) will behave identically as this [just_finished](https://docs.rs/bevy/latest/bevy/time/struct.Timer.html#method.just_finished) method.

Let’s add the resource and the system to give it a try!

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(InitPlugin)
        .insert_resource(NotificationTimer(Timer::from_seconds(
            2.0,
            TimerMode::Repeating,
        )))
        .add_systems(Update, notification_system)
        .run();
}

This time we should see the following print out to our terminal every 2 seconds!

notification to Itsuki Only!
notification to Itsuki + pikachu!

Update On User Input

Remember our update_user_name_system above?

Instead of running it every frame, let’s say we only want to run it when we capture some kind of user input, and we will only update the name if the input is the key U.

Let’s first modify our system to use the [ButtonInput](https://docs.rs/bevy/latest/bevy/input/struct.ButtonInput.html) resource which is added with the InputPlugin included in the DefaultPlugins.

fn update_user_name_system(mut query: Query<&mut User>, input: Res<ButtonInput<KeyCode>>) {
    if input.just_pressed(KeyCode::KeyU) {
        println!("update");
        for mut user in &mut query {
            user.name = Name("itsuki updated".to_string())
        }
    } else {
        println!("not update")
    }
}

Now, let’s add it to our App configured with [run_if](https://docs.rs/bevy/latest/bevy/ecs/prelude/trait.IntoScheduleConfigs.html#method.run_if) to run the system only if a key is pressed and is not triggered by adding the resource.

App::new()
    .add_systems(
        Update,
        (update_user_name_system, greeting_system).chain().run_if(
            resource_changed::<ButtonInput<KeyCode>>
                // By default detecting changes will also trigger if the resource was
                // just added, this won't work with my example so I will add a second
                // condition to make sure the resource wasn't just added
                .and(not(resource_added::<ButtonInput<KeyCode>>)),
        ),
    )

Here we have use the [resource_changed](https://docs.rs/bevy/latest/bevy/prelude/fn.resource_changed.html), a [Condition](https://docs.rs/bevy/latest/bevy/prelude/trait.Condition.html)-satisfying system that returns true if the resource of the given type has had its value changed since the condition was last checked, in combination ([and](https://docs.rs/bevy/latest/bevy/prelude/trait.Condition.html#method.and)) with the [not](https://docs.rs/bevy/latest/bevy/prelude/fn.not.html) [resource_added](https://docs.rs/bevy/latest/bevy/prelude/fn.resource_added.html) to achieve the condition we want.

In addition to the [and](https://docs.rs/bevy/latest/bevy/prelude/trait.Condition.html#method.and) , we also have [nand](https://docs.rs/bevy/latest/bevy/prelude/trait.Condition.html#method.nand), [nor](https://docs.rs/bevy/latest/bevy/prelude/trait.Condition.html#method.nor), [or](https://docs.rs/bevy/latest/bevy/prelude/trait.Condition.html#method.or), [xnor](https://docs.rs/bevy/latest/bevy/prelude/trait.Condition.html#method.xnor) and [xor](https://docs.rs/bevy/latest/bevy/prelude/trait.Condition.html#method.xor) to create a combination of [Condition](https://docs.rs/bevy/latest/bevy/prelude/trait.Condition.html)s.

And of course, there are other conditions available! [any_component_removed](https://docs.rs/bevy/latest/bevy/prelude/fn.any_component_removed.html), [condition_changed](https://docs.rs/bevy/latest/bevy/prelude/fn.condition_changed.html), [resource_removed](https://docs.rs/bevy/latest/bevy/prelude/fn.resource_removed.html), [in_state](https://docs.rs/bevy/latest/bevy/prelude/fn.in_state.html), and more!

And of course, we can create custom ones by simply having a function returning an impl Condition<()>!

// A condition that returns true every other time it's called.
fn every_other_time() -> impl Condition<()> {
    IntoSystem::into_system(|mut flag: Local<bool>| {
        *flag = !*flag;
        *flag
    })
}

schedule.add_systems(my_system.run_if(every_other_time()));

Thank you for reading!

That’s it for this article!

Planning on writing more about actually adding some UI into that Window and interacting with those! Soon!

Stay tuned if you are interested!!

Happy ECS-ing!

A message from our Founder

Hey, Sunil here. I wanted to take a moment to thank you for reading until the end and for being a part of this community.

Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? We don’t receive any funding, we do this to support the community. ❤️

If you want to show some love, please take a moment to follow me on LinkedIn, TikTok, **Instagram. You can also subscribe to our [weekly newsletter](https://newsletter.plainenglish.io/)**.

And before you go, don’t forget to clap and follow the writer️!


메타데이터
post_id
d47ef5eef481
slug
bevy-a-rust-game-engine-basic-concepts-in-super-detail-d47ef5eef481
url
https://blog.stackademic.com/bevy-a-rust-game-engine-basic-concepts-in-super-detail-d47ef5eef481
canonical_url
https://blog.stackademic.com/bevy-a-rust-game-engine-basic-concepts-in-super-detail-d47ef5eef481
author_url
https://medium.com/@itsuki.enjoy
status
ok
fetched_at
2026-06-15 20:49:13