Refactor WorldPos, WindowsPos done

This commit is contained in:
Jan Mrna 2025-10-04 16:54:29 +02:00
parent 4e9674b77c
commit 1ac55d7e10
13 changed files with 92 additions and 65 deletions

View File

@ -5,7 +5,6 @@
#include "log.hpp"
#include "math.hpp"
#include "sprite.hpp"
#include "coorginates.hpp"
Entity::Entity(WorldPos position) : m_Position(position) {
LOG_DEBUG("spawning entity at position ", position);

View File

@ -26,10 +26,15 @@ void GameLoop::Run() {
const auto &tiles = map.GetMapTiles();
for (size_t row = 0; row < tiles.size(); row++) {
for (size_t col = 0; col < tiles[row].size(); col++) {
const auto& camera = m_Game->GetCamera();
// LOG_DEBUG("Drawing rect (", row, ", ", col, ")");
m_Window->DrawRect(
map.TileEdgeToWorld(TilePos{row, col}),
map.GetTileSize(), tiles[row][col]->R, tiles[row][col]->G,
camera.WorldToWindow(
map.TileEdgeToWorld(
TilePos{static_cast<int32_t>(row), static_cast<int32_t>(col)}
)
),
camera.WorldToWindow(map.GetTileSize()), tiles[row][col]->R, tiles[row][col]->G,
tiles[row][col]->B, tiles[row][col]->A);
}
}
@ -37,13 +42,15 @@ void GameLoop::Run() {
// draw the path, if it exists
WorldPos start_pos = m_Game->GetPlayer()->GetPosition();
for (const auto& next_pos: m_Game->GetPath()) {
m_Window->DrawLine(start_pos, next_pos);
const auto& camera = m_Game->GetCamera();
m_Window->DrawLine(camera.WorldToWindow(start_pos), camera.WorldToWindow(next_pos));
start_pos = next_pos;
}
// draw all the entities (player etc)
for (auto &entity : m_Game->GetEntities()) {
m_Window->DrawSprite(entity->GetPosition(), entity->GetSprite());
const auto& camera = m_Game->GetCamera();
m_Window->DrawSprite(camera.WorldToWindow(entity->GetPosition()), entity->GetSprite());
}
m_Window->Flush();

View File

@ -17,23 +17,24 @@ Map::Map(int rows, int cols) : m_Cols(cols), m_Rows(rows) {
}
WorldPos Map::TileToWorld(TilePos p) const {
return WorldPos{(p.x + 0.5) * TILE_SIZE, (p.y + 0.5) * TILE_SIZE};
return WorldPos{(p.x() + 0.5f) * TILE_SIZE, (p.y() + 0.5f) * TILE_SIZE};
}
WorldPos Map::TileEdgeToWorld(TilePos p) const {
return WorldPos{p.x * TILE_SIZE, p.y * TILE_SIZE};
return WorldPos{p.x() * TILE_SIZE, p.y() * TILE_SIZE};
}
TilePos Map::WorldToTile(WorldPos p) const {
return TilePos{p.x / TILE_SIZE, p.y / TILE_SIZE};
return TilePos{static_cast<int32_t>(p.x() / TILE_SIZE), static_cast<int32_t>(p.y() / TILE_SIZE)};
}
// TODO this should probably use something like WorldSize or WorldVec to make the distinction clear
WorldPos Map::GetTileSize() const { return WorldPos{TILE_SIZE, TILE_SIZE}; }
const Tile *Map::GetTileAt(TilePos p) const {
assert(IsTilePosValid(p));
size_t row = p.x;
size_t col = p.y;
size_t row = p.x();
size_t col = p.y();
return m_Tiles[row][col];
}
@ -43,10 +44,10 @@ const Tile *Map::GetTileAt(WorldPos p) const {
}
bool Map::IsTilePosValid(TilePos p) const {
if (p.x < 0 || p.y < 0)
if (p.x() < 0 || p.y() < 0)
return false;
size_t row = static_cast<size_t>(p.x);
size_t col = static_cast<size_t>(p.y);
size_t row = static_cast<size_t>(p.x());
size_t col = static_cast<size_t>(p.y());
return row < m_Tiles.size() && col < m_Tiles[0].size();
}
@ -75,14 +76,14 @@ std::vector<TilePos> Map::GetNeighbors(TilePos center) const
void Map::PaintCircle(TilePos center, unsigned radius, TileType tile_type)
{
// get rectangle that wraps the circle
TilePos corner1 = TilePos{center.x - radius, center.y - radius};
TilePos corner2 = TilePos{center.x + radius, center.y + radius};
TilePos corner1 = TilePos{center.x() - static_cast<int32_t>(radius), center.y() - static_cast<int32_t>(radius)};
TilePos corner2 = TilePos{center.x() + static_cast<int32_t>(radius), center.y() + static_cast<int32_t>(radius)};
// iterate through all valid points, setting the type
const unsigned radius_squared = radius * radius;
for (int x = corner1.x; x < corner2.x; x++) {
for (int y = corner1.y; y < corner2.y; y++) {
for (int x = corner1.x(); x < corner2.x(); x++) {
for (int y = corner1.y(); y < corner2.y(); y++) {
TilePos current_tile = {x, y};
unsigned distance_squared = center.distance_squared(current_tile);
unsigned distance_squared = static_cast<unsigned>(center.DistanceTo(current_tile) * center.DistanceTo(current_tile));
if (IsTilePosValid(current_tile) && distance_squared < radius_squared)
{
// y is row, x is col
@ -94,19 +95,20 @@ void Map::PaintCircle(TilePos center, unsigned radius, TileType tile_type)
void Map::PaintLine(TilePos start_tile, TilePos stop_tile, double width, TileType tile_type)
{
const Vec2D<double> start(start_tile);
const Vec2D<double> stop(stop_tile);
const double line_length = start.distance(stop);
const Vec2D<double> step((stop-start)/line_length);
const Vec2D<double> ortho = step.orthogonal();
const vec<double, 2> start{static_cast<double>(start_tile.x()), static_cast<double>(start_tile.y())};
const vec<double, 2> stop{static_cast<double>(stop_tile.x()), static_cast<double>(stop_tile.y())};
const double line_length = start.DistanceTo(stop);
const vec<double, 2> step = (stop - start) / line_length;
const vec<double, 2> ortho = step.GetOrthogonal();
LOG_DEBUG("step = ", step, " ortho = ", ortho);
for (double t = 0; t < line_length; t += 1.0) {
for (double ortho_t = 0; ortho_t < width; ortho_t += 0.1) {
auto tile_pos = start + step * t + ortho * ortho_t;
if (IsTilePosValid(tile_pos)) {
size_t row = static_cast<size_t>(tile_pos.x);
size_t col = static_cast<size_t>(tile_pos.y);
TilePos tile_pos_int{static_cast<int32_t>(tile_pos.x()), static_cast<int32_t>(tile_pos.y())};
if (IsTilePosValid(tile_pos_int)) {
size_t row = static_cast<size_t>(tile_pos.x());
size_t col = static_cast<size_t>(tile_pos.y());
m_Tiles[row][col] = &tile_types.at(tile_type);
}
}
@ -116,15 +118,15 @@ void Map::PaintLine(TilePos start_tile, TilePos stop_tile, double width, TileTyp
void Map::PaintRectangle(TilePos first_corner, TilePos second_corner, TileType tile_type)
{
std::initializer_list<int> xvals = {first_corner.x, second_corner.x};
std::initializer_list<int> yvals = {first_corner.y, second_corner.y};
std::initializer_list<int> xvals = {first_corner.x(), second_corner.x()};
std::initializer_list<int> yvals = {first_corner.y(), second_corner.y()};
for (int x = std::min(xvals); x < std::max(xvals); x++) {
for (int y = std::min(yvals); y < std::max(yvals); y++) {
TilePos tile_pos{x,y};
LOG_DEBUG("tile_pos = ", tile_pos);
if (IsTilePosValid(tile_pos)) {
size_t row = static_cast<size_t>(tile_pos.x);
size_t col = static_cast<size_t>(tile_pos.y);
size_t row = static_cast<size_t>(tile_pos.x());
size_t col = static_cast<size_t>(tile_pos.y());
m_Tiles[row][col] = &tile_types.at(tile_type);
}
}

View File

@ -60,7 +60,15 @@ public:
// binary operators
//
friend bool operator==(const vec &a, const vec &b) {
friend bool operator==(const vec &a, const vec &b)
requires (std::is_integral_v<T>)
{
return std::ranges::equal(a.m_Array, b.m_Array);
}
friend bool operator==(const vec &a, const vec &b)
requires (std::is_floating_point_v<T>)
{
for (const auto &[u, v] : std::views::zip(a.m_Array, b.m_Array)) {
if (!equalEpsilon(u, v)) {
return false;
@ -72,21 +80,21 @@ public:
friend bool operator!=(const vec &a, const vec &b) { return !(a == b); }
friend vec operator+(const vec &a, const vec &b) {
vec<T, N> c;
vec<T, N, Tag> c;
std::ranges::transform(a.m_Array, b.m_Array, c.m_Array.begin(),
std::plus{});
return c;
}
friend vec operator-(const vec &a, const vec &b) {
vec<T, N> c;
vec<T, N, Tag> c;
std::ranges::transform(a.m_Array, b.m_Array, c.m_Array.begin(),
std::minus{});
return c;
}
friend vec operator*(const vec &a, const T &scalar) {
vec<T, N> c;
vec<T, N, Tag> c;
std::ranges::transform(a.m_Array, std::views::repeat(scalar),
c.m_Array.begin(), std::multiplies{});
return c;
@ -95,7 +103,7 @@ public:
friend vec operator*(const T &scalar, const vec &a) { return a * scalar; }
friend vec operator/(const vec &a, const T &scalar) {
vec<T, N> c;
vec<T, N, Tag> c;
std::ranges::transform(a.m_Array, std::views::repeat(scalar),
c.m_Array.begin(), std::divides{});
return c;
@ -124,7 +132,7 @@ public:
//
T LengthSquared() const {
return std::transform_reduce(m_Array.begin(), m_Array.end(), T{0.0},
return std::transform_reduce(m_Array.begin(), m_Array.end(), T{},
std::plus{}, [](T x) { return x * x; });
}
@ -230,15 +238,14 @@ using uvec3 = vec<std::uint32_t, 3>;
using uvec4 = vec<std::uint32_t, 4>;
// tags for differentiating between domains
struct WorldTag {
} struct WindowTag {
} struct TileTag {
}
struct WorldTag {};
struct WindowTag {};
struct TileTag {};
// types for each domain
using WorldPos = vec<float, 2, WorldTag>;
using WindowPos = vec<float, 2, WindowTag>;
using TilePos = vec<int32_t, 2, WindowTag>;
using TilePos = vec<int32_t, 2, TileTag>;
//
// Utils

View File

@ -11,7 +11,7 @@ namespace pathfinder {
float GBFS::Heuristic(const TilePos& a, const TilePos& b)
{
return static_cast<float>(std::abs(a.x- b.x) + std::abs(a.y - b.y));
return static_cast<float>(std::abs(a.x() - b.x()) + std::abs(a.y() - b.y()));
}
Path GBFS::CalculatePath(WorldPos start_world, WorldPos end_world)

View File

@ -63,7 +63,7 @@ void PathFindingDemo::CreateMap() {
}
WorldPos PathFindingDemo::GetRandomPosition() const {
return WorldPos{0.0, 0.0}; // totally random!
return WorldPos{0.0f, 0.0f}; // totally random!
}
std::optional<WorldPos> PathFindingDemo::GetMoveTarget() {
@ -75,7 +75,7 @@ std::optional<WorldPos> PathFindingDemo::GetMoveTarget() {
WorldPos next_player_pos = m_Path.front();
if (current_player_pos.distance(next_player_pos) > 1.0) {
if (current_player_pos.DistanceTo(next_player_pos) > 1.0) {
// target not reached yet
return next_player_pos;
}
@ -95,7 +95,7 @@ void PathFindingDemo::UpdatePlayerVelocity() {
WorldPos velocity = WorldPos{};
if (next_pos) {
velocity = next_pos.value() - current_pos;
velocity.normalize();
velocity.Normalize();
//LOG_DEBUG("I want to move to: ", next_pos.value(),
// ", velocity: ", velocity);
}

View File

@ -10,6 +10,7 @@
#include "map.hpp"
#include "user_input.hpp"
#include "pathfinder/base.hpp"
#include "camera.hpp"
class PathFindingDemo {
public:
@ -24,6 +25,7 @@ public:
std::shared_ptr<Player> GetPlayer() { return m_Player; }
std::vector<std::shared_ptr<Entity>>& GetEntities() { return m_Entities; }
const Map& GetMap() const { return m_Map; }
const Camera& GetCamera() const { return m_Camera; }
const pathfinder::Path& GetPath() const { return m_Path; }
bool IsExitRequested() const { return m_ExitRequested; }
@ -37,6 +39,7 @@ public:
private:
bool m_ExitRequested = false;
Map m_Map;
Camera m_Camera;
std::vector<std::shared_ptr<Entity>> m_Entities;
std::shared_ptr<Player> m_Player;
pathfinder::Path m_Path;

View File

@ -12,13 +12,13 @@
Sprite::Sprite() : m_Texture(nullptr, SDL_DestroyTexture) {}
Sprite::Sprite(std::string path, Vec2D<float> center) : Sprite() {
Sprite::Sprite(std::string path, WorldPos center) : Sprite() {
LoadImage(path, center);
}
Sprite::~Sprite() { LOG_DEBUG("."); }
void Sprite::LoadImage(std::string path, Vec2D<float> image_center) {
void Sprite::LoadImage(std::string path, WorldPos image_center) {
LOG_INFO("Loading image ", path);
assert(m_Renderer != nullptr);

View File

@ -14,7 +14,7 @@ class Sprite {
public:
Sprite();
~Sprite();
explicit Sprite(std::string path, WorldPos center = {0, 0});
explicit Sprite(std::string path, WorldPos center = WorldPos{});
Sprite(const Sprite &) = delete;
Sprite &operator=(const Sprite &) = delete;
@ -28,7 +28,7 @@ public:
WorldPos GetSize() const { return m_Size; }
WorldPos GetCenter() const { return m_ImageCenter; }
void LoadImage(std::string path, WorldPos image_center = {0.0, 0.0});
void LoadImage(std::string path, WorldPos image_center = WorldPos{});
private:
static std::shared_ptr<SDL_Renderer> m_Renderer;

View File

@ -11,8 +11,8 @@ class UserAction {
public:
enum class Type { NONE, EXIT, SET_MOVE_TARGET, SELECT_PATHFINDER };
UserAction() = default;
UserAction(Type t) : type(t) {}
UserAction() : type(Type::NONE), Argument{.number = 0} {}
UserAction(Type t) : type(t), Argument{.number = 0} {}
UserAction(Type t, char key) : type(t), Argument{.key = key} {}
UserAction(Type t, WorldPos v) : type(t), Argument{.position = v} {}
UserAction(Type t, int arg) : type(t), Argument{.number = arg} {}
@ -25,6 +25,9 @@ public:
char key;
int number;
} Argument;
// TODO use std::variant
//std::variant<WorldPos, char, int> Argument;
};
class UserInput {

View File

@ -76,17 +76,17 @@ Window::~Window() {
LOG_DEBUG(".");
}
void Window::DrawSprite(const WorldPos &position, Sprite &s) {
void Window::DrawSprite(const WindowPos &position, Sprite &s) {
WorldPos size = s.GetSize();
WorldPos img_center = s.GetCenter();
SDL_FRect rect = {position.x - img_center.x, position.y - img_center.y,
size.x, size.y};
SDL_FRect rect = {position.x() - img_center.x(), position.y() - img_center.y(),
size.x(), size.y()};
SDL_RenderTexture(m_Renderer.get(), s.GetTexture(), nullptr, &rect);
}
void Window::DrawRect(const WorldPos &position, const WorldPos size, uint8_t R,
void Window::DrawRect(const WindowPos &position, const WindowPos size, uint8_t R,
uint8_t G, uint8_t B, uint8_t A) {
SDL_FRect rect = {position.x, position.y, size.x, size.y};
SDL_FRect rect = {position.x(), position.y(), size.x(), size.y()};
SDL_SetRenderDrawColor(m_Renderer.get(), R, G, B, A);
SDL_RenderFillRect(m_Renderer.get(), &rect);
}
@ -98,9 +98,9 @@ void Window::ClearWindow() {
void Window::Flush() { SDL_RenderPresent(m_Renderer.get()); }
void Window::DrawCircle(const WorldPos &position, float radius) {
int cx = static_cast<int>(position.x);
int cy = static_cast<int>(position.y);
void Window::DrawCircle(const WindowPos &position, float radius) {
int cx = static_cast<int>(position.x());
int cy = static_cast<int>(position.y());
SDL_SetRenderDrawColor(m_Renderer.get(), 255, 0, 0, 255);
for (int i = 0; i < 360; ++i) {
double a = i * M_PI / 180.0;
@ -110,9 +110,9 @@ void Window::DrawCircle(const WorldPos &position, float radius) {
}
}
void Window::DrawLine(const WorldPos &A, const WorldPos &B)
void Window::DrawLine(const WindowPos &A, const WindowPos &B)
{
SDL_SetRenderDrawColor(m_Renderer.get(), 255, 0, 0, 255);
SDL_RenderLine(m_Renderer.get(), A.x, A.y, B.x, B.y);
SDL_RenderLine(m_Renderer.get(), A.x(), A.y(), B.x(), B.y());
}

View File

@ -22,13 +22,13 @@ public:
Window &operator=(Window &&) = delete;
std::expected<void, std::string> Init();
void DrawSprite(const WorldPos &position, Sprite &s);
void DrawRect(const WorldPos &position, const WorldPos size, uint8_t R,
void DrawSprite(const WindowPos &position, Sprite &s);
void DrawRect(const WindowPos &position, const WindowPos size, uint8_t R,
uint8_t G, uint8_t B, uint8_t A);
void ClearWindow();
void Flush();
void DrawCircle(const WorldPos &position, float radius);
void DrawLine(const WorldPos &A, const WorldPos &B);
void DrawCircle(const WindowPos &position, float radius);
void DrawLine(const WindowPos &A, const WindowPos &B);
private:
uint32_t m_Width;

View File

@ -37,6 +37,12 @@ TEST(vec, equalEpsilon) {
ASSERT_EQ(v1, v2);
}
TEST(vec, equalInt) {
ivec2 v1{1,2};
ivec2 v2{1,2};
ASSERT_EQ(v1, v2);
}
TEST(vec, nonEqualEpsilon) {
// Test operator!=
vec3 v1{1.0f, 2.0f, 3.0f};