Created
July 27, 2022 13:02
-
-
Save killercup/b99a8dade4d396c08855d2dcff3cc10c to your computer and use it in GitHub Desktop.
Rebuilding Bevy ECS -- queries
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![allow(dead_code)] | |
// ------------------ | |
// The game code | |
// ------------------ | |
fn main() { | |
let mut app = App::new() | |
.add_system(example_system) | |
.add_system(another_example_system) | |
.add_system(complex_example_system); | |
app.world.add_entity().add(Position { x: 1., y: 4.2 }); | |
app.run(); | |
} | |
fn example_system() { | |
println!("foo"); | |
} | |
fn another_example_system(q: Query<Position>) { | |
println!("bar -- query: {q:?}"); | |
} | |
// TODO: Fix tuple query | |
fn complex_example_system(q: Query<(Position, ())>, _r: ()) { | |
println!("baz -- query {q:?}"); | |
} | |
// ------------------ | |
// App boilerplate | |
// ------------------ | |
#[derive(Default)] | |
struct App { | |
systems: Vec<Box<dyn System>>, | |
world: World, | |
} | |
impl App { | |
fn new() -> App { | |
App::default() | |
} | |
fn add_system<F: IntoSystem<Params>, Params: SystemParam>(mut self, function: F) -> Self { | |
self.systems.push(Box::new(function.into_system())); | |
self | |
} | |
fn run(&mut self) { | |
for system in &mut self.systems { | |
system.run(&self.world); | |
} | |
} | |
} | |
use std::{ | |
any::{Any, TypeId}, | |
collections::HashMap, | |
}; | |
/// Terrible entity component container | |
#[derive(Default)] | |
struct World(HashMap<Entity, HashMap<TypeId, Box<dyn Any>>>); | |
#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)] | |
struct Entity(u32); | |
impl World { | |
fn add_entity<'world>(&'world mut self) -> EntityHandle<'world> { | |
let max_id = self.0.keys().map(|x| x.0).max().unwrap_or(1); | |
let entity = Entity(max_id); | |
self.0.insert(entity, HashMap::default()); | |
EntityHandle { | |
world: self, | |
entity, | |
} | |
} | |
fn query<'world, T: Any>(&'world self) -> impl Iterator<Item = &'world T> { | |
let id = TypeId::of::<T>(); | |
eprintln!( | |
"querying for {} with typeId {id:?}", | |
std::any::type_name::<T>() | |
); | |
self.0 | |
.values() | |
.flat_map(|x| x.get(&TypeId::of::<T>())) | |
.filter_map(|x| x.downcast_ref::<T>()) | |
} | |
} | |
struct EntityHandle<'world> { | |
world: &'world mut World, | |
entity: Entity, | |
} | |
impl<'world> EntityHandle<'world> { | |
fn add<T: Any>(&'world mut self, component: T) { | |
let component: Box<dyn Any> = Box::new(component); | |
let id = (&*component).type_id(); | |
eprintln!( | |
"inserting {} with typeId {id:?}", | |
std::any::type_name::<T>() | |
); | |
self.world | |
.0 | |
.get_mut(&self.entity) | |
.unwrap() | |
.insert(id, component); | |
} | |
} | |
/// Use this to fetch entities | |
#[derive(Debug, Clone)] | |
struct Query<T> { | |
output: Vec<T>, | |
} | |
/// The position of an entity in 2D space | |
#[derive(Debug, Clone)] | |
struct Position { | |
x: f32, | |
y: f32, | |
} | |
// ------------------ | |
// Systems magic | |
// ------------------ | |
use core::marker::PhantomData; | |
/// This is what we store | |
trait System: 'static { | |
fn run(&mut self, world: &World); | |
} | |
/// Convert thing to system (to create a trait object) | |
trait IntoSystem<Params> { | |
type System: System; | |
fn into_system(self) -> Self::System; | |
} | |
/// Convert any function with only system params into a system | |
impl<F, Params: SystemParam> IntoSystem<Params> for F | |
where | |
F: SystemParamFunction<Params>, | |
{ | |
type System = FunctionSystem<F, Params>; | |
fn into_system(self) -> Self::System { | |
FunctionSystem { | |
system: self, | |
params: PhantomData, | |
} | |
} | |
} | |
/// Represent a system with its params | |
// | |
// TODO: do stuff with params | |
struct FunctionSystem<F: 'static, Params: SystemParam> { | |
system: F, | |
params: PhantomData<Params>, | |
} | |
/// Make our wrapper be a System | |
impl<F, Params: SystemParam> System for FunctionSystem<F, Params> | |
where | |
F: SystemParamFunction<Params>, | |
{ | |
fn run(&mut self, world: &World) { | |
SystemParamFunction::run(&mut self.system, world); | |
} | |
} | |
/// Function with only system params | |
trait SystemParamFunction<Params: SystemParam>: 'static { | |
fn run(&mut self, world: &World); | |
} | |
/// unit function | |
impl<F> SystemParamFunction<()> for F | |
where | |
F: Fn() -> () + 'static, | |
{ | |
fn run(&mut self, _: &World) { | |
eprintln!("calling a function with no params"); | |
self(); | |
} | |
} | |
/// one param function | |
impl<F, P1: SystemParam> SystemParamFunction<(P1,)> for F | |
where | |
F: Fn(P1) -> () + 'static, | |
{ | |
fn run(&mut self, world: &World) { | |
eprintln!("calling a function"); | |
eprintln!("params:"); | |
<P1 as SystemParam>::debug(); | |
let p1 = <P1 as SystemParam>::fetch(world); | |
self(p1); | |
} | |
} | |
/// two param function | |
impl<F, P1: SystemParam, P2: SystemParam> SystemParamFunction<(P1, P2)> for F | |
where | |
F: Fn(P1, P2) -> () + 'static, | |
{ | |
fn run(&mut self, world: &World) { | |
eprintln!("calling a function"); | |
eprintln!("params:"); | |
<P1 as SystemParam>::debug(); | |
<P2 as SystemParam>::debug(); | |
let p1 = <P1 as SystemParam>::fetch(world); | |
let p2 = <P2 as SystemParam>::fetch(world); | |
self(p1, p2); | |
} | |
} | |
// exercise for reader: macro to make this for other functions | |
/// Marker trait for parameters of a System function | |
trait SystemParam: 'static { | |
fn debug(); | |
fn fetch(world: &World) -> Self; | |
} | |
/// Query is a good param | |
impl<T: Clone + 'static> SystemParam for Query<T> { | |
fn debug() { | |
eprintln!("Query") | |
} | |
fn fetch(world: &World) -> Self { | |
Query { | |
output: world.query::<T>().cloned().collect(), | |
} | |
} | |
} | |
impl SystemParam for () { | |
fn debug() { | |
eprintln!("unit") | |
} | |
fn fetch(_: &World) -> Self { | |
() | |
} | |
} | |
impl<T1> SystemParam for (T1,) | |
where | |
T1: SystemParam, | |
{ | |
fn debug() { | |
eprintln!("Tuple("); | |
<T1 as SystemParam>::debug(); | |
eprintln!(")"); | |
} | |
fn fetch(world: &World) -> Self { | |
(<T1 as SystemParam>::fetch(world),) | |
} | |
} | |
impl<T1, T2> SystemParam for (T1, T2) | |
where | |
T1: SystemParam, | |
T2: SystemParam, | |
{ | |
fn debug() { | |
eprintln!("Tuple("); | |
<T1 as SystemParam>::debug(); | |
eprintln!(", "); | |
<T2 as SystemParam>::debug(); | |
eprintln!(")"); | |
} | |
fn fetch(world: &World) -> Self { | |
( | |
<T1 as SystemParam>::fetch(world), | |
<T2 as SystemParam>::fetch(world), | |
) | |
} | |
} | |
// exercise for reader: macro to make this for other tuples | |
// exercise for highly-ambitious reader: 1 macro for both the other macros |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment