From cbf09557862fc9c288429fd3860cec3863862c0d Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sat, 1 Feb 2025 14:39:08 -0500 Subject: [PATCH] Raycaster now controls the sprite locations with SpatialMap rather than the old way. Quick hack job in main.cpp that shows how they can move too. --- levelmanager.hpp | 4 ++-- main.cpp | 14 +++++++++++- point.hpp | 3 ++- raycaster.cpp | 54 ++++++++++++++------------------------------ raycaster.hpp | 6 +++-- spatialmap.cpp | 16 +++++++++++++ spatialmap.hpp | 8 ++++--- tests/matrix.cpp | 14 ++++++------ tests/spatialmap.cpp | 27 ++++++++++++++++++++++ texture.cpp | 8 ------- texture.hpp | 4 ---- 11 files changed, 93 insertions(+), 65 deletions(-) diff --git a/levelmanager.hpp b/levelmanager.hpp index c8bf0f6..f207d76 100644 --- a/levelmanager.hpp +++ b/levelmanager.hpp @@ -18,8 +18,8 @@ struct GameLevel { }; struct LevelScaling { - int map_width=40; - int map_height=50; + int map_width=20; + int map_height=20; }; class LevelManager { diff --git a/main.cpp b/main.cpp index 88f5503..6d75ba0 100644 --- a/main.cpp +++ b/main.cpp @@ -53,14 +53,16 @@ int main() { TexturePack textures; textures.load_tiles(); textures.load_sprites(); - textures.position_sprite(4.0, 3.55, "evil_eye"); auto map = generate_map(textures, cur_level, player); + Point evil_eye_pos{player.x+1, player.y+1}; + Raycaster rayview(window, textures, map, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT); rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y); rayview.position_camera(player.x, player.y); rayview.init_shaders(); + DinkyECS::Entity evil_ent = rayview.position_sprite(evil_eye_pos, "evil_eye"); double moveSpeed = 0.1; double rotSpeed = 0.1; @@ -76,6 +78,9 @@ int main() { window.setVerticalSyncEnabled(VSYNC); window.setFramerateLimit(FRAME_LIMIT); + double new_x = evil_eye_pos.x+0.1; + double new_y = evil_eye_pos.y+0.1; + while(window.isOpen()) { auto start = std::chrono::high_resolution_clock::now(); rayview.render(); @@ -113,6 +118,13 @@ int main() { } if(sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) { + new_x += 0.1; + new_y += 0.1; + rayview.$collision.move(evil_eye_pos, {size_t(new_x), size_t(new_y)}, evil_ent); + evil_eye_pos = {size_t(new_x), size_t(new_y)}; + rayview.$sprites[evil_ent].x = new_x; + rayview.$sprites[evil_ent].y = new_y; + rayview.$anim.play(false); rotation = -30.0f; } else { diff --git a/point.hpp b/point.hpp index 50e57cd..2409b43 100644 --- a/point.hpp +++ b/point.hpp @@ -17,6 +17,7 @@ typedef std::vector PointList; template<> struct std::hash { size_t operator()(const Point& p) const { - return std::hash()(p.x) ^ std::hash()(p.y); + auto hasher = std::hash(); + return hasher(p.x) ^ hasher(p.y); } }; diff --git a/raycaster.cpp b/raycaster.cpp index 13a20b9..ecf8c40 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -34,8 +34,6 @@ Raycaster::Raycaster(sf::RenderWindow& window, TexturePack &textures, Matrix &ma $width(width), $height(height), $window(window), $map(map), - spriteOrder(NUM_SPRITES), - spriteDistance(NUM_SPRITES), ZBuffer(width), $anim(256, 256, 10, "assets/monster-1.ogg") { @@ -77,22 +75,11 @@ void Raycaster::sprite_casting() { const int halfHeight = TEXTURE_HEIGHT / 2; // sort sprites from far to close - for(int i = 0; i < NUM_SPRITES; i++) { - auto& sprite = $textures.get_sprite(i); - spriteOrder[i] = i; - // this is just the distance calculation - spriteDistance[i] = (($posX - sprite.x) * - ($posX - sprite.x) + - ($posY - sprite.y) * - ($posY - sprite.y)); - } - - sort_sprites(spriteOrder, spriteDistance, NUM_SPRITES); + auto sprite_order = $collision.distance_sorted({(size_t)$posX, (size_t)$posY}); // after sorting the sprites, do the projection - for(int i = 0; i < NUM_SPRITES; i++) { - int sprite_index = spriteOrder[i]; - Sprite& sprite_rec = $textures.get_sprite(sprite_index); + for(auto& rec : sprite_order) { + Sprite& sprite_rec = $sprites[rec.second]; // TODO: this must die auto sf_sprite = sprite_rec.sprite.sprite; @@ -317,13 +304,15 @@ void Raycaster::draw_ceiling_floor() { int cellX = int(floorX); int cellY = int(floorY); - // get the texture coordinat from the fractional part + // get the texture coordinate from the fractional part int tx = int(textureWidth * (floorX - cellX)) & (textureWidth - 1); int ty = int(textureWidth * (floorY - cellY)) & (textureHeight - 1); floorX += floorStepX; floorY += floorStepY; + double d = std::sqrt(($posX - floorX) * ($posX - floorX) + ($posY - floorY) * ($posY - floorY)); + // now get the pixel from the texture uint32_t color; // this uses the previous ty/tx fractional parts of @@ -331,11 +320,11 @@ void Raycaster::draw_ceiling_floor() { // FLOOR color = floor_texture[textureWidth * ty + tx]; - $pixels[pixcoord(x, y)] = color; + $pixels[pixcoord(x, y)] = dumb_lighting(color, d); // CEILING color = ceiling_texture[textureWidth * ty + tx]; - $pixels[pixcoord(x, $height - y - 1)] = color; + $pixels[pixcoord(x, $height - y - 1)] = dumb_lighting(color, d); } } } @@ -357,24 +346,6 @@ bool Raycaster::empty_space(int new_x, int new_y) { } -void Raycaster::sort_sprites(std::vector& order, std::vector& dist, int amount) -{ - std::vector> sprites(amount); - - for(int i = 0; i < amount; i++) { - sprites[i].first = dist[i]; - sprites[i].second = order[i]; - } - - std::sort(sprites.begin(), sprites.end()); - - // restore in reverse order - for(int i = 0; i < amount; i++) { - dist[i] = sprites[amount - i - 1].first; - order[i] = sprites[amount - i - 1].second; - } -} - void Raycaster::run(double speed, int dir) { double speed_and_dir = speed * dir; if(empty_space(int($posX + $dirX * speed_and_dir), int($posY))) { @@ -396,3 +367,12 @@ void Raycaster::rotate(double speed, int dir) { $planeX = $planeX * cos(speed_and_dir) - $planeY * sin(speed_and_dir); $planeY = oldPlaneX * sin(speed_and_dir) + $planeY * cos(speed_and_dir); } + + +DinkyECS::Entity Raycaster::position_sprite(Point pos, string name) { + auto sprite_txt = $textures.sprite_textures[name]; + $sprites.emplace_back(pos.x, pos.y, sprite_txt); + DinkyECS::Entity ent = $sprites.size() - 1; + $collision.insert({pos.x, pos.y}, ent); + return ent; +} diff --git a/raycaster.hpp b/raycaster.hpp index bde4048..b92b2f5 100644 --- a/raycaster.hpp +++ b/raycaster.hpp @@ -13,6 +13,7 @@ #include "texture.hpp" #include #include "animator.hpp" +#include "spatialmap.hpp" using matrix::Matrix; using RGBA = uint32_t; @@ -41,8 +42,8 @@ struct Raycaster { int $height; sf::RenderWindow& $window; Matrix& $map; - std::vector spriteOrder; - std::vector spriteDistance; + SpatialMap $collision; + std::vector $sprites; std::vector ZBuffer; // width Animator $anim; sf::Shader $paused; @@ -66,6 +67,7 @@ struct Raycaster { void set_position(int x, int y); void init_shaders(); + DinkyECS::Entity position_sprite(Point pos, string name); inline size_t pixcoord(int x, int y) { if(!(x >=0 && x < $width)) { diff --git a/spatialmap.cpp b/spatialmap.cpp index 330e839..90a8b6a 100644 --- a/spatialmap.cpp +++ b/spatialmap.cpp @@ -64,3 +64,19 @@ FoundEntities SpatialMap::neighbors(Point cell, bool diag) const { return {!result.empty(), result}; } + +SortedEntities SpatialMap::distance_sorted(Point from) { + SortedEntities sprite_distance; + + for(const auto &rec : table) { + Point sprite = rec.first; + int inside = (from.x - sprite.x) * (from.x - sprite.x) + + (from.y - sprite.y) * (from.y - sprite.y); + + sprite_distance.push_back({inside, rec.second}); + } + + std::sort(sprite_distance.begin(), sprite_distance.end()); + + return sprite_distance; +} diff --git a/spatialmap.hpp b/spatialmap.hpp index 97a6266..57b09d2 100644 --- a/spatialmap.hpp +++ b/spatialmap.hpp @@ -8,7 +8,8 @@ typedef std::vector EntityList; // Point's has is in point.hpp -typedef std::unordered_map PointEntityMap; +using PointEntityMap = std::unordered_map; +using SortedEntities = std::vector>; struct FoundEntities { bool found; @@ -18,6 +19,7 @@ struct FoundEntities { class SpatialMap { public: SpatialMap() {} + PointEntityMap table; void insert(Point pos, DinkyECS::Entity obj); void move(Point from, Point to, DinkyECS::Entity ent); @@ -26,6 +28,6 @@ class SpatialMap { DinkyECS::Entity get(Point at) const; FoundEntities neighbors(Point position, bool diag=false) const; - private: - PointEntityMap table; + SortedEntities distance_sorted(Point from); + size_t size() { return table.size(); } }; diff --git a/tests/matrix.cpp b/tests/matrix.cpp index 3b60f66..b28bdd5 100644 --- a/tests/matrix.cpp +++ b/tests/matrix.cpp @@ -44,13 +44,13 @@ TEST_CASE("basic matrix iterator", "[matrix:basic]") { row_count += box.x == box.left; walls[box.y][box.x] = 3; } - matrix::dump("2,2 WALLS", walls, 2, 2); + //matrix::dump("2,2 WALLS", walls, 2, 2); REQUIRE(row_count == 3); } { - matrix::dump("1:1 POINT", walls, 1,1); + // matrix::dump("1:1 POINT", walls, 1,1); // confirm boxes have the right number of rows // when x goes to 0 on first next call row_count = 0; @@ -68,7 +68,7 @@ TEST_CASE("basic matrix iterator", "[matrix:basic]") { println("START IS {},{}=={}", star.x, star.y, walls[star.y][star.x]); walls[star.y][star.x] = 11; } - matrix::dump("STAR POINT", walls, 1,1); + // matrix::dump("STAR POINT", walls, 1,1); } } @@ -115,10 +115,10 @@ TEST_CASE("thrash box distance iterators", "[matrix:distance]") { result[box.y][box.x] = box.distance(); } - matrix::dump(format("MAP {}x{} @ {},{}; BOX {}x{}; size: {}", - matrix::width(result), matrix::height(result), - target.x, target.y, box.right - box.left, box.bottom - box.top, size), - result, target.x, target.y); + // matrix::dump(format("MAP {}x{} @ {},{}; BOX {}x{}; size: {}", + // matrix::width(result), matrix::height(result), + // target.x, target.y, box.right - box.left, box.bottom - box.top, size), + // result, target.x, target.y); } TEST_CASE("thrash box iterators", "[matrix]") { diff --git a/tests/spatialmap.cpp b/tests/spatialmap.cpp index 06560e8..9a893bd 100644 --- a/tests/spatialmap.cpp +++ b/tests/spatialmap.cpp @@ -3,6 +3,7 @@ #include #include "spatialmap.hpp" #include "dinkyecs.hpp" +#include "rand.hpp" using DinkyECS::Entity; using namespace fmt; @@ -135,3 +136,29 @@ TEST_CASE("check all diagonal works", "[collision]") { } } } + +TEST_CASE("confirm can iterate through all", "[spatialmap-sort]") { + DinkyECS::World world; + SpatialMap collider; + Point player{10,10}; + + for(int i = 0; i < 10; i++) { + size_t max = Random::uniform(2,30); + for(size_t i = 0; i < max; i++) { + size_t x = Random::uniform(0, 213); + size_t y = Random::uniform(0, 251); + + Entity ent = world.entity(); + collider.insert({x,y}, ent); + } + + auto sprite_distance = collider.distance_sorted(player); + + int prev_dist = 0; + + for(auto dist : sprite_distance) { + REQUIRE(prev_dist <= dist.first); + prev_dist = dist.first; + } + } +} diff --git a/texture.cpp b/texture.cpp index d01a9a0..5213d0b 100644 --- a/texture.cpp +++ b/texture.cpp @@ -34,10 +34,6 @@ void TexturePack::load_sprites() { ceiling = load_image(assets["sprites"]["ceiling"]); } -void TexturePack::position_sprite(double x, double y, string name) { - sprites.emplace_back(x, y, sprite_textures[name]); -} - void TexturePack::load_tiles() { Config assets("assets/tiles.json"); auto &tiles = assets.json(); @@ -58,10 +54,6 @@ const uint32_t* TexturePack::get_surface(size_t num) { return (const uint32_t *)surfaces[num].getPixelsPtr(); } -Sprite &TexturePack::get_sprite(size_t sprite_num) { - return sprites[sprite_num]; -} - matrix::Matrix TexturePack::convert_char_to_texture(matrix::Matrix &tile_ids) { auto result = matrix::make(matrix::width(tile_ids), matrix::height(tile_ids)); diff --git a/texture.hpp b/texture.hpp index beed4cd..90d8cc7 100644 --- a/texture.hpp +++ b/texture.hpp @@ -21,7 +21,6 @@ struct Sprite { struct TexturePack { std::vector surfaces; - std::vector sprites; std::unordered_map sprite_textures; std::unordered_map char_to_texture; sf::Image floor; @@ -31,10 +30,7 @@ struct TexturePack { void load_tiles(); void load_sprites(); sf::Image load_image(std::string filename); - Sprite& get_sprite(size_t sprite_num); const uint32_t* get_surface(size_t num); - // this needs to go into a map place - void position_sprite(double x, double y, std::string name); // ZED: this is ugly so maybe you should like rewrite it or something matrix::Matrix convert_char_to_texture(matrix::Matrix &from);