Last active
July 7, 2017 17:05
-
-
Save remyroez/249361d006f50dd1972766accc8795f4 to your computer and use it in GitHub Desktop.
簡易 Entity Component System 雛形
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
#include <iostream> | |
#include <tuple> | |
#include <vector> | |
#include <unordered_map> | |
#include <string> | |
#include <cassert> | |
#include <functional> | |
#include <typeinfo> | |
#include <deque> | |
namespace ecs { | |
using entity_id = unsigned int; | |
template <typename... Args> | |
class system { | |
public: | |
using data_type = std::tuple<std::vector<Args>...>; | |
using component_initializer_type = std::tuple<Args...>; | |
using component_index_type = std::size_t; | |
using free_component_index_container = std::deque<component_index_type>; | |
using entity_map_type = std::unordered_map<entity_id, component_index_type>; | |
system() : _size(0) {} | |
template <std::size_t Index> | |
auto get_members() { | |
return std::get<Index>(data()); | |
} | |
void add_component(entity_id id, component_initializer_type &&initializer) { | |
const auto last_size = size(); | |
if (register_entity(id, last_size)) { | |
get_component_from_index(last_size) = initializer; | |
_size++; | |
} | |
} | |
void add_component(entity_id id, Args&&... args) { | |
add_component(id, std::make_tuple(std::forward<Args>(args)...)); | |
} | |
void remove_component(entity_id id) { | |
auto index = get_component_index(id); | |
push_component_index(index); | |
deregister_entity(id); | |
} | |
auto get_component(entity_id id) { | |
return get_component_from_index(get_component_index(id)); | |
} | |
const data_type &data() const { return _data; } | |
data_type &data() { return _data; } | |
std::size_t size() const { return _size; } | |
protected: | |
template <std::size_t Index, typename Tuple> | |
static decltype(auto) get_element(Tuple &tuple, component_index_type index) { | |
auto &container = std::get<Index>(tuple); | |
if (index >= container.size()) { | |
container.resize(index + 1); | |
} | |
return container[index]; | |
} | |
template <typename T, std::size_t... Indices> | |
static auto make_component_handle(T &tuple, component_index_type index, std::index_sequence<Indices...>) { | |
(void)index; | |
return std::tie(get_element<Indices>(tuple, index)...); | |
} | |
auto get_component_from_index(component_index_type index) { | |
return make_component_handle(data(), index, std::index_sequence_for<Args...>()); | |
} | |
bool register_entity(entity_id id, component_index_type index) { | |
return entity_map().emplace(id, index).second; | |
} | |
bool deregister_entity(entity_id id) { | |
return (entity_map().erase(id) > 0); | |
} | |
component_index_type get_component_index(entity_id id) const { | |
return entity_map().at(id); | |
} | |
const entity_map_type &entity_map() const { return _entity_map; } | |
entity_map_type &entity_map() { return _entity_map; } | |
component_index_type make_component_index() { | |
component_index_type index = size(); | |
if (has_free_component_index()) { | |
index = pop_component_index(); | |
} | |
return index; | |
} | |
void push_component_index(component_index_type index) { | |
_free_component_indices.push_back(index); | |
} | |
component_index_type pop_component_index() { | |
auto index = _free_component_indices.front(); | |
_free_component_indices.pop_back(); | |
return index; | |
} | |
bool has_free_component_index() const { | |
return !_free_component_indices.empty(); | |
} | |
private: | |
entity_map_type _entity_map; | |
free_component_index_container _free_component_indices; | |
data_type _data; | |
std::size_t _size; | |
}; | |
} // namespace ecs | |
void test_system() | |
{ | |
std::cout << "test_system ----------" << std::endl; | |
ecs::system<int, int, std::string> system; | |
system.add_component(100, 123, 456, "foo"); | |
system.add_component(200, 789, 741, "bar"); | |
system.add_component(300, 852, 963, "baz"); | |
auto component = system.get_component(100); | |
std::get<0>(component) = 173; | |
std::cout | |
<< std::get<0>(component) << ", " | |
<< std::get<1>(component) << ", " | |
<< std::get<2>(component) | |
<< std::endl; | |
std::cout << std::endl; | |
for (auto a : system.get_members<2>()) { | |
std::cout << a << std::endl; | |
} | |
std::cout << std::endl; | |
} | |
void test_empty_system() | |
{ | |
std::cout << "test_empty_system ----------" << std::endl; | |
ecs::system<> system; | |
system.add_component(123); | |
auto component = system.get_component(123); | |
std::cout << typeid(component).name() << std::endl; | |
std::cout << std::endl; | |
} | |
void test_component() | |
{ | |
std::cout << "test_component ----------" << std::endl; | |
struct position { | |
using target = std::tuple<float&, float&>; | |
constexpr static decltype(auto) x(target &tuple) { return std::get<0>(tuple); } | |
constexpr static decltype(auto) y(target &tuple) { return std::get<1>(tuple); } | |
}; | |
ecs::system<float, float> system; | |
system.add_component(100, 123.456f, 456.0f); | |
auto component = system.get_component(100); | |
position::x(component) = 789.123f; | |
std::cout << position::x(component) << std::endl; | |
std::cout << std::endl; | |
} | |
int main() | |
{ | |
test_system(); | |
test_empty_system(); | |
test_component(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment