You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
BevyCSharp.Editor, dotnet run --project BevyCSharp.Editor
Mark a struct [Behavior], give it methods with stage attributes, and a Roslyn source generator
registers it with Bevy's schedule, as a component and a system at the same time. Bevy is the engine
underneath, with its ECS, its scheduler, its timing, its input and its renderer.
usingBevy;[Behavior]publicpartialstructSpin{[Range(0f,5f)]publicfloatSpeed;privatefloat_angle;// A static method is one plain system[OnStartup]publicstaticvoidScene(BehaviorContextctx){ctx.Ecs.Add(Render.SpawnCamera3d(),Transform.LookingAt(newVec3(3f,3f,5f),Vec3.Zero,Vec3.UnitY));Render.SpawnLight(LightKind.Directional,10_000f);varcube=ctx.Ecs.Spawn();ctx.Ecs.Add(cube,newSpin{Speed=1.2f});Render.SetMesh(ctx.Ecs,cube,Render.CreateMesh(MeshShape.Cuboid,1f,1f,1f));Render.SetMaterial(ctx.Ecs,cube,Render.CreateMaterial(0.25f,0.55f,0.85f));}// An instance method runs once per entity that has one, handed the entity's transform[OnUpdate]publicvoidTick(BehaviorContextctx,refTransformtransform){_angle+=Speed*ctx.Time.Delta;transform.Rotation=Quat.FromAxisAngle(Vec3.UnitY,_angle);}}
In Program.cs
usingBevy;BevyApp.Run();
Behaviors are discovered automatically, so a consuming project needs no registration code.
This is Bevy, run unchanged underneath, so what a game draws and how its
systems are scheduled are Bevy's. What it adds is C#, behaviors reloaded while the game runs, an
editor, scene files and saves, physics, and a package with no Rust in it. What it costs is a call
across to the bridge each time C# reaches Bevy, and only what the bridge has opened, which reaches
two thirds of the Bevy examples about a game rather than Rust. Compared with Bevy has each, with what was measured.
Bevy's own examples are written in C# under Bevy's names in BevyCSharp.Examples, each opened by
name and captured beside Bevy's picture of it, and EXAMPLES.md has a
row for every one of Bevy's, saying whether it is written, can be, or waits on something the bridge
lacks. A picture opens the C# program that drew it. A group's heading counts the examples of
Bevy's group written here, and one that prints rather than draws is in EXAMPLES.md rather than in
the row.
Of Bevy's 438 examples, 373 are written in C# here and 0 more in part, 0 more can be with what is bridged, 3 wait on something the bridge lacks, 2 are declined and 60 are about Rust itself (EXAMPLES.md).
Swarm, one of the games under games/, built on the package as a game outside this repository is,
in its third wave, recorded by ./bcs record as the guide's
animated captures say:
Blockout, a level blocked out from one recipe of the modeling tools,
its hall, its stairs to the room over it and its lamp, under ray-traced lighting, walked in and
reshaped from the modeling window while it is walked in:
dotnet run --project BevyCSharp.Examples -- 3d_scene
./bcs open --example lighting --offscreen # serving, for ./bcs to control and capture
Install
dotnet new install BevyCSharp.Templates
dotnet new bevycsharp -o MyGame &&cd MyGame
dotnet run
The template makes the program above, Spin.cs and Program.cs, in a project asking for the
package of the template's own version, and dotnet run opens its window. The package carries the
managed library, the source generator and a prebuilt bridge per platform, and any other project
takes it with dotnet add package BevyCSharp.
On Linux the bridge's audio links ALSA, so the bridge needs ALSA's library to load, libasound2
(libasound2t64 on Ubuntu 24.04 and Debian 13) or alsa-lib on Fedora and Arch, which a desktop
has and a container usually does not, and drawing needs a Vulkan driver. Where there is no display,
as in a container or on a build server, BCS_OFFSCREEN=1 draws into an image instead of a window
and BCS_FRAMES ends the run after that many frames. Mesa's software Vulkan (mesa-vulkan-drivers)
draws where there is no graphics card:
BCS_OFFSCREEN=1 BCS_FRAMES=120 dotnet run
The pack run follows these steps in a container holding the .NET SDK and the two packages alone.
Guide
The guide in docs/ is for somebody using the engine, a page an area. Everything in
it is Bevy's own, bridged rather than reimplemented, so a Transform written from C# is the
transform the renderer reads and a sound played from C# is an entity in the same world.
What is the same, what it adds, what it costs, and what was measured
Controlling a running app
./bcs controls a running app from a terminal, the editor, a game or an example, answering in JSON,
which is how a change is checked without a person at the window:
./bcs open --editor --offscreen # start one, detached, with no display needed
./bcs command entity.set Cube Transform.Translation 0,2.5,0
./bcs command input.hold W,D 12 # keys held for twelve frames, inside the app
./bcs shot after.png # the next frame, as a PNG
./bcs stop
A game adds commands of its own with [Command] on a static method, and
the tools page has the rest.
Status and limitations
Early. The behavior system, the ECS bridge and the schedule work and are covered by tests that
run against a real Bevy app. Bevy's own examples are the measure of how much of it is reached, and
how many of them are written stands under Examples.
Known gaps:
A locally built package contains only the platform you built it on. Use the pack workflow, or
run build-native.sh on each target platform, to produce a package covering all of them.
A render build draws. Mesh primitives, textured physically based materials, cameras, lights,
sprites, gizmos, UI nodes and text are reachable from a behavior script, verified on Vulkan. glTF
files and .scn scenes load and spawn, audio plays, and a camera tonemaps, blooms, multisamples,
antialiases, scatters a sky over what it draws, pulls focus and finds its own exposure. What is
thin is the layer above that. A model's clips play one at a time, sprites step through no frames
of their own, and a compressed texture a desktop GPU cannot decode is not transcoded.
.github/TODO.md lists what each gap needs.
The library compiles no C# at runtime, so a game carries no compiler. Behavior scripts loaded
while an app runs go through BevyCSharp.Scripting, which the editor and the player reference
and a game references only if it loads scripts itself, over App.EnableDynamicSystems and
App.RemoveSystemsBySource.
The editor's document is a scene file, with the hierarchy, entity references and Bevy's own
components kept, and primitives and materials made in memory written as how to make them again.
A mesh built vertex by vertex is written as its geometry, and a material can be kept in a
*.material.json of its own. .github/SCENES.md has what is left.
Five of Bevy's components are mirrored by hand, and the rest are reached through Bevy's
reflection, by type path and JSON or through generated typed wrappers, at the cost of a
serialization a call. A component holds a list, inline or in a managed store, and a dictionary
in the same store, and shared values live in data assets of their own.
.github/COMPONENTS.md plans generated mirrors.
Component filters must be table-stored components, which is everything C# registers. A filter
naming a Bevy-side sparse-set component is rejected rather than silently wrong.
A cubemap comes from a file, as six square faces in a column, a row or a cross, or from a
reflection probe that captures itself. One a game renders into with its own cameras, a layer at a time, has no
bridge.
Slang shaders compile with slangc, which the build fetches. A machine without it draws what
was compiled and cached on one that had it, and cannot compile an edit.
The renderer is open at fewer points than virtualized geometry, texture streaming, screen-space
and world-space GI or reflections need. .github/RENDERING.md lists what
each of those asks of an engine, what is here, what is missing, and the order it is built in.
Building from source
Using the package needs none of this, since it ships a prebuilt bridge for every supported
platform. This
is for working on the bridge itself.
build/build-native.sh # the headless bridge
dotnet build # what copies it beside each project's binaries
dotnet test BevyCSharp.Tests/BevyCSharp.Tests.csproj
The two halves are built separately, and a rebuilt bridge is invisible until a managed build copies
it, which is why the second line is not optional. build/build-native.sh --render adds the renderer
and Dear ImGui's pass, and --editor adds the asset watcher the editor edits files with on top of it.
.github/BUILDING.md has the rest, the three native profiles and what each
costs, the platforms and their prerequisites, how the package is assembled for six runtime
identifiers, and how a release is published.
Contributing
Prose in this repository follows .github/STYLE.md, which uses no em dashes, no spaced
hyphens as punctuation, no colon inside a running sentence, no
padded section banners, and comments that explain why rather than restate the code.
License
Mozilla Public License 2.0. The full text is in LICENSE, and it ships inside the
package. So does THIRD-PARTY-NOTICES.md, which names the work of
others the package carries, Bevy and every crate the bridge is built from among it, each with its
license and the notices its own files give, for a game shipped on the package to carry in turn.
MPL-2.0 is file-level copyleft, meaning changes to files that are part of this project have to stay
under it and be made available in source form, while anything you build around it, including a
game that references the package, is yours under whatever terms you like. Bevy itself is MIT and
Apache-2.0, which this can incorporate freely.
About
Bevy C# [Behavior] on a struct are component & systems, generated at compile or runtime and wired straight into Bevy's ECS.