Implemented a simple collision hash table.
This commit is contained in:
parent
dbc2a10933
commit
743f906bc7
7 changed files with 174 additions and 1 deletions
38
collider.cpp
Normal file
38
collider.cpp
Normal file
|
@ -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);
|
||||||
|
}
|
32
collider.hpp
Normal file
32
collider.hpp
Normal file
|
@ -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;
|
||||||
|
};
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "dinkyecs.hpp"
|
#include "dinkyecs.hpp"
|
||||||
|
#include "map.hpp"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
namespace Components {
|
namespace Components {
|
||||||
|
|
5
map.hpp
5
map.hpp
|
@ -15,10 +15,13 @@
|
||||||
#define PLAYER_TILE "☺"
|
#define PLAYER_TILE "☺"
|
||||||
#define ENEMY_TILE "Ω"
|
#define ENEMY_TILE "Ω"
|
||||||
|
|
||||||
|
|
||||||
struct Point {
|
struct Point {
|
||||||
size_t x = 0;
|
size_t x = 0;
|
||||||
size_t y = 0;
|
size_t y = 0;
|
||||||
|
|
||||||
|
bool operator==(const Point& other) const {
|
||||||
|
return other.x == x && other.y == y;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Room {
|
struct Room {
|
||||||
|
|
|
@ -17,9 +17,11 @@ runtests = executable('runtests', [
|
||||||
'dbc.cpp',
|
'dbc.cpp',
|
||||||
'map.cpp',
|
'map.cpp',
|
||||||
'rand.cpp',
|
'rand.cpp',
|
||||||
|
'collider.cpp',
|
||||||
'tests/fsm.cpp',
|
'tests/fsm.cpp',
|
||||||
'tests/dbc.cpp',
|
'tests/dbc.cpp',
|
||||||
'tests/map.cpp',
|
'tests/map.cpp',
|
||||||
|
'tests/collider.cpp',
|
||||||
],
|
],
|
||||||
dependencies: dependencies)
|
dependencies: dependencies)
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ roguish = executable('roguish', [
|
||||||
'map.cpp',
|
'map.cpp',
|
||||||
'gui.cpp',
|
'gui.cpp',
|
||||||
'rand.cpp',
|
'rand.cpp',
|
||||||
|
'collider.cpp',
|
||||||
'systems.cpp',
|
'systems.cpp',
|
||||||
],
|
],
|
||||||
dependencies: dependencies)
|
dependencies: dependencies)
|
||||||
|
|
|
@ -6,3 +6,4 @@ TODO:
|
||||||
|
|
||||||
* Work on collision detection with either a coordinate map or morton codes.
|
* Work on collision detection with either a coordinate map or morton codes.
|
||||||
* Bring back sounds, check out SoLoud.
|
* Bring back sounds, check out SoLoud.
|
||||||
|
* getNearby does size_t - int
|
||||||
|
|
95
tests/collider.cpp
Normal file
95
tests/collider.cpp
Normal file
|
@ -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…
Add table
Add a link
Reference in a new issue