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
- To create components, resources, states, and traits the following rules must be applied, or the engine won't detect them.
- 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.
- Use
component
,state
, andresource
to mark structs and enums to be used by the engine. - Use the
trait_bound
attribute macro to mark traits. - Use the
trait_for
macro to assign a component to a trait. - 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
- 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.
- Use
task
to mark functions to be used by the engine. - Should tasks need to export something, they must be inside a tuple.
- Use the
arch_types
macro to mark the arch types to be used by the engine. - Should tasks need to export something, they must be inside a tuple.
- Tasks can only have
Res<T>
,State<T>
,Hierarchy<T>
,Arch<(&T1,&T2,...).
andDeltaTime
types as input andRArch<(&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
- It needs to have
corrosive_engine_builder!
macro. corrosive_engine
needs to be imported from".corrosive_engine/mod.rs"
.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", ¤t_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
- comp
- 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());
}