parent
d798d154ae
commit
96efc990c1
@ -0,0 +1,11 @@ |
||||
#include "dinkyecs.hpp" |
||||
|
||||
namespace DinkyECS { |
||||
void configure(World& world, const ComponentMap& component_map, Entity ent, json& data) { |
||||
for (auto &i : data) { |
||||
assert(i.contains("_type") && i["_type"].is_string()); |
||||
assert(component_map.contains(i["_type"])); |
||||
component_map.at(i["_type"])(world, ent, i); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,250 @@ |
||||
#include <catch2/catch_test_macros.hpp> |
||||
#include "dinkyecs.hpp" |
||||
#include <iostream> |
||||
#include <fmt/core.h> |
||||
#include "point.hpp" |
||||
|
||||
using namespace fmt; |
||||
using DinkyECS::Entity; |
||||
using std::string; |
||||
|
||||
struct Player { |
||||
string name; |
||||
Entity eid; |
||||
}; |
||||
|
||||
struct Position { |
||||
Point location; |
||||
}; |
||||
|
||||
DINKY_HAS_COMPONENT(Point, x, y); |
||||
DINKY_HAS_COMPONENT(Position, location); |
||||
|
||||
struct Motion { |
||||
int dx; |
||||
int dy; |
||||
bool random=false; |
||||
}; |
||||
|
||||
DINKY_HAS_COMPONENT(Motion, dx, dy, random); |
||||
|
||||
struct Velocity { |
||||
double x, y; |
||||
}; |
||||
|
||||
DINKY_HAS_COMPONENT(Velocity, x, y); |
||||
|
||||
struct Gravity { |
||||
double level; |
||||
}; |
||||
|
||||
DINKY_HAS_COMPONENT(Gravity, level); |
||||
|
||||
struct DaGUI { |
||||
int event; |
||||
}; |
||||
|
||||
DINKY_HAS_COMPONENT(DaGUI, event); |
||||
|
||||
/*
|
||||
* Using a function catches instances where I'm not copying |
||||
* the data into the world. |
||||
*/ |
||||
void configure(DinkyECS::World &world, Entity &test) { |
||||
println("---Configuring the base system."); |
||||
Entity test2 = world.entity(); |
||||
|
||||
world.set<Position>(test, {10,20}); |
||||
world.set<Velocity>(test, {1,2}); |
||||
|
||||
world.set<Position>(test2, {1,1}); |
||||
world.set<Velocity>(test2, {9,19}); |
||||
|
||||
println("---- Setting up the player as a fact in the system."); |
||||
|
||||
auto player_eid = world.entity(); |
||||
Player player_info{"Zed", player_eid}; |
||||
// just set some player info as a fact with the entity id
|
||||
world.set_the<Player>(player_info); |
||||
|
||||
world.set<Velocity>(player_eid, {0,0}); |
||||
world.set<Position>(player_eid, {0,0}); |
||||
|
||||
auto enemy = world.entity(); |
||||
world.set<Velocity>(enemy, {0,0}); |
||||
world.set<Position>(enemy, {0,0}); |
||||
|
||||
println("--- Creating facts (singletons)"); |
||||
world.set_the<Gravity>({0.9}); |
||||
} |
||||
|
||||
TEST_CASE("confirm ECS system works", "[ecs]") { |
||||
DinkyECS::World world; |
||||
Entity test = world.entity(); |
||||
|
||||
configure(world, test); |
||||
|
||||
Position &pos = world.get<Position>(test); |
||||
REQUIRE(pos.location.x == 10); |
||||
REQUIRE(pos.location.y == 20); |
||||
|
||||
Velocity &vel = world.get<Velocity>(test); |
||||
REQUIRE(vel.x == 1); |
||||
REQUIRE(vel.y == 2); |
||||
|
||||
world.query<Position>([](const auto &ent, auto &pos) { |
||||
REQUIRE(ent > 0); |
||||
REQUIRE(pos.location.x >= 0); |
||||
REQUIRE(pos.location.y >= 0); |
||||
}); |
||||
|
||||
world.query<Velocity>([](const auto &ent, auto &vel) { |
||||
REQUIRE(ent > 0); |
||||
REQUIRE(vel.x >= 0); |
||||
REQUIRE(vel.y >= 0); |
||||
}); |
||||
|
||||
println("--- Manually get the velocity in position system:"); |
||||
world.query<Position>([&](const auto &ent, auto &pos) { |
||||
Velocity &vel = world.get<Velocity>(ent); |
||||
|
||||
REQUIRE(ent > 0); |
||||
REQUIRE(pos.location.x >= 0); |
||||
REQUIRE(pos.location.y >= 0); |
||||
REQUIRE(ent > 0); |
||||
REQUIRE(vel.x >= 0); |
||||
REQUIRE(vel.y >= 0); |
||||
}); |
||||
|
||||
println("--- Query only entities with Position and Velocity:"); |
||||
world.query<Position, Velocity>([&](const auto &ent, auto &pos, auto &vel) { |
||||
Gravity &grav = world.get_the<Gravity>(); |
||||
REQUIRE(grav.level <= 1.0f); |
||||
REQUIRE(grav.level > 0.5f); |
||||
REQUIRE(ent > 0); |
||||
REQUIRE(pos.location.x >= 0); |
||||
REQUIRE(pos.location.y >= 0); |
||||
REQUIRE(ent > 0); |
||||
REQUIRE(vel.x >= 0); |
||||
REQUIRE(vel.y >= 0); |
||||
}); |
||||
|
||||
// now remove Velocity
|
||||
REQUIRE(world.has<Velocity>(test)); |
||||
world.remove<Velocity>(test); |
||||
REQUIRE_THROWS(world.get<Velocity>(test)); |
||||
REQUIRE(!world.has<Velocity>(test)); |
||||
|
||||
println("--- After remove test, should only result in test2:"); |
||||
world.query<Position, Velocity>([&](const auto &ent, auto &pos, auto &vel) { |
||||
auto &in_position = world.get<Position>(ent); |
||||
auto &in_velocity = world.get<Velocity>(ent); |
||||
REQUIRE(pos.location.x >= 0); |
||||
REQUIRE(pos.location.y >= 0); |
||||
REQUIRE(in_position.location.x == pos.location.x); |
||||
REQUIRE(in_position.location.y == pos.location.y); |
||||
REQUIRE(in_velocity.x == vel.x); |
||||
REQUIRE(in_velocity.y == vel.y); |
||||
}); |
||||
} |
||||
|
||||
enum GUIEvent { |
||||
HIT, MISS |
||||
}; |
||||
|
||||
TEST_CASE("confirm that the event system works", "[ecs]") { |
||||
DinkyECS::World world; |
||||
DinkyECS::Entity player = world.entity(); |
||||
|
||||
world.send<GUIEvent>(GUIEvent::HIT, player, string{"hello"}); |
||||
|
||||
bool ready = world.has_event<GUIEvent>(); |
||||
REQUIRE(ready == true); |
||||
|
||||
auto [event, entity, data] = world.recv<GUIEvent>(); |
||||
REQUIRE(event == GUIEvent::HIT); |
||||
REQUIRE(entity == player); |
||||
auto &str_data = std::any_cast<string&>(data); |
||||
REQUIRE(string{"hello"} == str_data); |
||||
|
||||
ready = world.has_event<GUIEvent>(); |
||||
REQUIRE(ready == false); |
||||
} |
||||
|
||||
|
||||
TEST_CASE("confirm copying and constants", "[ecs-constants]") { |
||||
DinkyECS::World world1; |
||||
|
||||
Player player_info{"Zed", world1.entity()}; |
||||
world1.set_the<Player>(player_info); |
||||
|
||||
world1.set<Position>(player_info.eid, {10,10}); |
||||
world1.make_constant(player_info.eid); |
||||
|
||||
DinkyECS::World world2; |
||||
world1.clone_into(world2); |
||||
|
||||
auto &test1 = world1.get<Position>(player_info.eid); |
||||
auto &test2 = world2.get<Position>(player_info.eid); |
||||
|
||||
REQUIRE(test2.location.x == test1.location.x); |
||||
REQUIRE(test2.location.y == test1.location.y); |
||||
|
||||
// check for accidental reference
|
||||
test1.location.x = 100; |
||||
REQUIRE(test2.location.x != test1.location.x); |
||||
|
||||
// test the facts copy over
|
||||
auto &player2 = world2.get_the<Player>(); |
||||
REQUIRE(player2.eid == player_info.eid); |
||||
} |
||||
|
||||
|
||||
TEST_CASE("test serialization with nlohmann::json", "[ecs-serialize]") { |
||||
DinkyECS::ComponentMap component_map; |
||||
DinkyECS::Component<Position>(component_map); |
||||
DinkyECS::Component<Velocity>(component_map); |
||||
DinkyECS::Component<Motion>(component_map); |
||||
DinkyECS::Component<Gravity>(component_map); |
||||
DinkyECS::Component<DaGUI>(component_map); |
||||
|
||||
auto data = R"( |
||||
[ |
||||
{ |
||||
"_type": "Position", |
||||
"location": { |
||||
"x": 10, |
||||
"y": 5 |
||||
} |
||||
}, |
||||
{ |
||||
"_type": "Motion", |
||||
"dx": 0, |
||||
"dy": 1 |
||||
}, |
||||
{ |
||||
"_type": "Velocity", |
||||
"x": 0.1, |
||||
"y": 10.2 |
||||
} |
||||
] |
||||
)"_json; |
||||
|
||||
DinkyECS::World world; |
||||
DinkyECS::Entity ent1 = world.entity(); |
||||
DinkyECS::Entity ent2 = world.entity(); |
||||
|
||||
DinkyECS::configure(world, component_map, ent1, data); |
||||
DinkyECS::configure(world, component_map, ent2, data); |
||||
|
||||
world.query<Position, Motion>([&](const auto ent, auto &pos, auto &motion) { |
||||
fmt::println("entity: {}; position={},{} and motion={},{} motion.random={}", |
||||
ent, pos.location.x, pos.location.y, |
||||
motion.dx, motion.dy, motion.random); |
||||
REQUIRE(pos.location.x == 10); |
||||
REQUIRE(pos.location.y == 5); |
||||
REQUIRE(motion.dx == 0); |
||||
REQUIRE(motion.dy == 1); |
||||
REQUIRE(motion.random == false); |
||||
}); |
||||
} |
Loading…
Reference in new issue