Skip to content

Instantly share code, notes, and snippets.

@krdln
Last active August 29, 2015 14:22
Show Gist options
  • Save krdln/57ebc1f1940cdd434646 to your computer and use it in GitHub Desktop.
Save krdln/57ebc1f1940cdd434646 to your computer and use it in GitHub Desktop.
ECS with tuple trait magic
use std::collections::BTreeMap;
extern crate typemap;
use typemap::{TypeMap, Key};
/// Keys for my ECS
trait MyKey: Key where <Self as Key>::Value: Clone {}
struct Ecs {
// just an example layout. totally not cache-friendly
entities: BTreeMap<usize, TypeMap>
}
/// Trait describing types, which Ecs::collect can be parametrized over
trait CollectArg {
type Ret;
fn filter(tm: &TypeMap) -> Option<Self::Ret>;
}
// impl<T: Key> would collide with impl for tuples
impl<T: MyKey> CollectArg for T
where <T as Key>::Value: Clone // (why do I have to repeat this bound here?)
{
type Ret = T::Value;
fn filter(tm: &TypeMap) -> Option<Self::Ret> {
tm.get::<T>().cloned()
}
}
macro_rules! otry{ ($e:expr) => ( if let Some(x) = $e { x } else { return None } ) }
impl<T: CollectArg, U: CollectArg> CollectArg for (T, U) {
type Ret = (T::Ret, U::Ret);
fn filter(tm: &TypeMap) -> Option<Self::Ret> {
Some( ( otry!(T::filter(tm)), otry!(U::filter(tm)) ) )
}
}
impl<T: CollectArg, U: CollectArg, V: CollectArg> CollectArg for (T, U, V) {
type Ret = (T::Ret, U::Ret, V::Ret);
fn filter(tm: &TypeMap) -> Option<Self::Ret> {
Some( ( otry!(T::filter(tm)), otry!(U::filter(tm)), otry!(V::filter(tm)) ) )
}
}
// you can add impls for longer tuples, or just nest them
// this is just addition i've added for fun
struct Exclude<T>(T);
impl<T: MyKey> CollectArg for Exclude<T> {
type Ret = ();
fn filter(tm: &TypeMap) -> Option<()> {
if let Some(_) = tm.get::<T>() { None }
else { Some(()) }
}
}
impl Ecs {
fn new() -> Ecs { Ecs { entities: BTreeMap::new() } }
fn set<T: MyKey>(&mut self, id: usize, val: T::Value) {
self.entities.entry(id).or_insert_with(||TypeMap::new()).insert::<T>(val);
}
fn collect<What: CollectArg>(&self) -> Vec<(usize, What::Ret)> {
self.entities.iter().filter_map( |(&i,tm)| What::filter(tm).map(|x|(i,x)) ).collect()
}
}
// 8< -------------------------------------------------------------------------------------- >8
// example:
struct Position;
impl Key for Position { type Value = P; }
impl MyKey for Position {}
struct Velocity;
impl Key for Velocity { type Value = P; }
impl MyKey for Velocity {}
struct Name;
impl Key for Name { type Value = &'static str; }
impl MyKey for Name {}
#[derive(Debug, Clone)]
struct P(i32, i32);
fn main() {
let mut e = Ecs::new();
e.set::<Name>(0, "background");
e.set::<Name>(1, "car");
e.set::<Position>(1, P(25, 2));
e.set::<Velocity>(1, P(10, 0));
e.set::<Name>(2, "raindrop");
e.set::<Position>(2, P(35, 10));
e.set::<Velocity>(2, P(0, -5));
e.set::<Name>(3, "house");
e.set::<Position>(2, P(0, 0));
println!("\none argument");
for (i, name) in e.collect::<Name>() {
println!(" {}: {}", i, name);
}
println!("\nmany arguments");
for (i, (name, x, v)) in e.collect::<(Name, Position, Velocity)>() {
println!(" {}: {} {:?} {:?}", i, name, x, v);
}
println!("\ntuple nesting");
for (i, (name, (x, v))) in e.collect::<(Name, (Position, Velocity))>() {
println!(" {}: {} {:?} {:?}", i, name, x, v);
}
println!("\nmore magic");
for (i, (name, _)) in e.collect::<(Name, Exclude<Velocity>)>() {
println!(" {}: {} can't move", i, name);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment