Property Mapping — #[gdbevy(...)] Grammar

This page was previously "Property Mapping with BevyBundle". The BevyBundle macro is gone; the unified #[gdbevy(...)] attribute replaces it on both bridging derives.

Both GodotNode (component-first) and BevyComponents (Godot-first) accept #[gdbevy(...)] annotations. The keys available depend on which derive you are using.

Component-first (GodotNode)

Companion component — newtype

Generate one exported property and wrap its value in a newtype component:

#![allow(unused)]
fn main() {
#[derive(Component, GodotNode, Default)]
#[gdbevy(base = CharacterBody2D, class_name = Player2D)]
#[gdbevy(require(speed: Speed, as = f32, default = 250.0))]
pub struct Player;
}
  • speed becomes a #[export] speed: f32 on the generated Player2D class (default 250.0).
  • When the node enters the tree, Speed(speed_value) is inserted.
  • as = f32 is required here — the macro cannot see Speed's inner type.

Companion component — struct

Generate multiple exported properties for a multi-field component:

#![allow(unused)]
fn main() {
#[derive(Component, GodotNode, Default)]
#[gdbevy(base = CharacterBody2D, class_name = Enemy2D)]
#[gdbevy(require(stats: Stats {
    health(as = f32, default = 100.0),
    mana(as = f32, default = 50.0),
}))]
pub struct Enemy;
}

Each inner field(as = T, …) follows the same as/default/with grammar as the newtype form. The name before : (e.g. stats) is required by the grammar but ignored — the generated export properties use the inner field names (health, mana).

Inspector metadata for generated exports

Metadata can be placed on a named or tuple primary field with #[gdbevy(export, ...)], on require(prop: Comp, ...), or on each field inside require(group: Comp { field(as = T, ...), ... }). Use one attribute per primary field.

Export types must implement gdext's Export trait. Rust String does not, so export a GString and convert it when constructing the component:

#![allow(unused)]
fn main() {
use bevy::prelude::Component;
use godot::prelude::GString;
use godot_bevy::prelude::GodotNode;

#[derive(Component)]
pub struct WeaponKind(pub String);

#[derive(Component, GodotNode, Default)]
#[gdbevy(class_name = WeaponNode)]
#[gdbevy(require(
    kind: WeaponKind,
    as = GString,
    with = from_godot_string,
    default = GString::from("Hands"),
    description = "Weapon selected by the designer",
    hint = ENUM,
    hint_string = "Hands,Knife"
))]
pub struct Weapon;

fn from_godot_string(value: GString) -> String {
    value.to_string()
}
}

description takes a string literal. Primary field /// docs are copied before an explicit description. hint names a bare Godot PropertyHint variant, and hint_string takes an expression. A hint string requires a hint.

Enable the register-docs feature on your godot-bevy dependency to register property descriptions in Godot's editor:

godot-bevy = { version = "0.12", features = ["register-docs"] }

This feature is off by default and requires Godot API 4.3 or later. With it off, hints still work and descriptions remain Rust docs. API 4.2 consumers must leave it disabled.

Marker companion

Insert a component via Default with no exported property:

#![allow(unused)]
fn main() {
#[gdbevy(require(Stunned))]
}

Primary field with conversion

Fields on the component struct itself can declare a type conversion:

#![allow(unused)]
fn main() {
#[derive(Component, GodotNode, Default)]
#[gdbevy(base = Node2D, class_name = Slider2D)]
pub struct Slider {
    /// Editor shows 0–100; component gets 0.0–1.0
    #[gdbevy(export, as = f32, with = percentage_to_fraction)]
    pub value: f32,
}

fn percentage_to_fraction(v: f32) -> f32 { v / 100.0 }
}

as = T is optional when the field type already implements gdext's Export trait; add it only when the export type differs from the Rust field type.

Tuple fields

Tuple structs are supported for component-first nodes. Exported fields are named value0, value1, and so on in the generated Godot class.

#![allow(unused)]
fn main() {
#[derive(Component, GodotNode, Default)]
pub struct Velocity(
    #[gdbevy(export)] f32,
    #[gdbevy(export)] f32,
);
}

All component-first keys

PlacementKeyRequired?Meaning
structbase = GodotBaseno (default: Node)Godot class to extend
structclass_name = Nameno (default: <Struct>BevyComponent)Generated class name
structrequire(…)noCompanion component (see forms above)
fieldexportyesMarks the field as a generated Godot export
fieldas = TnoGodot export type
fielddefault = exprnoEditor default (via #[init(val = …)]); a pure-Bevy spawn(T) uses the struct's own Default — make them agree if you rely on spawn(T).
fieldwith = fnnoGodot-value → field-value conversion
require(prop: Comp, …)as = TyesExport type for the generated property
require(prop: Comp, …)default = exprnoExport default
require(prop: Comp, …)with = fnnoConversion before constructing the component
generated primary or companion fielddescription = "..."noProperty docs, registered with register-docs
generated primary or companion fieldhint = NAMEnoBare Godot PropertyHint variant
generated primary or companion fieldhint_string = exprnoHint string, requires hint

Godot-first (BevyComponents)

Field binding

Map a single #[export] property to a newtype component:

#![allow(unused)]
fn main() {
#[derive(GodotClass, BevyComponents)]
#[class(base = Node2D, init)]
pub struct EnemyNode {
    base: Base<Node2D>,

    #[gdbevy(component = Health)]
    #[export]
    max_health: f32,

    #[gdbevy(component = Speed, with = to_speed)]
    #[export]
    #[init(val = 100.0)]
    speed: f32,
}
}

component = Comp is required. with = fn is optional. as and default are not allowed — gdext's #[init(val = …)] owns defaults on this path.

Marker at the struct level

#![allow(unused)]
fn main() {
#[derive(GodotClass, BevyComponents)]
#[class(base = CharacterBody2D, init)]
#[gdbevy(require(Player))]
pub struct PlayerNode {
    base: Base<CharacterBody2D>,
    // ...
}
}

N→1 binding

Build a multi-field component from several existing #[export] properties:

#![allow(unused)]
fn main() {
#[derive(GodotClass, BevyComponents)]
#[class(base = CharacterBody2D, init)]
#[gdbevy(require(Stats { health: max_health, mana: max_mana }))]
pub struct PlayerNode {
    base: Base<CharacterBody2D>,
    #[export] max_health: f32,
    #[export] max_mana: f32,
}
}

All Godot-first keys

PlacementKeyRequired?Meaning
structrequire(Marker)noInsert Marker::default()
structrequire(Comp { bevy_field: godot_field, … })noBuild struct component from existing exports
fieldcomponent = CompyesBevy component type (Comp(value))
fieldwith = fnnoGodot-value → component-value conversion

Godot-first classes own their gdext declaration, so use gdext's native #[var(hint = ..., hint_string = ...)] and Rust documentation attributes for Inspector metadata on those fields.

Native property descriptions also require godot-bevy/register-docs and Godot API 4.3 or later. Keep metadata on the native attributes, outside gdbevy.

Reserved keys

into and sync are reserved for the upcoming component-sync feature and will produce a compile error if used. Use only the keys documented above.