Compare commits

...

2 commits

Author SHA1 Message Date
3aaa4557c6 Use std::invocable<T&...> 2025-02-18 22:44:21 +03:00
006d9fc95e Custom concept for components 2025-02-18 22:27:54 +03:00
2 changed files with 63 additions and 45 deletions

View file

@ -1,5 +1,4 @@
#include <catch2/catch_test_macros.hpp>
#include <string>
#define CATCH_CONFIG_MAIN
#include <catch2/catch_all.hpp>
@ -74,9 +73,9 @@ TEST_CASE("Attach a simple component to an entity and verify it is correctly "
REQUIRE(w.has<ChoosenOne>(e2));
}
struct Name
struct Comp
{
std::string value;
int v = 0;
};
TEST_CASE("Retrieve a component from an entity and verify its data matches "
@ -85,13 +84,13 @@ TEST_CASE("Retrieve a component from an entity and verify its data matches "
world w;
auto e = w.make_entity();
w.set(e, Name{"zecsy!"});
w.set(e, Comp());
REQUIRE(w.get<Name>(e).value == "zecsy!");
REQUIRE(w.get<Comp>(e).v == 0);
w.get<Name>(e).value = "super-zecsy!";
w.get<Comp>(e).v = 77;
REQUIRE(w.get<Name>(e).value == "super-zecsy!");
REQUIRE(w.get<Comp>(e).v == 77);
}
TEST_CASE(
@ -161,15 +160,15 @@ TEST_CASE("Attach multiple components to an entity and verify all are "
world w;
auto e = w.make_entity();
w.set(e, ChoosenOne{}, Name{"zecsy"});
w.set(e, ChoosenOne{}, Comp{});
REQUIRE(w.has<ChoosenOne, Name>(e));
REQUIRE(w.has<ChoosenOne, Comp>(e));
w.remove<ChoosenOne, Name>(e);
w.remove<ChoosenOne, Comp>(e);
REQUIRE_FALSE(w.has<ChoosenOne, Name>(e));
REQUIRE_FALSE(w.has<ChoosenOne, Comp>(e));
REQUIRE_FALSE(w.has<ChoosenOne>(e));
REQUIRE_FALSE(w.has<Name>(e));
REQUIRE_FALSE(w.has<Comp>(e));
}
TEST_CASE("Create a simple system that processes entities with a specific "

View file

@ -1,11 +1,13 @@
#pragma once
#include <algorithm>
#include <concepts>
#include <cstdint>
#include <cstdlib>
#include <format>
#include <functional>
#include <set>
#include <stdexcept>
#include <type_traits>
#include <unordered_map>
#include <vector>
@ -13,6 +15,23 @@ namespace zecsy
{
using entity_id = uint64_t;
/*
* Actually, not a pure POD. Gotta figure out a better name
*/
template<typename... T>
concept Component = []
{
static_assert((std::is_default_constructible_v<T> && ...),
"Should have a default constructor");
static_assert((std::is_trivially_copyable_v<T> && ...),
"Should be trivially copyable");
static_assert((std::is_trivially_destructible_v<T> && ...),
"Should be trivially destructible");
static_assert((std::is_standard_layout_v<T> && ...),
"Should have standard layout");
return true;
}();
/*
* Using std::set for entities_set to maintain sorted order, which can
* improve cache locality during queries and iterations.
@ -22,32 +41,32 @@ namespace zecsy
class component_storage final
{
public:
template<typename T>
template<Component T>
bool has(entity_id e) const;
template<typename First, typename Second, typename... Rest>
template<Component First, Component Second, Component... Rest>
bool has(entity_id e) const;
template<typename T>
template<Component T>
T& get(entity_id e);
template<typename T>
template<Component T>
void set(entity_id e);
template<typename T>
template<Component T>
void set(entity_id e, const T& comp);
template<typename First, typename Second, typename... Rest>
template<Component First, Component Second, Component... Rest>
void set(entity_id e);
template<typename First, typename Second, typename... Rest>
template<Component First, Component Second, Component... Rest>
void set(entity_id e, const First& comp0, const Second& comp1,
const Rest&... rest_comps);
template<typename T>
template<Component T>
void remove(entity_id e);
template<typename First, typename Second, typename... Rest>
template<Component First, Component Second, Component... Rest>
void remove(entity_id e);
private:
@ -75,7 +94,7 @@ namespace zecsy
inline component_storage::comp_id component_storage::next_component_id = 0;
template<typename T>
template<Component T>
inline bool component_storage::has(entity_id e) const
{
auto id = get_component_id<T>();
@ -86,7 +105,7 @@ namespace zecsy
return false;
}
template<typename T>
template<Component T>
inline T& component_storage::get(entity_id e)
{
auto id = get_component_id<T>();
@ -101,7 +120,7 @@ namespace zecsy
return *reinterpret_cast<T*>(&pool.data[index * sizeof(T)]);
}
template<typename T>
template<Component T>
inline void component_storage::set(entity_id e, const T& comp)
{
auto id = get_component_id<T>();
@ -124,13 +143,13 @@ namespace zecsy
pool.index_to_entity[index] = e;
}
template<typename T>
template<Component T>
inline void component_storage::set(entity_id e)
{
set(e, T{});
}
template<typename T>
template<Component T>
inline void component_storage::remove(entity_id e)
{
auto id = get_component_id<T>();
@ -153,13 +172,13 @@ namespace zecsy
pool.index_to_entity.erase(index);
}
template<typename First, typename Second, typename... Rest>
template<Component First, Component Second, Component... Rest>
inline bool component_storage::has(entity_id e) const
{
return has<First>(e) && has<Second>(e) && (has<Rest>(e) && ...);
}
template<typename First, typename Second, typename... Rest>
template<Component First, Component Second, Component... Rest>
inline void component_storage::set(entity_id e)
{
set(e, First{});
@ -167,7 +186,7 @@ namespace zecsy
(set(e, Rest{}), ...);
}
template<typename First, typename Second, typename... Rest>
template<Component First, Component Second, Component... Rest>
inline void component_storage::set(entity_id e, const First& comp0,
const Second& comp1,
const Rest&... rest_comps)
@ -177,7 +196,7 @@ namespace zecsy
(set(e, rest_comps), ...);
}
template<typename First, typename Second, typename... Rest>
template<Component First, Component Second, Component... Rest>
inline void component_storage::remove(entity_id e)
{
remove<First>(e);
@ -238,23 +257,23 @@ namespace zecsy
void destroy_entity(entity_id e);
bool is_alive(entity_id e) const;
template<typename... T>
template<Component... T>
bool has(entity_id e) const;
template<typename T>
template<Component T>
T& get(entity_id e);
template<typename... T>
template<Component... T>
void set(entity_id e);
template<typename... T>
template<Component... T>
void set(entity_id e, const T&... comps);
template<typename... T>
template<Component... T>
void remove(entity_id e);
template<typename... T>
void query(auto&& system);
template<Component... T>
void query(std::invocable<T&...> auto&& system);
private:
entities_set alive_entities;
@ -279,38 +298,38 @@ namespace zecsy
return alive_entities.contains(e);
}
template<typename... T>
template<Component... T>
inline bool world::has(entity_id e) const
{
return storage.has<T...>(e);
}
template<typename T>
template<Component T>
inline T& world::get(entity_id e)
{
return storage.get<T>(e);
}
template<typename... T>
template<Component... T>
inline void world::set(entity_id e)
{
storage.set<T...>(e);
}
template<typename... T>
template<Component... T>
inline void world::set(entity_id e, const T&... comps)
{
storage.set(e, comps...);
}
template<typename... T>
template<Component... T>
inline void world::remove(entity_id e)
{
storage.remove<T...>(e);
}
template<typename... T>
inline void world::query(auto&& system)
template<Component... T>
inline void world::query(std::invocable<T&...> auto&& system)
{
for(auto e: alive_entities)
{