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