DEV Community

Fumiya Funatsu
Fumiya Funatsu

Posted on • Edited on

Bevy Engine, as a Small Unity, Big Processing (P5)

(This article is self-translation from my original post in Japanese. I'm sorry if not translated well.)

2D Bloom sample of Bevy Engine 1

Introduction: Creative Coding and Processing

There are many game engines out there, what are you using now?

I like so-called creative coding, and I use, for example, Unity, UE5, Touch Designer, Notch, React, etc., to make what I have in mind. I always choose a tool that seems to be just right for prototyping each time.

When I wonder what the origin of creative coding is for me, I think it must be Processing (also called P52).

When I look back on why I love Processing, it was just "simple", as ease of use. Even today, that philosophy continues in the form of p5.js and openFrameworks. For example, p5.js has an online editor in your browser 3.

p5.js online editor

From that time on, Processing required only one editor and nothing else, and we just enjoyed pure simplicity of writing code.

Large-scale game engines and the in-between ravine

By the way, creative coding is a formless concept, and I consider the so-called visual editors in Blender, Unreal Engine, etc. as the same creative coding.

Blender's geometry node (Figure: Blender's geometry node 4)

Material Editor in Unreal Engine 5 (Figure: Material Editor in Unreal Engine 5 5)

In fact, I myself make full use of such things to give shape to my ideas, and as the word of "no code" suggests, I feel "code" has become just "a concept" nowadays.

Web browsers are also one type of engine, and "engines", not limited to game engines, are the backbone that supports our production and have become indispensable.

However, sometimes I feel a desire to go back to the roots of something lightweight and easy to use like Processing, or I want something that combines modern concepts (such as reactive programming in React) and ease of use, in other words, something in between Unity and Processing, which I can get my hands on immediately, without having to wait for dozens of GB of editors to download 6.

Bevy Engine, as a in-between Unity and Processing

While I was thinking about above, I came across the Bevy Engine.

3D Gizmos example of Bevy Engine 7

Bevy Engine is open source and has not been developed for many years, although, there are already many volunteer plug-ins, just like add-ons to openFrameworks.

Similar to Processing and openFrameworks, the core is written small, with such plugin systems being added as you need.

And most importantly, you basically don't need any tools other than Rust (cargo). First, create an empty project with $ cargo new bevy-hello 8, move to the project folder with $ cd bevy-hello, add bevy as a library with $ cargo add bevy, and then just paste any code in src/main.rs (e.g. the following) and run $ cargo run to get it working.



// hello world example of src/main.rs

use bevy::prelude::*;

fn main() {
    App::new()
        //.add_plugins(DefaultPlugins) // uncomment this to show the window
        .add_systems(Startup, hello_world)
        .run();
}

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


Enter fullscreen mode Exit fullscreen mode

hello world with VSCode editor

It takes a while to start (build, compile) the first time, but after the second time it reloads surprisingly fast and can hot-reload assets if you need.

Short look at ECS (Entity Component System)

ECS's mock-up diagram (Figure: ECS's mock-up diagram 9)

First, "System".

Now that we have written what is called "Hello World," let's take a look at some other code.



// (first part omitted)

fn main() {
    App::new()
        .add_plugins(DefaultPlugins) // this is a magic spell
        .add_systems(Startup, setup)
        .add_systems(Update, (system, rotate_camera, update_config))
        .run();
}


Enter fullscreen mode Exit fullscreen mode

This is the beginning of the code taken from a sample called 3D Gizmos, where add_systems(Startup, ...) and add_systems(Update, ...) registers functions, in terms of Processing , setup (a function called only once) and draw (a function called every time when updating).

Let's take a look at one of the functions actually registered.



fn system(mut gizmos: Gizmos, time: Res<Time>) {
    gizmos.cuboid(
        Transform::from_translation(Vec3::Y * 0.5).with_scale(Vec3::splat(1.)),
        Color::BLACK,
    );
    gizmos.rect(
        Vec3::new(time.elapsed_seconds().cos() * 2.5, 1., 0.),
        Quat::from_rotation_y(PI / 2.),
        Vec2::splat(2.),
        Color::GREEN,
    );

    // (last part omitted)
}


Enter fullscreen mode Exit fullscreen mode

Here's the amazing part: if you specify a type for the arguments that points to what you want, and it will automatically bind that value. It's a trick that could be called DI (Dependency Injection), how cool :)

Startup basically does registering elements, like HTML or JSX in React. The elements are updated by functions registered in Update, which is equivalent to JavaScript in HTML.

We call this group of functions "System", and basically everything that does any work is written in "System" 10.

The difference from draw in Processing is that the drawing itself is basically done automatically 11. So, instead of draw, it's called update, and in update, basically only parameters such as values and components are changed, not the drawing itself. This is similar to React and others.

All are Entity, all attributes are Component, all data are Resource

And this is where it is similar to Unity (strictly speaking Unity DOTS): all elements are derived from something called Entity. Everything is Entity (same as Game Object in Unity), so it is easy to handle in a unified way, and furthermore, all attributes are derived from Component, and since Components exist under the same concept in Unity, it is easy to understand how to read them.

In addition, all data that you want to keep, which are so-called variables, become Resource, and assets such as images and 3D data are Asset. This is also simple and easy to understand.

And how to get them from the System is the same as before: declare a type as an argument and it will fetch them for you 12. It's magic.



fn check_zero_health(
    // Access Entity with `Health` and `Transform` Component.
    // Get `Health` as read-only, get `Transform` as mutable.
    // Optional: get the `Player` Component if it exists
    mut query: Query<(&Health, &mut Transform, Option<&Player>)>,
) {
    // Get all matching Entities
    for (health, mut transform, player) in query.iter_mut() {
        eprintln!("Entity at {} has {} HP.", transform.translation, health.hp);

        // If HP is 0, move to center
        if health.hp <= 0.0 {
            transform.translation = Vec3::ZERO;
        }

        if let Some(player) = player {
            // here Entity is `Player`.
        }
    }
}

// code cited from (includes comments, but I translated into English from Japanese):
// https://xianliang2032.hatenablog.com/entry/2021/09/28/Rust_Bevy_Query%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6


Enter fullscreen mode Exit fullscreen mode

I would like to keep just as introduction to this architecture, which is called ECS (Entity Component System), but you can see that it has the same general-purpose mechanisms as Unity and others.

Unity and Bevy terminology correspondence chart (Figure: Unity and Bevy terminology correspondence chart)

Other features: Cross-platform, low-level drawing engine abstraction, etc. (skips today)

Cross platform check table 13

There are many other interesting features of the Bevy Engine, such as the wgpu mechanism, which allows automatic cross-platform support for DirectX, Metal, Vulkan, WebGL, etc. with a single code, but skips description today, because it would be commonplace in Unity and other platforms nowadays.

Last one: Plug-in system for more and more extensions / e.g. Inspector, Gizmo, Particles ...etc...

Finally, I would like to mention a few things about plug-ins. For example, if you get greedy, you may wish to have an "editor" like Unity. Plug-ins can do it.

Bevy editor plug-in like Unity 14

For example, if you want a GPU particle system, you can use bevy_hanabi, with display a more casual inspector, bevy-inspector-egui can be used to.

bevy_hanabi and bevy-inspector-egui

The list of plug-ins is endless, but at last if you felt "want to write a UI in HTML!", a plugin called belly will do it for you.

belly for bevy

This kind of casual extension is fun part of any engine, and thanks to the ECS architecture, I'm happy to be able to use it in parallel and well-ordered with other Entity, Component, and other structures without any discomfort.

p.s. Bevy Plug-in list is here: https://bevyengine.org/assets/#assets, and here https://github.com/topics/bevy-plugin

Summary: Merging "past" and "future" of Creative Coding will be...?

Now, I have introduced the Bevy Engine as a "just fit" product in between, for those of you who are tired of Unity, Unreal Engine (and other game engines), and who are wondering what to do next to Processing in your programming classes.

By the way, if you actually try to write a game with the Bevy Engine, you will be surprised at how hard it is to understand the errors that Rust produces... (LOL 😂) 15

...Half joking but... it's largely a matter of getting used to it, but Bevy Engine is designed so that you don't have to be so conscious of ownership and lifetime (which are difficult parts of Rust) thanks to the component system called ECS. And probably in the future, something like the Bevy Engine will be available from general languages such as TypeScript, and I feel that this may be the future of creative coding.

Bevy has only been around for a short period of time, so there is no handy sketch sharing platform online like p5.js, or there is no web-site or list of add-ons (p.s. Bevy plug-ins list
already existed in official!), but already Example can be viewed interactively with WebGL, and I think such a future is just around the corner.

I hope that little by little, from a place like this, the best of each of the "past" and "future" of creative coding can be merged well together.


Additional information

  • You can use shadplay as the same kind of tool like ShaderToy for wgsl shader language, it's useful if you want to try only effects without worrying about ECS, or debugging purposes 16.

  1. quoted and reconstructed from https://bevyengine.org/examples/2D%20Rendering/bloom-2d/

  2. Originally, Processing had used the domain proce55ing.net, it became origin of P5. see https://en.wikipedia.org/wiki/Processing#History

  3. As a side note, cultures such as Tweet Processing is also interesting related to one-liners. 

  4. Image cited from https://forest.watch.impress.co.jp/docs/serial/blenderwthing/1373040.html (Japanese article). 

  5. Image cited from https://docs.unrealengine.com/5.0/en-US/organizing-a-material-graph-in-unreal-engine/

  6. Just to follow up on the capacity thing, capacity and specs are relative, and I think the impression of what simplicity is will always change. In fact, Processing at the time was about 300 MB, which was not small in capacity. So, in the context of this article, I'll assume here that simplicity means keeping the core simple by using plugin systems, abstractions, etc. (Unity, for example, is already simple enough, I think). 

  7. quoted and reconstructed from https://bevyengine.org/examples/3D%20Rendering/3d-gizmos/

  8. Commands beginning with $ refer to what you type into the terminal. (Omit the $ and type what follows.) 

  9. Figure cited from https://blog.mozvr.com/introducing-ecsy/

  10. "System" also allows you to specify dependencies and create combinations (called sets), but this is redundant and will not be discussed here. 

  11. Note that this does not mean that you cannot customize your drawing, as there is a good balance provided between high-level APIs such as ECS, low-level APIs for the drawing engine itself (wgpu), and mid-level APIs that fall in between. Using mid-level APIs or low-level APIs, assured that the shader pipeline and various buffer objects can be manipulated. 

  12. Code cited from (includes comments, but I translated into English from Japanese) https://xianliang2032.hatenablog.com/entry/2021/09/28/Rust_Bevy_Query%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6

  13. Table cited from https://bevy-cheatbook.github.io/gpu/intro.html

  14. Image cited from https://zenn.dev/eloy/articles/ea11899ee3dbe4 (Japanese article). 

  15. I omitted it in the article, but when you actually write the code, if you put rust-analyzer in VSCode, the completion will work perfectly, so you are now ready. Have a happy coding! 

  16. Information was received from the official Bevy Discord community. I really appreciate all the support from the community. 

Top comments (0)