Bevy Game Engine

repository·main·Indexed 13 days ago

https://github.com/bevyengine/bevy

A data-driven, modular, and highly performant 2D/3D game engine and app framework built in Rust using the Entity Component System (ECS) paradigm. Version 0.20.0-dev features a robust ECS with entities, components, and systems, a flexible scheduling system, and specialized crates like bevy_app, bevy_platform, bevy_ptr, and bevy_reflect for dynamic type interaction.

Tokens
139K
Snippets
446
Records
598
Agent score
98%

What's inside Bevy

  1. What is Bevy Pointer

    main
    bevy_ptr is a crate designed to bridge the gap between fully unsafe raw pointers (*mut ()) and safe Rust borrows (&'a T). It allows developers to choose specific invariants to uphold, enabling the construction of progressively safer abstractions. It is particularly useful for working with heterogeneous type-erased storage (like ECS tables or typemaps) while avoiding the overhead of dynamic dispatch.
  2. Use Bevy Time for timekeeping

    main
    Bevy Time is the built-in plugin responsible for timekeeping within the Bevy game engine. It provides the necessary abstractions to track elapsed time, manage game loops, and handle different time scales (such as pausing or slow motion) across the engine.
  3. Use Reflection for Dynamic Type Interaction

    main

    Bevy's reflection system allows you to interact with Rust types dynamically at runtime. Key capabilities demonstrated in the examples include:

    • Type Registration: Automatically registering types (especially for platforms without inventory support).
    • Mutation: Using Mutation by Reflection to change data that is not natively Rust-accessible.
    • Serialization: Performing serialization and deserialization using reflection instead of the serde traits.
    • Function Reflection: Calling functions dynamically via their reflected signatures.
    • Generic Reflection: Registering concrete instances of generic types so they can be used with the reflection system.
  4. Manage data with Worlds and Resources

    main

    The World is the central container for all ECS data. It stores Entities, Components, and Resources.

    • World: Use World::new() to create a container. It provides methods to spawn entities, insert_resource, and access data.
    • Resources: Global, unique data that does not belong to any specific entity (e.g., Time, AssetServer). Resources are identified by their type. You can access them in systems using the Res<T> or ResMut<T> parameter types.
    use bevy_ecs::prelude::*
    use bevy_ecs::world::World;
    
    #[derive(Resource, Default)]
    struct Time {
        seconds: f32,
    }
    
    fn main() {
        let mut world = World::new();
        
        // Inserting a resource
        world.insert_resource(Time::default());
    
        // Accessing a resource from the world
        let time = world.get_resource::<Time>().unwrap();
    }
    
    // Accessing a resource from a system
    fn print_time(time: Res<Time>) {
        println!("{}", time.seconds);
    }
  5. Handle time and timers in Bevy

    main

    Bevy provides several mechanisms for managing time within the ECS:

    • Time handling: General explanation of how Time is managed in systems.
    • Timers: How to tick Timer resources inside systems and manage their state.
    • Virtual time: Using Time<Virtual> to implement game mechanics like pausing, resuming, slowing down, or speeding up the game world.
    // See implementation details in:
    // examples/time/time.rs
    // examples/time/timers.rs
    // examples/time/virtual_time.rs
  6. How trait reflection works

    main

    Bevy Reflect allows you to call methods from a trait on a &dyn Reflect reference, even if you don't know the concrete type at compile time.

    1. Mark the trait with #[reflect_trait].
    2. Mark the implementing type with #[reflect(TraitName)].
    3. Register the type in a TypeRegistry.
    4. Use type_registry.get_type_data::<ReflectTraitName>(type_id) to retrieve a helper object that can cast the &dyn Reflect to a &dyn Trait.
    #[reflect_trait]
    pub trait DoThing {
        fn do_thing(&self) -> String;
    }
    
    #[derive(Reflect)]
    #[reflect(DoThing)]
    struct MyType {
        value: String,
    }
    
    impl DoThing for MyType {
        fn do_thing(&self) -> String {
            format!("{} World!", self.value)
        }
    }
    
    // Usage:
    let mut type_registry = TypeRegistry::default();
    type_registry.register::<MyType>();
    
    let reflect_value: Box<dyn Reflect> = Box::new(MyType { value: "Hello".to_string() });
    
    let reflect_do_thing = type_registry
        .get_type_data::<ReflectDoThing>(reflect_value.type_id())
        .unwrap();
    
    let my_trait: &dyn DoThing = reflect_do_thing.get(&*reflect_value).unwrap();
    println!("{}", my_trait.do_thing());
  7. Understanding `no_std` support in Bevy

    main

    Bevy can be used on no_std targets (software that does not rely on the Rust standard library std), which is common in embedded environments like the Raspberry Pi Pico where no operating system is present to support threads or filesystems.

    While Bevy supports no_std environments, it has a critical dependency on the alloc crate. Because Bevy relies heavily on dynamic memory allocation, it requires access to a global allocator to compile. You cannot use Bevy in a no_std environment that lacks an allocator.

  8. How color space conversions are structured

    main

    Bevy uses a delegated conversion model to ensure all color transformations take the shortest path and minimize complexity. Instead of every color space knowing how to convert to every other space, conversions are defined based on primary relationships:

    • sRGB (Srgba) is defined by its relationship with Linear RGB (LinearRgba).
    • HWB (Hwba) is defined by its relationship with sRGB (Srgba).

    When you need to convert between two spaces that don't have a direct relationship (e.g., LinearRgba to Hwba), the library delegates the conversion through an intermediary (like Srgba) to ensure consistency and limit the domain-specific logic required for each space.

  9. Important development warnings for Bevy users

    main

    Bevy is in early development and follows a rapid release cycle. Users should be aware of the following:

    • Breaking Changes: A new version containing breaking API changes is released approximately every 3 months. Use migration guides when upgrading.
    • MSRV (Minimum Supported Rust Version): Bevy relies on recent Rust language features. The MSRV is generally close to the latest stable release of Rust.
    • Documentation: Documentation may be sparse due to the rapid pace of development.
  10. Implement Custom Shaders and Materials

    main

    Bevy supports several ways to implement custom shaders depending on your needs:

    • Standard Materials: Use Material to create shaders that work with the standard PBR pipeline.
    • Custom Render Phases: Use Custom Render Phase or Custom phase item to enqueue unique draw commands.
    • Compute Shaders: Use compute shaders for general-purpose GPU processing, such as simulating Game of Life or generating meshes via Compute Shader Mesh.
    • Post-Processing: Implement custom effects using a Custom Render Pass that runs after the main pass.
    • Advanced Techniques:
      • Bindless Textures: Use Texture Binding Array or Material - Bindless to sample multiple textures as a binding array.
      • Fullscreen Materials: Use Fullscreen Material for effects that cover the entire screen.
      • GPU Readback: Use compute shaders to write data to a buffer that the CPU can read.