Bevy 0.10: data oriented game engine built in Rust
bevyengine.org
bevyengine.org
I wouldn't be surprised if in 2-4 years, we see some fantastic, revolutionary indie game coded in Bevy that totally breaks the mold in some way. The next Minecraft, basically.
I say that because Unreal sucks for developers who like to code in text, want a fresh blank canvas, and don't want someone else making all the decisions about how gameplay should be created: Basically, the exact same type of person who's likely to make something really cool.
And Bevy's coded in Rust! Bevy gains all of Rust's safety, ergonomics, speed; yet it avoids pitfalls with some OSS Rust products: it doesn't overuse lifetimes or macros and is relatively easy to follow.
Plus, my understanding is that Bevy is way better at multithreading than Unreal and Unity; and consumer core counts just keep going up.
IMO, there's a big opportunity for someone(s) with resources to invest in Bevy's development somehow.
Just uhhh... don't look at the core ECS implementation, haha. There's probably no other concentration that much use of unsafe in the entire Rust ecosystem, outside of the stdlib. Could always use some more eyes scrutinizing the design and API surface there. I'm saying this as someone who's wrote most of the optimizations to the ECS in the past 3-4 releases. Definitely have introduced (and fixed) my fair share of undefined behavior bugs due to hyper-aggressive use of unsafe.
Being designed for ECS first should in theory open up a lot of potential for optimizations well beyond traditional OO game engines. You can already see that many parts of the game loop/workflow is parallelized.
I've been rendering procedurally generated chunks via Bevy's task system, and it's pretty insane how fast it is. Can generate meshes from millions of (live function queried) points and render the results with lighting, etc in a second or so leveraging SIMD and threading.
Rust really feels like the ideal language to replace C/C++ for gamedev.
I've been working on a project in Bevy for the last few weeks, having previously attempted it in TS+pixie.js and Godot. It's been a joy in comparison, with much faster progress - in TS I was constantly fighting the ecosystem, and GDScript is just awful, particularly it's type system. Thank you!
Glad you're enjoying Bevy so far!
We want to make a break from Unreal pixel streaming and Rust on the frontend delivered over WASM/WebGPU would be so ideal. The rest of our stack is Rust, so it would slot right in with our Rust monorepo.
[1] I was donating to one of the devs until GitHub removed PayPal payments without warning or mitigation about a week ago. I'll fix that soon.
Code-first is lovely, but being able to fine-tune things without a recompile is key, and the workflow needs to work for any artists on larger teams too.
I see in the Bevy FAQ that you've contributed to other open source game engines. How did you get originally interested in game engine development? Are you developing a game yourself using Bevy? Or contracting with any studios using Bevy in their games?
Currently I only build small jam-like games in Bevy (I participated in https://itch.io/jam/bevy-jam-1 and https://itch.io/jam/bevy-jam-2). But I plan on picking up a larger project soon. I've got the itch :)
How is Bevy going to approach this kind of render pipeline customization?
(I'm very new to Bevy. So if it's a stupid question please bear with me.)
For a comparison with Unity, it's equivalent to the scriptable render pipeline, but abandoning the default one doesn't mean you need to write your own pipeline from the ground up.
And what's the equivalance of Unity's Frame Debugger? Since Bevy doesn't have a GUI editor yet, I guess the answer is just to use Render Doc?
The best 10,000ft view that doesn't just point you at the code itself is probably https://bevy-cheatbook.github.io/gpu/intro.html. If you need finer details, you'll need to read the code itself, or ask in the #rendering(-dev) channels on the Discord.
Again, I'm not happy with the state of public documentation for this, but as seen with this release, it's still heavily in flux. I don't think we've had a single release since 0.5 that hasn't made major architectural changes of some kind.
> And what's the equivalance of Unity's Frame Debugger? Since Bevy doesn't have a GUI editor yet, I guess the answer is just to use Render Doc?
Yep, just use RenderDoc right now. I don't think we'll have a good solution for this for a long while, since building one of those from the ground up is a huge undertaking.
Thank you very much for the answer!
Do I need to learn Rust first, or does Bevy use a limited subset of the language like Unity and C#. Although I'd argue till I'm blue in the face C# is a much easier language.
In simple game code you don't need to write your own traits, use unsafe, and like laundmo says, the borrow checker pains are way easier than in most Rust codebases because there's already a nice architecture in place.
How did you tackle the problem of components containing geometry for platform-specific graphics engines? Does Bevy use platform-specific components? If an application needs multiple offscreen textures to compose a frame, are there components that contain metadata for which passes they belong to (so, now, platform and pass specific components)?
1. We build on top of cross platform abstractions with feature detection (wgpu is our cross platform low level graphics library, cpal is that for audio, etc). We build our features in a way that adjust the ECS app logic according to what features are available at runtime 2. In a few cases we do "compile time feature/platform detection" to select the appropriate code for a given platform. But most "Bevy features" don't use this / we opt for (1) more than anything else
In the case of geometry: that has a "dynamic" representation that can be adjusted according to the platform context (although in practice we don't do any platform-specific geometry transforms yet). And that gets written to the platform's gpu using wgpu.
Bevy has a "render graph", which feeds on Bevy ECS data. We adapt the render graph (and the ECS data that drives it) according to the features supported on a given platform.
(not _yet_ my highest priority, but others are already investigating this)
The only reason we haven't merged it yet is we need to sort out a high level "app api" thing: WebGPU requires async init (as in the Rust async feature). The PR above solves this by making our plugin init async. This is the most straightforward, but it makes people put `.await` in their app init, which we're not sure is the right call yet. We're discussing alternatives that might "encapsulate" the async.
Additionally, while obviously not perfect, are you following new developments in AI based animation[0] and prediction based light transport[1]? Do you expect these types of automation to eventually become first class within Bevy, or any thoughts on how they can be best integrated into a Bevy game?
I'm one of the two developers currently behind our longer term animation efforts, and I am keeping up with the state of the art in terms of animation compression, streaming, composition, and generation, but there's still a sizable amount of groundwork and plumbing before any of that can even be attempted. We definitely need a lot more eyes on this area of the engine.
I'm now unemployed on a sabbatical, and looking to pour my heart into game development. Jumping into such a large, moving project can be intimidating. How can I get started effectively contributing to Bevy?
I'll add, a ton of work seems to be happening in rendering. This is not an area I can contribute or know enough about, but I am able/willing to help with just about everything higher level than that.
I strongly suggest any of the docs issues, as it doesn't require you to write any actual functionality, but forces you to get to know the architecture, the language, and the core parts of the engine itself.
Rendering is definitely moving really fast and we have a lot of accumulated domain expertise in the area now. If you're looking to potentially bootstrap or collaborate on design in a new space: animation or audio both need a lot more love. I'm one of the two animation SMEs and I still am playing catchup to the state of the art.
Real answer: Bevy is already very featureful, but we have a long way to go before we can start making "stable api guarantees". Locking things down right now would prevent us from achieving the best apis we can. We are building the engine in the open and people will naturally still take dependencies on us, even under these constraints. We are very careful about our messaging / provide warnings in the appropriate places.
In practice, migrating between versions is generally pretty straightforward and we provide migration guides to help the process. The dust is already settling on some of our APIs / they're starting to feel "stable". Many people (and companies) are happily building real products right now in Bevy (and porting them across versions).
The problem is that "the prototyping phase" isn't a real phase, and this doesn't specify if breaking changes will occur between major, minor, or patch versions. It gives developers no guidance of what semver to track based on their own willingness to endure breaking changes. Using a library that still can't tell you that is just asking for trouble.
> Initial development releases starting with "0.y.z" can treat changes in "y" as a major release, and "z" as a minor release. "0.0.z" releases are always major changes. This is because Cargo uses the convention that only changes in the left-most non-zero component are considered incompatible.
The 0.x is essentially used as way to tell people to use at their own risk since it isn't considered stable yet. Using a library like this isn't asking for trouble if you know what you are dealing with and in the context of bevy it currently means a breaking change every 3 months with a blog post and migration guide.
People can run anything they want however the fuck they want, I just won't use it. How's that?
You just assumed this, but this isn't true. Bevy uses semver like most of the rust ecosystem.
The warning is just telling people that the breaking changes will be frequent. It doesn't mean they will be in a patch release. I don't understand what's to be confused about here. Bevy never specified any custom release strategy because there isn't one, it's just semver like almost everything else in rust.
> doesn't specify if breaking changes will occur between major, minor, or patch versions
What I'm telling you is that it _does_ specify that: by using a 0.x.y version they are explicitly specifying that breaking changes can occur between _any_ version. If you don't want to use Bevy because of that, then that's fine.
In Rust, 0.x.y would treat .y as NON-BREAKING, BACKWARDS-COMPATIBLE changes.
I don't think I'm stepping beyond the pale of reasonable engineering when I suggest that a package in the Rust ecosystem that doesn't follow the Rust semver guidance -- and doesn't EXPLICITLY STATE that they don't follow the Rust semver guidance -- is a library I won't touch.
The fact that there's so much confusion on this thread about what their semver policy actually is shows exactly why it should be stated upfront and not masked behind a generic "stability warning."
semver says that in 0.x.y, "anything goes." But that is just not true with Cargo.
This is also documented, emphasis mine:
> This guide uses the terms "major" and "minor" assuming this relates to a "1.0.0" release or later. Initial development releases starting with "0.y.z" can treat changes in "y" as a major release, and "z" as a minor release. "0.0.z" releases are always major changes. This is because Cargo uses the convention that only changes in the left-most non-zero component are considered incompatible.
While the semver spec has contained that line about 0.y.z, the semver spec also does not define ranges, that is, how specifying version requirements works. Cargo implements these things, like ^ and > and etc, the exact same way that every other semver implementation implements them.
That’s because very quickly the “use minor like major when major == 0” thing became the de facto way that people used semver in its earlier days. Implementations understood this, but the spec was never changed.
So Cargo does follow semver in practice, like everyone else. There’s a combination of discrepancy and under-specification in the spec; which is a great reminder that specifications tend to follow implementations, not the other way around.
It’s on my long list of things to do to eventually reconcile this in the semver specification, but it’s been a few years and I still haven’t managed it, so no promises there.
> Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.
It is also simultaneously the case that "anything may change at 0.x.y" is not true for Cargo. Cargo is stricter than "anything may change."
Maybe what you're saying is that Cargo is consistent with semver, but "just" stricter? I'd agree with that, absolutely. That is why I try hard to not say "Cargo doesn't follow semver." (Although it's easy to slip on that one.)
“Just stricter” is much more accurate than “doesn’t follow”, for sure.
“No semver implementation follows the major version zero sentence in the semver spec, and Cargo does what they all do” is maybe an even closer approximation. We’ll see how good I can do with more than 60 seconds rest between typing :)
- Don't put 0.0.x in prod ever.
- Probably don't put 0.1.x in prod, either.
- Maybe put 0.40.x in prod. Pin to 0.40 if you do.
- Put 1.x.x in prod, because they're basically done with it.
I get upset when these conventions aren't followed and I'm using something expecting them to be.
I did skip 0.7 since I remember upgrading from 0.6 to 0.8 directly, though.
The minor jumps are sometimes very small changes, sometimes giant ones (but alas, it's still a 0.x project, so I got not qualms)
The entire Rust ecosystem works on a model of breaking changes for 0.x version happening in minor but not patch releases. In fact, I'm struggling to think of any software project that intentionally releases breaking changes in patch releases.
It's specifically not a question of knowing how the Rust community usually does these things, it's a question of "does this package conform to Rust's semver conventions, the official semver conventions, or something custom?"
The statement, _as its written in the docs_ implies "we do something custom." Whether that's true or not is irrelevant. I read it, interpreted it as a lack of governance or adherence to standards, and moved on. Any professional engineer would do the same.
Making a quick judgement based on an assumption that's provably wrong isn't really the definition of a professional engineer.
The engine is very threaded in general. We do pipelined rendering so the next frame's logic is computed while the current frame is rendering, we multithread expensive potentially high-entity tasks like animation and transform propagation, all ECS systems run in parallel (which includes the renderer systems), etc. We'll use whatever cores you give us!
I'm curious how the dynamic nature of an Editor will interact with rust.
As I am also working on improving the Rust audio library for Glicol language (https://github.com/chaosprint/glicol), I would like to know what features are the most required for merging this into a game engine, for example the HRTF mentioned in the blog. Another feature required is to embed scripting language like Rhai (https://github.com/chaosprint/glicol/discussions/112). Anything else?
From my personal side, I would love to see more demos with both action and sound (then we can talk about latency etc.), not just the graphical side.
Definitely agree that we need more sophisticated demos, especially with audio and UI, to show (and test) how it all fits together.
The whole "same engine, new assets, a lil scripting" model of game dev I can't help but think makes for a artistic monoculture. Scripting is a painkiller, not a cure, for the problem of the inflexible inscruitable C++ monster engine.
If everything is modular and in the same language, we can get less "framework-y" and more "library-y", making a porous boundary between the "game" and the "engine".
Tiny Glad is a perfect example of that. I didn't even know today they were making a game! Because it is started as just a little procedural modeling demo to be used in other games. It's great its getting attention as an actual game, but they can and definitely should eventually get the code out as a the library for use in other games too!
This was a particularly thorny area for me personally, and pretty excited to see this change.
Bevy is data oriented due to its ECS: roughly: which data an entity has attached to it decides which behaviours are run. Want something to have a health bar? Just attach the Health { current: 50, max: 100 } struct and it will have all associated behaviour like being able to take damage. Want to make it invincible again? Just remove the Health struct.
This is different from OOP engines, where you might need to keep an is_invincible boolean around to check against.
Usually this is done via an entity component system.
More performance thanks to cache locality, makes it much easier to write multithreaded gameplay code (remember, games are basically a big ol' pile of "cross-cutting concerns" [1]), as well as facilitating simulation-y code by allowing you to address game objects by arbitrary sets of properties.
---
[1]: Games often break the traditional "layers" we like to build in software architecture to enhance maintainability. That means traditionally you often get blobs of references and mutations scattered everywhere through the code. In a shooter, the visuals for a bullet need to know not only their origin and direction, but possibly the pose of any players in their path, as well as stats about the weapon, or even network latency, for a crude example.
https://en.m.wikipedia.org/wiki/Data-oriented_design
The idea is that you use data structures and algorithms to make better use of CPU cache.
Scaling Bevy Development - https://news.ycombinator.com/item?id=34387062 - Jan 2023 (20 comments)
Bevy 0.9: data oriented game engine built in Rust - https://news.ycombinator.com/item?id=33577284 - Nov 2022 (50 comments)
Automated testing in Bevy - https://news.ycombinator.com/item?id=32802402 - Sept 2022 (81 comments)
Bevy 0.8: data oriented game engine built in Rust - https://news.ycombinator.com/item?id=32287828 - July 2022 (18 comments)
Bevy 0.7: data oriented game engine built in Rust - https://news.ycombinator.com/item?id=31043668 - April 2022 (63 comments)
Bevy game engine 0.6 - https://news.ycombinator.com/item?id=29854416 - Jan 2022 (89 comments)
Bevy's First Birthday: a year of open source Rust game engine development - https://news.ycombinator.com/item?id=28132114 - Aug 2021 (13 comments)
Bevy 0.5: data oriented game engine built in Rust - https://news.ycombinator.com/item?id=26716166 - April 2021 (65 comments)
Bevy: A game engine built in Rust - https://news.ycombinator.com/item?id=26131350 - Feb 2021 (117 comments)
Bevy 0.4: data oriented game engine built in Rust - https://news.ycombinator.com/item?id=25480321 - Dec 2020 (23 comments)
Making a Snake Clone with Bevy - https://news.ycombinator.com/item?id=24999073 - Nov 2020 (11 comments)
Bevy 0.3: game engine built in Rust - https://news.ycombinator.com/item?id=24983956 - Nov 2020 (55 comments)
Bevy 0.2 - https://news.ycombinator.com/item?id=24530698 - Sept 2020 (43 comments)
Bevy: A Game Engine in Rust - https://news.ycombinator.com/item?id=24334307 - Aug 2020 (42 comments)
Bevy: A data-driven game engine and app framework built in Rust - https://news.ycombinator.com/item?id=24123283 - Aug 2020 (103 comments)
[1]https://github.com/bevyengine/bevy/discussions/3239 [2]https://github.com/Andrewp2/andrew-bevy/tree/webgpu-2 warning extremely bad code