The freecs Macro
The freecs::ecs! macro generates the entire World struct, component storage, accessors, query methods, and entity management from a declarative definition.
Macro Syntax
#![allow(unused)] fn main() { freecs::ecs! { World { Core { local_transform: LocalTransform => LOCAL_TRANSFORM, global_transform: GlobalTransform => GLOBAL_TRANSFORM, render_mesh: RenderMesh => RENDER_MESH, material_ref: MaterialRef => MATERIAL_REF, camera: Camera => CAMERA, light: Light => LIGHT, } Ui { ui_layout_root: UiLayoutRoot => UI_LAYOUT_ROOT, ui_layout_node: UiLayoutNode => UI_LAYOUT_NODE, } Sprite2d { sprite: Sprite => SPRITE, sprite_animator: SpriteAnimator => SPRITE_ANIMATOR, } } Resources { window: Window, input: Input, graphics: Graphics, active_camera: Option<Entity>, } } }
Nightshade uses three subsystems: Core (3D engine components), Ui (retained UI), and Sprite2d (2D sprites). Component accessors are scoped to their subsystem: world.core.get_light(entity), world.ui.get_ui_layout_node(entity), world.sprite2d.get_sprite(entity).
Each line in the component block declares:
- A field name (snake_case) - used to generate accessor methods
- A type - the Rust struct stored for this component
- A flag constant (UPPER_SNAKE_CASE) - the bitflag for queries
What Gets Generated
From the macro invocation, freecs generates:
The World Struct
#![allow(unused)] fn main() { pub struct World { // Internal entity storage (archetype tables, free lists, etc.) entities: EntityStorage, // All global resources pub resources: Resources, } }
Per-Component Accessors
For each component foo: Foo => FOO, the macro generates:
#![allow(unused)] fn main() { // Immutable access world.core.get_foo(entity) -> Option<&Foo> // Mutable access world.core.get_foo_mut(entity) -> Option<&mut Foo> // Set value world.core.set_foo(entity, value: Foo) }
Entity Management
#![allow(unused)] fn main() { // Spawn entities with a component mask world.spawn_entities(flags: ComponentFlags, count: usize) -> Vec<Entity> // Despawn entities world.despawn_entities(entities: &[Entity]) // Check if entity has components world.core.entity_has_components(entity: Entity, flags: ComponentFlags) -> bool }
Query Methods
#![allow(unused)] fn main() { // Query entities matching a component mask world.core.query_entities(flags: ComponentFlags) -> impl Iterator<Item = Entity> }
Component Flag Constants
#![allow(unused)] fn main() { pub const LOCAL_TRANSFORM: ComponentFlags = 1 << 0; pub const GLOBAL_TRANSFORM: ComponentFlags = 1 << 1; pub const RENDER_MESH: ComponentFlags = 1 << 2; // ... one per component, powers of 2 }
The Resources Struct
#![allow(unused)] fn main() { pub struct Resources { pub window: Window, pub input: Input, pub graphics: Graphics, pub active_camera: Option<Entity>, // ... all declared resources with Default initialization } }
Nightshade's World Definition
Nightshade declares 45 core components, 10 UI components, and 4 sprite components across three subsystems (Core, Ui, Sprite2d), plus 30+ resources. Here is the Core component declaration (simplified paths for readability):
Components
| Flag | Field | Type | Category |
|---|---|---|---|
ANIMATION_PLAYER | animation_player | AnimationPlayer | Animation |
NAME | name | Name | Identity |
LOCAL_TRANSFORM | local_transform | LocalTransform | Transform |
GLOBAL_TRANSFORM | global_transform | GlobalTransform | Transform |
LOCAL_TRANSFORM_DIRTY | local_transform_dirty | LocalTransformDirty | Transform |
PARENT | parent | Parent | Transform |
IGNORE_PARENT_SCALE | ignore_parent_scale | IgnoreParentScale | Transform |
AUDIO_SOURCE | audio_source | AudioSource | Audio |
AUDIO_LISTENER | audio_listener | AudioListener | Audio |
CAMERA | camera | Camera | Camera |
PAN_ORBIT_CAMERA | pan_orbit_camera | PanOrbitCamera | Camera |
LIGHT | light | Light | Lighting |
LINES | lines | Lines | Debug |
VISIBILITY | visibility | Visibility | Rendering |
DECAL | decal | Decal | Rendering |
RENDER_MESH | render_mesh | RenderMesh | Rendering |
MATERIAL_REF | material_ref | MaterialRef | Rendering |
RENDER_LAYER | render_layer | RenderLayer | Rendering |
TEXT | text | Text | Text |
TEXT_CHARACTER_COLORS | text_character_colors | TextCharacterColors | Text |
TEXT_CHARACTER_BACKGROUND_COLORS | text_character_background_colors | TextCharacterBackgroundColors | Text |
BOUNDING_VOLUME | bounding_volume | BoundingVolume | Spatial |
HOVERED | hovered | Hovered | Input |
ROTATION | rotation | Rotation | Transform |
CASTS_SHADOW | casts_shadow | CastsShadow | Rendering |
RIGID_BODY | rigid_body | RigidBodyComponent | Physics |
COLLIDER | collider | ColliderComponent | Physics |
CHARACTER_CONTROLLER | character_controller | CharacterControllerComponent | Physics |
COLLISION_LISTENER | collision_listener | CollisionListener | Physics |
PHYSICS_INTERPOLATION | physics_interpolation | PhysicsInterpolation | Physics |
INSTANCED_MESH | instanced_mesh | InstancedMesh | Rendering |
PARTICLE_EMITTER | particle_emitter | ParticleEmitter | Particles |
PREFAB_SOURCE | prefab_source | PrefabSource | Prefabs |
PREFAB_INSTANCE | prefab_instance | PrefabInstance | Prefabs |
SCRIPT | script | Script | Scripting |
SKIN | skin | Skin | Animation |
JOINT | joint | Joint | Animation |
MORPH_WEIGHTS | morph_weights | MorphWeights | Animation |
NAVMESH_AGENT | navmesh_agent | NavMeshAgent | Navigation |
LATTICE | lattice | Lattice | Deformation |
LATTICE_INFLUENCED | lattice_influenced | LatticeInfluenced | Deformation |
WATER | water | Water | Rendering |
GRASS_REGION | grass_region | GrassRegion | Rendering |
GRASS_INTERACTOR | grass_interactor | GrassInteractor | Rendering |
TWEEN | tween | Tween | Animation |
Resources
The Resources block includes:
| Field | Type | Feature Gate |
|---|---|---|
window | Window | always |
secondary_windows | SecondaryWindows | always |
user_interface | UserInterface | always |
graphics | Graphics | always |
input | Input | always |
audio | AudioEngine | audio |
physics | PhysicsWorld | physics |
navmesh | NavMeshWorld | always |
text_cache | TextCache | always |
mesh_cache | MeshCache | always |
animation_cache | AnimationCache | always |
prefab_cache | PrefabCache | always |
material_registry | MaterialRegistry | always |
texture_cache | TextureCache | always |
active_camera | Option<Entity> | always |
event_bus | EventBus | always |
command_queue | Vec<WorldCommand> | always |
entity_names | HashMap<String, Entity> | always |
Conditional resources are included only when their feature flag is enabled, using #[cfg(feature = "...")] attributes in the macro.