parent
dbc2a10933
commit
743f906bc7
@ -0,0 +1,38 @@ |
|||||||
|
#include "collider.hpp" |
||||||
|
|
||||||
|
using DinkyECS::Entity; |
||||||
|
|
||||||
|
void SpatialHashTable::insert(Point pos, Entity ent) { |
||||||
|
table[pos] = ent; |
||||||
|
} |
||||||
|
|
||||||
|
void SpatialHashTable::remove(Point pos) { |
||||||
|
table.erase(pos); |
||||||
|
} |
||||||
|
|
||||||
|
void SpatialHashTable::move(Point from, Point to, Entity ent) { |
||||||
|
remove(from); |
||||||
|
insert(to, ent); |
||||||
|
} |
||||||
|
|
||||||
|
bool SpatialHashTable::occupied(Point at) { |
||||||
|
return table[at]; |
||||||
|
} |
||||||
|
|
||||||
|
std::tuple<bool, FoundList> SpatialHashTable::neighbors(Point cell) { |
||||||
|
FoundList result; |
||||||
|
|
||||||
|
// Check the current cell and its 8 neighbors
|
||||||
|
// BUG: this can sign underflow, assert it won't
|
||||||
|
for (size_t x = cell.x - 1; x <= cell.x + 1; x++) { |
||||||
|
for (size_t y = cell.y - 1; y <= cell.y + 1; y++) { |
||||||
|
Point neighborCell = {x, y}; |
||||||
|
auto it = table.find(neighborCell); |
||||||
|
if (it != table.end()) { |
||||||
|
result.insert(result.end(), it->second); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return std::tuple(!result.empty(), result); |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
#pragma once |
||||||
|
#include <vector> |
||||||
|
#include <unordered_map> |
||||||
|
#include "map.hpp" |
||||||
|
#include "dinkyecs.hpp" |
||||||
|
#include <tuple> |
||||||
|
|
||||||
|
struct PointHash { |
||||||
|
size_t operator()(const Point& p) const { |
||||||
|
return std::hash<int>()(p.x) ^ std::hash<int>()(p.y); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
typedef std::vector<DinkyECS::Entity> FoundList; |
||||||
|
|
||||||
|
class SpatialHashTable { |
||||||
|
public: |
||||||
|
SpatialHashTable() {} |
||||||
|
|
||||||
|
// disable copying, I think?
|
||||||
|
SpatialHashTable(SpatialHashTable &other) = delete; |
||||||
|
|
||||||
|
void insert(Point pos, DinkyECS::Entity obj); |
||||||
|
void move(Point from, Point to, DinkyECS::Entity ent); |
||||||
|
void remove(Point pos); |
||||||
|
bool occupied(Point pos); |
||||||
|
|
||||||
|
std::tuple<bool, FoundList> neighbors(Point position); |
||||||
|
|
||||||
|
private: |
||||||
|
std::unordered_map<Point, DinkyECS::Entity, PointHash> table; |
||||||
|
}; |
@ -0,0 +1,95 @@ |
|||||||
|
#include <catch2/catch_test_macros.hpp> |
||||||
|
#include <fmt/core.h> |
||||||
|
#include <string> |
||||||
|
#include "collider.hpp" |
||||||
|
#include "dinkyecs.hpp" |
||||||
|
|
||||||
|
using DinkyECS::Entity; |
||||||
|
using namespace fmt; |
||||||
|
|
||||||
|
TEST_CASE("confirm basic collision operations", "[collision]") { |
||||||
|
DinkyECS::World world; |
||||||
|
Entity player = world.entity(); |
||||||
|
Entity enemy = world.entity(); |
||||||
|
|
||||||
|
SpatialHashTable coltable; |
||||||
|
coltable.insert({11,11}, player); |
||||||
|
coltable.insert({21,21}, enemy); |
||||||
|
|
||||||
|
{ // not found
|
||||||
|
auto [found, nearby] = coltable.neighbors({1,1}); |
||||||
|
REQUIRE(!found); |
||||||
|
REQUIRE(nearby.empty()); |
||||||
|
} |
||||||
|
|
||||||
|
{ // found
|
||||||
|
auto [found, nearby] = coltable.neighbors({10,10}); |
||||||
|
|
||||||
|
REQUIRE(found); |
||||||
|
REQUIRE(nearby[0] == player); |
||||||
|
} |
||||||
|
|
||||||
|
{ // removed
|
||||||
|
coltable.remove({11,11}); |
||||||
|
auto [found, nearby] = coltable.neighbors({10,10}); |
||||||
|
REQUIRE(!found); |
||||||
|
REQUIRE(nearby.empty()); |
||||||
|
} |
||||||
|
|
||||||
|
coltable.insert({11,11}, player); // setup for the move test
|
||||||
|
|
||||||
|
{ // moving
|
||||||
|
coltable.move({11,11}, {12, 12}, player); |
||||||
|
auto [found, nearby] = coltable.neighbors({10,10}); |
||||||
|
REQUIRE(!found); |
||||||
|
REQUIRE(nearby.empty()); |
||||||
|
} |
||||||
|
|
||||||
|
{ // find it after move
|
||||||
|
auto [found, nearby] = coltable.neighbors({11,11}); |
||||||
|
REQUIRE(found); |
||||||
|
REQUIRE(nearby[0] == player); |
||||||
|
} |
||||||
|
|
||||||
|
{ |
||||||
|
REQUIRE(coltable.occupied({12,12})); |
||||||
|
REQUIRE(coltable.occupied({21,21})); |
||||||
|
REQUIRE(!coltable.occupied({1,10})); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("confirm multiple entities moving", "[collision]") { |
||||||
|
DinkyECS::World world; |
||||||
|
Entity player = world.entity(); |
||||||
|
Entity e1 = world.entity(); |
||||||
|
Entity e2 = world.entity(); |
||||||
|
Entity e3 = world.entity(); |
||||||
|
|
||||||
|
SpatialHashTable coltable; |
||||||
|
coltable.insert({11,11}, player); |
||||||
|
coltable.insert({10,10}, e2); |
||||||
|
coltable.insert({11,10}, e3); |
||||||
|
coltable.insert({21,21}, e1); |
||||||
|
|
||||||
|
{ // find e3 and e2
|
||||||
|
auto [found, nearby] = coltable.neighbors({11, 11}); |
||||||
|
REQUIRE(found); |
||||||
|
REQUIRE(nearby.size() == 3); |
||||||
|
// BUG: replace this with std::find/std::search
|
||||||
|
REQUIRE(nearby[0] == e2); |
||||||
|
REQUIRE(nearby[1] == e3); |
||||||
|
REQUIRE(nearby[2] == player); |
||||||
|
} |
||||||
|
|
||||||
|
coltable.move({11,11}, {20,20}, player); |
||||||
|
{ // should only find the e1
|
||||||
|
auto [found, nearby] = coltable.neighbors({20,20}); |
||||||
|
REQUIRE(found); |
||||||
|
REQUIRE(nearby.size() == 2); |
||||||
|
// BUG: replace this with std::find/std::search
|
||||||
|
REQUIRE(nearby[0] == player); |
||||||
|
REQUIRE(nearby[1] == e1); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue