Implemented a simple collision hash table.

main
Zed A. Shaw 2 months ago
parent dbc2a10933
commit 743f906bc7
  1. 38
      collider.cpp
  2. 32
      collider.hpp
  3. 1
      components.hpp
  4. 5
      map.hpp
  5. 3
      meson.build
  6. 1
      status.txt
  7. 95
      tests/collider.cpp

@ -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;
};

@ -1,5 +1,6 @@
#pragma once
#include "dinkyecs.hpp"
#include "map.hpp"
#include <deque>
namespace Components {

@ -15,10 +15,13 @@
#define PLAYER_TILE "☺"
#define ENEMY_TILE "Ω"
struct Point {
size_t x = 0;
size_t y = 0;
bool operator==(const Point& other) const {
return other.x == x && other.y == y;
}
};
struct Room {

@ -17,9 +17,11 @@ runtests = executable('runtests', [
'dbc.cpp',
'map.cpp',
'rand.cpp',
'collider.cpp',
'tests/fsm.cpp',
'tests/dbc.cpp',
'tests/map.cpp',
'tests/collider.cpp',
],
dependencies: dependencies)
@ -29,6 +31,7 @@ roguish = executable('roguish', [
'map.cpp',
'gui.cpp',
'rand.cpp',
'collider.cpp',
'systems.cpp',
],
dependencies: dependencies)

@ -6,3 +6,4 @@ TODO:
* Work on collision detection with either a coordinate map or morton codes.
* Bring back sounds, check out SoLoud.
* getNearby does size_t - int

@ -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…
Cancel
Save