Skip to content

Instantly share code, notes, and snippets.

@Midiman
Forked from r-lyeh-archived/compo.cpp
Created November 18, 2013 05:53
Show Gist options
  • Save Midiman/7523144 to your computer and use it in GitHub Desktop.
Save Midiman/7523144 to your computer and use it in GitHub Desktop.
#include <map> // Component-entity system in 24 lines of C++11. 2013 rlyeh, MIT licensed.
#include <set> // Code fragment from kult engine - https://github.com/r-lyeh/kult
enum SUBSYSTEM_MODE { JOIN = 0, MERGE = 1, EXCLUDE = 2 };
template<typename T> std::set<unsigned> &system()
{ static std::set<unsigned> entities; return entities; }
template<typename T, int MODE> std::set<unsigned> subsystem( const std::set<unsigned> &B )
{ std::set<unsigned> newset; const std::set<unsigned> &A = system<T>(); // union first,
/**/ if (MODE == MERGE) { newset = B; for( auto &id : A ) newset.insert(id); } // then difference,
else if (MODE == EXCLUDE) { newset = B; for( auto &id : A ) newset.erase (id); } // then intersection
else if(A.size() < B.size()) { for( auto &id : A ) if(B.find(id) != B.end()) newset.insert( id ); }
else { for( auto &id : B ) if(A.find(id) != A.end()) newset.insert( id ); }
return newset; }
template<typename T> std::map< unsigned, T > &components()
{ static std::map< unsigned, T > objects; return objects; }
template<typename T> bool has( unsigned id )
{ return components<T>().find( id ) != components<T>().end(); }
template<typename T> decltype(T::value_type) &get( unsigned id )
{ static decltype(T::value_type) invalid, reset;
return has<T>(id) ? components<T>()[id].value_type : invalid = reset; }
template<typename T> decltype(T::value_type) &add( unsigned id )
{ return system<T>().insert( id ), components<T>()[id] = components<T>()[id], get<T>(id); }
template<typename T> bool del( unsigned id )
{ return add<T>(id), components<T>().erase( id ), system<T>().erase( id ), !has<T>( id ); }
template<typename T, int NAME> struct component { T value_type; };
// example:
#include <cassert>
#include <string>
#include <iostream>
// gamedev types and constants
typedef std::pair<int, int> vec2i;
typedef std::pair<float,float> vec2f;
const vec2f zero = { 0.f, 0.f }, one = { 1.f, 1.f };
// component aliases
using friendly = component< bool, 'team' >;
using health = component< int, 'heal' >;
using mana = component< int, 'mana' >;
using coins = component< int, 'coin' >;
using name = component< std::string, 'name' >;
using position = component< vec2f, 'pos2' >;
// sugars
template<class T, class U> std::set< unsigned > join() { return subsystem<T,JOIN>( system<U>() ); }
template<class T, class U, class V> std::set< unsigned > join() { return subsystem<T,JOIN>( join<U,V>() ); }
template<class T, class U, class V, class W> std::set< unsigned > join() { return subsystem<T,JOIN>( join<U,V,W>() ); }
template<class T> std::set< unsigned > exclude( const std::set<unsigned> &B ) { return subsystem<T,EXCLUDE>(B); }
int main()
{
// entities
int none = 0, player = 1, enemy = 2;
// components
assert( !has<name>(player) );
assert( !has<position>(player) );
assert( !has<coins>(enemy) );
assert( !has<health>(enemy) );
add<name>(player) = "Hero";
add<position>(player) = zero;
add<health>(player) = 100;
add<coins>(player) = 200;
add<mana>(player) = 4000;
add<friendly>(player) = true;
add<name>(enemy) = "Orc";
add<position>(enemy) = one;
add<health>(enemy) = 200;
add<coins>(enemy) = 50;
add<mana>(enemy) = 10;
assert( get<health>(player) == 100 ); // :>
assert( has<name>(player) );
assert( !has<vec2i>(player) );
assert( has<position>(player) );
assert( has<health>(player) );
assert( get<name>(player) == "Hero" );
assert( get<position>(player) == zero );
assert( get<health>(player) == 100 );
// systems; here we intersect a system of all elements with <name> and <position>.
assert( (join<name, position>().size() == 2) );
// systems; render game state
auto display = []() {
std::cout << "- ";
for( auto &id : join<name, coins, health, position>() ) {
std::cout
<< get<name>(id) << " at "
<< "(" << get<position>(id).first << "," << get<position>(id).second << ")"
<< " " << get<health>(id) << "HP"
<< " " << get<coins>(id) << "$, ";
}
std::cout << std::endl;
};
display();
// systems; simulate movement
for( auto &id : join<name, position>() ) {
std::cout << get<name>(id) << " says: im moving!" << std::endl;
vec2f &pos = get<position>(id);
pos.first += 10;
pos.second ++;
}
// systems; simulate a spell bomb in entities of any type
for( auto &id : system<mana>() ) {
std::cout << "spellboomb!!!" << std::endl;
get<mana>(id) -= 50;
}
// systems; simulate a powerup (+$100) for all players
for( auto &id : join<name, coins, friendly>() ) {
get<coins>(id) += 100;
std::cout << get<name>(id) << " says: money! :)" << std::endl;
}
// systems; simulate a poison (-50%HP) to all entities that are not friendly (so enemies)
for( auto &id : exclude<friendly>( join<name, health>() ) ) {
get<health>(id) *= 0.5;
std::cout << get<name>(id) << " says: ugh! poisoned :(" << std::endl;
}
display();
assert( get<health>(player) == 100+0 );
assert( get<health>(enemy) == 200/2 );
assert( get<coins>(player) == 200+100 );
assert( get<coins>(enemy) == 50+0 );
assert( get<mana>(player) == 4000-50 );
assert( get<mana>(enemy) == 10-50 );
assert( del<position>(player) );
assert( !has<position>(player) );
assert( del<name>(player) );
assert( !has<name>(player) );
assert( (join<name, position>().size() == 1) );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment