Nodes from Components
godot-bevy bridges Godot nodes and Bevy entities through two derive macros that share one #[gdbevy(...)] attribute grammar:
GodotNode(component-first) ā you write a BevyComponent; the macro generates the Godot class.BevyComponents(Godot-first) ā you write theGodotClassyourself; the macro wires its#[export]fields into Bevy components.
Pick whichever fits your workflow. Both produce the same result at runtime: a Godot scene node whose editor-set values become Bevy components on the entity.
Component-first: GodotNode
Derive Component and GodotNode on a plain Rust struct. The macro generates a Godot class with #[export] properties for each annotated field, plus an autosync registration so that components are inserted when the node enters the scene tree.
Minimal marker node
#![allow(unused)] fn main() { #[derive(Component, GodotNode, Default, Debug, Clone)] #[gdbevy(base = Area2D, class_name = Gem2D)] pub struct Gem; }
This generates a Gem2D Godot class (extending Area2D). When Gem2D enters the scene tree, a Gem component is inserted on its entity. No exported properties.
Primary fields with defaults
Fields on the component struct can be exported to the editor:
#![allow(unused)] fn main() { #[derive(Component, GodotNode, Default, Debug, Clone)] #[gdbevy(base = Area2D, class_name = Door2D)] pub struct Door { #[gdbevy(export, default = LevelId::Level1)] pub level_id: LevelId, } }
export is required and marks the field as a generated Godot export. default = expr sets the editor default (via #[init(val = ā¦)]). The field's Rust type is used as the Godot export type unless you add as = T.
Available keys on a field-level #[gdbevy(...)]:
| Key | Meaning |
|---|---|
export | (required) Marks the field as a generated Godot export. |
as = T | Godot export type (defaults to the field's Rust type). |
default = expr | Editor default value (via #[init(val = ā¦)]). A pure-Bevy spawn(T) uses the struct's own Default ā make them agree if you rely on spawn(T). |
with = fn | Converts the Godot value before assigning to the field. |
Companion components
Use require(...) at the struct level to generate exported properties that feed separate companion components. This is useful when a single node should spawn multiple components ā without needing a separate bundle type.
#![allow(unused)] fn main() { #[derive(Component, GodotNode, Default, Debug, Clone, Reflect)] #[reflect(Component)] #[gdbevy(base = CharacterBody2D, class_name = Player2D)] #[gdbevy( require(speed: Speed, as = f32, default = 250.0), require(jump_velocity: JumpVelocity, as = f32, default = -400.0), require(gravity: Gravity, as = f32, default = 980.0), )] pub struct Player; }
The Player2D Godot class gains three #[export] properties (speed, jump_velocity, gravity). When the node enters the tree, Player, Speed(ā¦), JumpVelocity(ā¦), and Gravity(ā¦) are all inserted on the entity.
require forms:
| Form | Meaning |
|---|---|
require(Marker) | Insert Marker::default() ā no export property. |
require(prop: Comp, as = T, default = expr) | Generate one export property; build Comp(value). as = T is required. |
require(prop: Comp { field(as = T, default = expr), ⦠}) | Generate multiple properties; build a struct Comp { field: value, ⦠}. The name before : is required by the grammar but ignored ā the generated export properties use the inner field names. |
with = fn is available on all non-marker forms and converts the Godot value before it is passed to the component constructor.
Pure-Bevy spawn: because
GodotNodealso registers required components,commands.spawn(Player)in a test or a headless context insertsSpeed,JumpVelocity, andGravitywith the declared defaults ā no Godot scene needed.
Godot-first: BevyComponents
When you already own the GodotClass struct ā or prefer writing gdext code yourself ā derive BevyComponents instead of GodotNode. The macro emits only the Bevy side; no new Godot class is generated.
#![allow(unused)] fn main() { #[derive(GodotClass, BevyComponents)] #[class(base = Node2D, init)] #[gdbevy(require(Player))] struct PlayerNode { base: Base<Node2D>, /// Maps the `speed` export to `Speed(to_speed(speed))`. #[gdbevy(component = Speed, with = to_speed)] #[export] #[init(val = 250.0)] speed: f32, } }
Field-level #[gdbevy(...)] keys on a Godot-first binding:
| Key | Meaning |
|---|---|
component = Comp | (required) The Bevy component to insert ā Comp(value). |
with = fn | Converts the Godot value before constructing the component. |
as and default are not allowed on Godot-first field bindings ā gdext's #[init(val = ā¦)] owns defaults, and the field's type is already visible.
Struct-level require(...) on the Godot-first path supports markers and Nā1 bindings:
| Form | Meaning |
|---|---|
require(Marker) | Insert Marker::default(). |
require(Comp { bevy_field: godot_field, ⦠}) | Build Comp from existing export fields. |
Which derive to use
GodotNode | BevyComponents | |
|---|---|---|
| Who writes the Godot class | Macro | You |
base / class_name | #[gdbevy(base = ā¦, class_name = ā¦)] | #[class(base = ā¦)] in gdext |
| Required-components (pure Bevy spawn) | Yes | No |
Custom init / #[godot_api] | No | Yes ā full gdext control |
Use GodotNode for new nodes defined entirely in Rust. Use BevyComponents when you need custom gdext lifecycle methods, or when the node class is shared with GDScript.