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.

master
Zed A. Shaw 11 months ago
parent a67d25ee10
commit cbf0955786
  1. 4
      levelmanager.hpp
  2. 14
      main.cpp
  3. 3
      point.hpp
  4. 54
      raycaster.cpp
  5. 6
      raycaster.hpp
  6. 16
      spatialmap.cpp
  7. 8
      spatialmap.hpp
  8. 14
      tests/matrix.cpp
  9. 27
      tests/spatialmap.cpp
  10. 8
      texture.cpp
  11. 4
      texture.hpp

@ -18,8 +18,8 @@ struct GameLevel {
}; };
struct LevelScaling { struct LevelScaling {
int map_width=40; int map_width=20;
int map_height=50; int map_height=20;
}; };
class LevelManager { class LevelManager {

@ -53,14 +53,16 @@ int main() {
TexturePack textures; TexturePack textures;
textures.load_tiles(); textures.load_tiles();
textures.load_sprites(); textures.load_sprites();
textures.position_sprite(4.0, 3.55, "evil_eye");
auto map = generate_map(textures, cur_level, player); 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); Raycaster rayview(window, textures, map, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT);
rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y); rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y);
rayview.position_camera(player.x, player.y); rayview.position_camera(player.x, player.y);
rayview.init_shaders(); rayview.init_shaders();
DinkyECS::Entity evil_ent = rayview.position_sprite(evil_eye_pos, "evil_eye");
double moveSpeed = 0.1; double moveSpeed = 0.1;
double rotSpeed = 0.1; double rotSpeed = 0.1;
@ -76,6 +78,9 @@ int main() {
window.setVerticalSyncEnabled(VSYNC); window.setVerticalSyncEnabled(VSYNC);
window.setFramerateLimit(FRAME_LIMIT); 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()) { while(window.isOpen()) {
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
rayview.render(); rayview.render();
@ -113,6 +118,13 @@ int main() {
} }
if(sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) { 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); rayview.$anim.play(false);
rotation = -30.0f; rotation = -30.0f;
} else { } else {

@ -17,6 +17,7 @@ typedef std::vector<Point> PointList;
template<> struct std::hash<Point> { template<> struct std::hash<Point> {
size_t operator()(const Point& p) const { size_t operator()(const Point& p) const {
return std::hash<int>()(p.x) ^ std::hash<int>()(p.y); auto hasher = std::hash<int>();
return hasher(p.x) ^ hasher(p.y);
} }
}; };

@ -34,8 +34,6 @@ Raycaster::Raycaster(sf::RenderWindow& window, TexturePack &textures, Matrix &ma
$width(width), $height(height), $width(width), $height(height),
$window(window), $window(window),
$map(map), $map(map),
spriteOrder(NUM_SPRITES),
spriteDistance(NUM_SPRITES),
ZBuffer(width), ZBuffer(width),
$anim(256, 256, 10, "assets/monster-1.ogg") $anim(256, 256, 10, "assets/monster-1.ogg")
{ {
@ -77,22 +75,11 @@ void Raycaster::sprite_casting() {
const int halfHeight = TEXTURE_HEIGHT / 2; const int halfHeight = TEXTURE_HEIGHT / 2;
// sort sprites from far to close // sort sprites from far to close
for(int i = 0; i < NUM_SPRITES; i++) { auto sprite_order = $collision.distance_sorted({(size_t)$posX, (size_t)$posY});
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);
// after sorting the sprites, do the projection // after sorting the sprites, do the projection
for(int i = 0; i < NUM_SPRITES; i++) { for(auto& rec : sprite_order) {
int sprite_index = spriteOrder[i]; Sprite& sprite_rec = $sprites[rec.second];
Sprite& sprite_rec = $textures.get_sprite(sprite_index);
// TODO: this must die // TODO: this must die
auto sf_sprite = sprite_rec.sprite.sprite; auto sf_sprite = sprite_rec.sprite.sprite;
@ -317,13 +304,15 @@ void Raycaster::draw_ceiling_floor() {
int cellX = int(floorX); int cellX = int(floorX);
int cellY = int(floorY); 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 tx = int(textureWidth * (floorX - cellX)) & (textureWidth - 1);
int ty = int(textureWidth * (floorY - cellY)) & (textureHeight - 1); int ty = int(textureWidth * (floorY - cellY)) & (textureHeight - 1);
floorX += floorStepX; floorX += floorStepX;
floorY += floorStepY; floorY += floorStepY;
double d = std::sqrt(($posX - floorX) * ($posX - floorX) + ($posY - floorY) * ($posY - floorY));
// now get the pixel from the texture // now get the pixel from the texture
uint32_t color; uint32_t color;
// this uses the previous ty/tx fractional parts of // this uses the previous ty/tx fractional parts of
@ -331,11 +320,11 @@ void Raycaster::draw_ceiling_floor() {
// FLOOR // FLOOR
color = floor_texture[textureWidth * ty + tx]; color = floor_texture[textureWidth * ty + tx];
$pixels[pixcoord(x, y)] = color; $pixels[pixcoord(x, y)] = dumb_lighting(color, d);
// CEILING // CEILING
color = ceiling_texture[textureWidth * ty + tx]; 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<int>& order, std::vector<double>& dist, int amount)
{
std::vector<std::pair<double, int>> 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) { void Raycaster::run(double speed, int dir) {
double speed_and_dir = speed * dir; double speed_and_dir = speed * dir;
if(empty_space(int($posX + $dirX * speed_and_dir), int($posY))) { 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); $planeX = $planeX * cos(speed_and_dir) - $planeY * sin(speed_and_dir);
$planeY = oldPlaneX * sin(speed_and_dir) + $planeY * cos(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;
}

@ -13,6 +13,7 @@
#include "texture.hpp" #include "texture.hpp"
#include <SFML/System/Clock.hpp> #include <SFML/System/Clock.hpp>
#include "animator.hpp" #include "animator.hpp"
#include "spatialmap.hpp"
using matrix::Matrix; using matrix::Matrix;
using RGBA = uint32_t; using RGBA = uint32_t;
@ -41,8 +42,8 @@ struct Raycaster {
int $height; int $height;
sf::RenderWindow& $window; sf::RenderWindow& $window;
Matrix& $map; Matrix& $map;
std::vector<int> spriteOrder; SpatialMap $collision;
std::vector<double> spriteDistance; std::vector<Sprite> $sprites;
std::vector<double> ZBuffer; // width std::vector<double> ZBuffer; // width
Animator $anim; Animator $anim;
sf::Shader $paused; sf::Shader $paused;
@ -66,6 +67,7 @@ struct Raycaster {
void set_position(int x, int y); void set_position(int x, int y);
void init_shaders(); void init_shaders();
DinkyECS::Entity position_sprite(Point pos, string name);
inline size_t pixcoord(int x, int y) { inline size_t pixcoord(int x, int y) {
if(!(x >=0 && x < $width)) { if(!(x >=0 && x < $width)) {

@ -64,3 +64,19 @@ FoundEntities SpatialMap::neighbors(Point cell, bool diag) const {
return {!result.empty(), result}; 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;
}

@ -8,7 +8,8 @@
typedef std::vector<DinkyECS::Entity> EntityList; typedef std::vector<DinkyECS::Entity> EntityList;
// Point's has is in point.hpp // Point's has is in point.hpp
typedef std::unordered_map<Point, DinkyECS::Entity> PointEntityMap; using PointEntityMap = std::unordered_map<Point, DinkyECS::Entity>;
using SortedEntities = std::vector<std::pair<int, DinkyECS::Entity>>;
struct FoundEntities { struct FoundEntities {
bool found; bool found;
@ -18,6 +19,7 @@ struct FoundEntities {
class SpatialMap { class SpatialMap {
public: public:
SpatialMap() {} SpatialMap() {}
PointEntityMap table;
void insert(Point pos, DinkyECS::Entity obj); void insert(Point pos, DinkyECS::Entity obj);
void move(Point from, Point to, DinkyECS::Entity ent); void move(Point from, Point to, DinkyECS::Entity ent);
@ -26,6 +28,6 @@ class SpatialMap {
DinkyECS::Entity get(Point at) const; DinkyECS::Entity get(Point at) const;
FoundEntities neighbors(Point position, bool diag=false) const; FoundEntities neighbors(Point position, bool diag=false) const;
private: SortedEntities distance_sorted(Point from);
PointEntityMap table; size_t size() { return table.size(); }
}; };

@ -44,13 +44,13 @@ TEST_CASE("basic matrix iterator", "[matrix:basic]") {
row_count += box.x == box.left; row_count += box.x == box.left;
walls[box.y][box.x] = 3; 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); 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 // confirm boxes have the right number of rows
// when x goes to 0 on first next call // when x goes to 0 on first next call
row_count = 0; 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]); println("START IS {},{}=={}", star.x, star.y, walls[star.y][star.x]);
walls[star.y][star.x] = 11; 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(); result[box.y][box.x] = box.distance();
} }
matrix::dump(format("MAP {}x{} @ {},{}; BOX {}x{}; size: {}", // matrix::dump(format("MAP {}x{} @ {},{}; BOX {}x{}; size: {}",
matrix::width(result), matrix::height(result), // matrix::width(result), matrix::height(result),
target.x, target.y, box.right - box.left, box.bottom - box.top, size), // target.x, target.y, box.right - box.left, box.bottom - box.top, size),
result, target.x, target.y); // result, target.x, target.y);
} }
TEST_CASE("thrash box iterators", "[matrix]") { TEST_CASE("thrash box iterators", "[matrix]") {

@ -3,6 +3,7 @@
#include <string> #include <string>
#include "spatialmap.hpp" #include "spatialmap.hpp"
#include "dinkyecs.hpp" #include "dinkyecs.hpp"
#include "rand.hpp"
using DinkyECS::Entity; using DinkyECS::Entity;
using namespace fmt; 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<size_t>(2,30);
for(size_t i = 0; i < max; i++) {
size_t x = Random::uniform<size_t>(0, 213);
size_t y = Random::uniform<size_t>(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;
}
}
}

@ -34,10 +34,6 @@ void TexturePack::load_sprites() {
ceiling = load_image(assets["sprites"]["ceiling"]); 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() { void TexturePack::load_tiles() {
Config assets("assets/tiles.json"); Config assets("assets/tiles.json");
auto &tiles = assets.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(); 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) { matrix::Matrix TexturePack::convert_char_to_texture(matrix::Matrix &tile_ids) {
auto result = matrix::make(matrix::width(tile_ids), matrix::height(tile_ids)); auto result = matrix::make(matrix::width(tile_ids), matrix::height(tile_ids));

@ -21,7 +21,6 @@ struct Sprite {
struct TexturePack { struct TexturePack {
std::vector<sf::Image> surfaces; std::vector<sf::Image> surfaces;
std::vector<Sprite> sprites;
std::unordered_map<std::string, SpriteTexture> sprite_textures; std::unordered_map<std::string, SpriteTexture> sprite_textures;
std::unordered_map<wchar_t, int> char_to_texture; std::unordered_map<wchar_t, int> char_to_texture;
sf::Image floor; sf::Image floor;
@ -31,10 +30,7 @@ struct TexturePack {
void load_tiles(); void load_tiles();
void load_sprites(); void load_sprites();
sf::Image load_image(std::string filename); sf::Image load_image(std::string filename);
Sprite& get_sprite(size_t sprite_num);
const uint32_t* get_surface(size_t 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 // ZED: this is ugly so maybe you should like rewrite it or something
matrix::Matrix convert_char_to_texture(matrix::Matrix &from); matrix::Matrix convert_char_to_texture(matrix::Matrix &from);