What Is Corrosive Engine

Corrosive engine is an open-source, ECS game engine built in Rust that focuses on code generation instead of abstraction.

Project Structure

Projects that use this engine must be structured as the following:

  • src
    • .corrosive-engine
    • comp
      • mod.rs
      • OTHER_FOLDERS
    • task
      • mod.rs
      • OTHER_FOLDERS
    • main.rs
  • build.rs

  • The .corrosive_engine contains files that are generated by the engine.
  • The comp folder is where components, resources, states, and traits are located.
  • The task files are where tasks are located.
  • The main.rs is where the corrosive-engine-builder macro is located.
  • The build.rs creates the engine at build time.

Comp folder

  1. To create components, resources, states, and traits the following rules must be applied, or the engine won't detect them.
  2. All modules must be Directory modules. Modules files or nested modules within a file won't be detected by the engine. Modules must be public.
  3. Use component, state, and resource to mark structs and enums to be used by the engine.
  4. Use the trait_bound attribute macro to mark traits.
  5. Use the trait_for macro to assign a component to a trait.
  6. Implement the SharedBehavior trait to a component so they can be used in a hierarchy.

Example:

#[derive(Resource, Default)]
pub struct Renderer2dData {
    pub(crate) data: Option<(Receiver<UnsafeRenderPass>, Sender<()>)>,
}

#[derive(Component)]
pub struct Sprite2D {
    offset: [f32; 2],
    texture: Asset<TextureAsset>,
    bind_group_layout_asset: Asset<BindGroupLayoutAsset>,
    bind_group: BindGroup,
    vertex_buffer: Buffer,
}

#[trait_bound]
pub trait Mesh2D {
    fn draw(&self, render_pass: &mut RenderPass);
    fn update(&self, render_pass: &mut RenderPass);
    fn name<'a>(&self) -> &'a str;
    fn get_bind_group_layout_desc(&self) -> 	&Asset<BindGroupLayoutAsset>;
}

trait_for!(trait Mesh2D => Sprite2D);

Task folder

  1. All modules must be Directory modules. Modules files or nested modules within a file won't be detected by the engine. Modules must be public.
  2. Use task to mark functions to be used by the engine.
  3. Should tasks need to export something, they must be inside a tuple.
  4. Use the arch_types macro to mark the arch types to be used by the engine.
  5. Should tasks need to export something, they must be inside a tuple.
  6. Tasks can only have Res<T>, State<T>, Hierarchy<T>, Arch<(&T1,&T2,...). and DeltaTime types as input and RArch<(&T1,&T2,...)>, Signal and Reset as output.

Example:

#[task]
pub fn example(
    e1: Arch<(&LockedRef<Position3>,)>,
    e2: Arch<(&LockedRef<Position3>, &Ref<Position2>)>,
    e3: Arch<(&Ref<Position2>, &LockedRef<Position3>)>,
    e4: State<StateExample>,
    e5: Res<MarkedResources>,
) -> (
    RArch<(Ref<Position2>, LockedRef<Position3>)>,
    RArch<(Ref<Position2>, Position3, LockedRef<Position3>)>,
    RArch<(LockedRef<Position3>,)>,
    Signal,
    Reset,
) {
    let mut r1: RArch<(Ref<Position2>, LockedRef<Position3>)> = RArch::default();
    let mut r2: RArch<(Ref<Position2>, Position3, LockedRef<Position3>)> = RArch::default();
    let mut r3: RArch<((LockedRef<Position3>,))> = RArch::default();
    let mut signal = Signal::default();
    let mut reset = Reset::default();
    r1.add((
        Ref::new(Position2 { x: 1.0, y: 1.0 }),
        LockedRef::new(Position3 { x: 1.0, y: 1.0 }),
    ));
    r2.add((
        Ref::new(Position2 { x: 1.0, y: 1.0 }),
        Position3 { x: 1.0, y: 1.0 },
        LockedRef::new(Position3 { x: 1.0, y: 1.0 }),
    ));
    r3.add((LockedRef::new(Position3 { x: 1.0, y: 1.0 }),));
    signal.trigger("aa");
    reset.trigger();
    (r1, r2, r3, signal, reset)
}

main.rs

  1. It needs to have corrosive_engine_builder! macro.
  2. corrosive_engine needs to be imported from ".corrosive_engine/mod.rs".
  3. run_engine() needs to be called.

example

use crate::corrosive_engine::engine::run_engine;
use corrosive_ecs_core_macro::corrosive_engine_builder;

mod comp;
mod task;

#[path = ".corrosive_engine/mod.rs"]
mod corrosive_engine;

corrosive_engine_builder!(
    path "./src",
    setup "setup",
    setup "setup1",
    setup "setup2",
    fixed_update "fixed_task" in_group "a",
    update "update_task_signal" in_group "a" if("Signal1"&&"signal2"||"signal3"&&StateExample::A),
    long_update "long_task" before_group "a",
    update "update_task",
    sync_update "sync_task"
);

fn main() {
    run_engine()
}

build.rs

corrosive-ecs-core crate with build feature must be used as a build dependency.

example

use corrosive_ecs_core::build::general_helper::create_engine;
use std::env;
use std::process::Command;

pub fn main() {
    let current_dir = env::current_dir().expect("Failed to get current directory");
    env::set_var("CORROSIVE_APP_ROOT", &current_dir);
    create_engine();
    Command::new("cargo")
        .arg("fmt")
        .status()
        .expect("Failed to execute cargo fmt");

Project Structure for packages

Packages for this engine must be structured as the following:

  • src
    • comp
      • mod.rs
      • OTHER_FOLDERS
    • task
      • mod.rs
      • OTHER_FOLDERS
    • lib.rs
  • build.rs

Comp & Task folder

rules for these folders are the same as described in the Project Structure section.

lib.rs

It needs to have corrosive_engine_builder! macro.

build.rs

corrosive-ecs-core crate with build feature must be used as a build dependency.

example

use corrosive_ecs_core::build::general_helper::create_engine_package;
use std::env;

fn main() {
    let crate_name = env!("CARGO_PKG_NAME");
    let current_dir = env::current_dir().expect("Failed to get current directory");
    corrosive_asset_manager::asset_package::append_assets(
        current_dir.to_str().unwrap(),
        crate_name,
    )
    .expect("Could not append assets");
    create_engine_package(crate_name, current_dir.to_str().unwrap());
}

Corrosive ECS Core

Corrosive ECS Core Macros