From dd1bbbc9deea80964067378dcf35f0039147235b Mon Sep 17 00:00:00 2001 From: Noel Berry Date: Sat, 2 Jan 2021 17:50:25 -0800 Subject: [PATCH] tilemap rendering! --- CMakeLists.txt | 2 +- content/tilesets/castle.ase | Bin 0 -> 601 bytes src/assets/tileset.cpp | 0 src/assets/tileset.h | 18 ++++++++++ src/components/collider.cpp | 26 ++++++++++++++ src/components/collider.h | 1 + src/components/tilemap.cpp | 61 +++++++++++++++++++++++++++++++ src/components/tilemap.h | 32 +++++++++++++++++ src/content.cpp | 69 ++++++++++++++++++++++++++++++++---- src/content.h | 2 ++ src/factory.cpp | 5 +-- src/game.cpp | 21 ++++++++--- src/game.h | 5 +++ 13 files changed, 228 insertions(+), 14 deletions(-) create mode 100644 content/tilesets/castle.ase create mode 100644 src/assets/tileset.cpp create mode 100644 src/assets/tileset.h create mode 100644 src/components/tilemap.cpp create mode 100644 src/components/tilemap.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 111d535..57a3050 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ add_executable(game src/masks.h src/factory.h src/factory.cpp -) + "src/assets/tileset.h" "src/assets/tileset.cpp" "src/components/tilemap.h" "src/components/tilemap.cpp") # Reference blah target_link_libraries(game blah) diff --git a/content/tilesets/castle.ase b/content/tilesets/castle.ase new file mode 100644 index 0000000000000000000000000000000000000000..cc9e9dda926bb89ab85c0e4cf6ddbe543967db52 GIT binary patch literal 601 zcmb`Fy-Pw-7>AFU=n#Y$fkSwRu(qz!mt+V*L)jn>hBRbYf(}L@Q6vr_1THQ?lnp@z z4Yd|Dv_vk^5CmZj71$O*Aq{EVo~woZ0ljb@-uoUnzvo;|utA8&eVvfP#L@{NW?1!N zrVZIO)rbGs6*&F#%}`A>aBeHhD65fwQts%jQvOVAe|D;^Dnz^Nfg@dR7;!mZHZTYu z=IyYs6M?ViTd@4z2REO6Fm|TE{`ENYM!TWk+X0~YI<*F)u^0>n{BUtN0}q`}SeOXI+m%z8-57(JIYo_efky4WOByc04baCO zaja3s7gn)C&caOjJ(nhB}pG*vu@;6PEWXg6hV14;$ MPE_ + +using namespace Blah; + +namespace TL +{ + struct Tileset + { + static constexpr int max_columns = 16; + static constexpr int max_rows = 16; + + String name; + int columns = 0; + int rows = 0; + Subtexture tiles[max_columns * max_rows]; + }; +} \ No newline at end of file diff --git a/src/components/collider.cpp b/src/components/collider.cpp index 717d6ea..fad9cbd 100644 --- a/src/components/collider.cpp +++ b/src/components/collider.cpp @@ -24,6 +24,9 @@ Collider Collider::make_grid(int tile_size, int columns, int rows) collider.m_grid.columns = columns; collider.m_grid.rows = rows; collider.m_grid.cells = std::shared_ptr(new bool[columns * rows]); + + memset(collider.m_grid.cells.get(), 0, sizeof(bool) * columns * rows); + return collider; } @@ -60,6 +63,13 @@ void Collider::set_cell(int x, int y, bool value) m_grid.cells[x + y * m_grid.columns] = value; } +void Collider::set_cells(int x, int y, int w, int h, bool value) +{ + for (int tx = x; tx < x + w; tx++) + for (int ty = y; ty < y + h; ty++) + m_grid.cells[tx + ty * m_grid.columns] = value; +} + bool Collider::check(uint32_t mask, Point offset) const { auto other = world()->first(); @@ -143,5 +153,21 @@ bool TL::Collider::rect_to_rect(const Collider* a, const Collider* b, Point offs bool TL::Collider::rect_to_grid(const Collider* a, const Collider* b, Point offset) { + // get a relative rectangle to the grid + RectI rect = a->m_rect + a->entity()->position + offset - b->entity()->position; + + // get the cells the rectangle overlaps + int left = Calc::clamp_int(Calc::floor(rect.x / (float)b->m_grid.tile_size), 0, b->m_grid.columns); + int right = Calc::clamp_int(Calc::ceiling(rect.right() / (float)b->m_grid.tile_size), 0, b->m_grid.columns); + int top = Calc::clamp_int(Calc::floor(rect.y / (float)b->m_grid.tile_size), 0, b->m_grid.rows); + int bottom = Calc::clamp_int(Calc::ceiling(rect.bottom() / (float)b->m_grid.tile_size), 0, b->m_grid.rows); + + // check each cell + for (int x = left; x < right; x++) + for (int y = top; y < bottom; y++) + if (b->m_grid.cells[x + y * b->m_grid.columns]) + return true; + + // all cells were empty return false; } diff --git a/src/components/collider.h b/src/components/collider.h index a976589..a22e5ec 100644 --- a/src/components/collider.h +++ b/src/components/collider.h @@ -29,6 +29,7 @@ namespace TL void set_rect(const RectI& value); bool get_cell(int x, int y) const; void set_cell(int x, int y, bool value); + void set_cells(int x, int y, int w, int h, bool value); bool check(uint32_t mask, Point offset = Point::zero) const; bool overlaps(const Collider* other, Point offset = Point::zero) const; diff --git a/src/components/tilemap.cpp b/src/components/tilemap.cpp new file mode 100644 index 0000000..276ab62 --- /dev/null +++ b/src/components/tilemap.cpp @@ -0,0 +1,61 @@ +#include "tilemap.h" + +using namespace TL; + +Tilemap::Tilemap() +{ +} + +Tilemap::Tilemap(int tile_width, int tile_height, int columns, int rows) +{ + m_tile_width = tile_width; + m_tile_height = tile_height; + m_columns = columns; + m_rows = rows; + m_grid = std::shared_ptr(new Subtexture[columns * rows]); +} + +int Tilemap::tile_width() const +{ + return m_tile_width; +} + +int Tilemap::tile_height() const +{ + return m_tile_height; +} + +int Tilemap::columns() const +{ + return m_columns; +} + +int Tilemap::rows() const +{ + return m_rows; +} + +void Tilemap::set_cell(int x, int y, const Subtexture* tex) +{ + if (tex) + m_grid[x + y * m_columns] = *tex; + else + m_grid[x + y * m_columns].texture.reset(); +} + +void Tilemap::set_cells(int x, int y, int w, int h, const Subtexture* tex) +{ + for (int tx = x; tx < x + w; tx++) + for (int ty = y; ty < y + h; ty++) + set_cell(tx, ty, tex); +} + +void Tilemap::render(Batch& batch) +{ + for (int x = 0; x < m_columns; x ++) + for (int y = 0; y < m_rows; y ++) + if (m_grid[x + y * m_columns].texture) + { + batch.tex(m_grid[x + y * m_columns], Vec2(x * m_tile_width, y * m_tile_height)); + } +} diff --git a/src/components/tilemap.h b/src/components/tilemap.h new file mode 100644 index 0000000..3d57208 --- /dev/null +++ b/src/components/tilemap.h @@ -0,0 +1,32 @@ +#pragma once +#include "../world.h" +#include +#include + +using namespace Blah; + +namespace TL +{ + class Tilemap : public Component + { + public: + Tilemap(); + Tilemap(int tile_width, int tile_height, int columns, int rows); + + int tile_width() const; + int tile_height() const; + int columns() const; + int rows() const; + + void set_cell(int x, int y, const Subtexture* tex); + void set_cells(int x, int y, int w, int h, const Subtexture* tex); + void render(Batch& batch) override; + + private: + std::shared_ptr m_grid; + int m_tile_width = 0; + int m_tile_height = 0; + int m_columns = 0; + int m_rows = 0; + }; +} \ No newline at end of file diff --git a/src/content.cpp b/src/content.cpp index 632eda5..d3a8f8c 100644 --- a/src/content.cpp +++ b/src/content.cpp @@ -1,5 +1,7 @@ #include "content.h" +#include "game.h" #include "assets/sprite.h" +#include "assets/tileset.h" using namespace TL; @@ -7,6 +9,7 @@ namespace { FilePath root; Vector sprites; + Vector tilesets; Vector subtextures; TextureRef sprite_atlas; @@ -62,13 +65,8 @@ void Content::load() SpriteInfo* info = sprite_info.expand(); info->aseprite = Aseprite(it.cstr()); info->name = String(it.cstr() + sprite_path.length(), it.end() - 4); - } - - // add to the atlas - for (auto& info : sprite_info) - { - info.pack_index = pack_index; - for (auto& frame : info.aseprite.frames) + info->pack_index = pack_index; + for (auto& frame : info->aseprite.frames) { packer.add(pack_index, frame.image); pack_index++; @@ -76,6 +74,36 @@ void Content::load() } } + // load tileset + Vector tileset_info; + { + // get all the tilesets + FilePath sprite_path = path() + "tilesets/"; + for (auto& it : Directory::enumerate(sprite_path, true)) + { + if (!it.ends_with(".ase")) + continue; + + SpriteInfo* info = tileset_info.expand(); + info->aseprite = Aseprite(it.cstr()); + info->name = String(it.cstr() + sprite_path.length(), it.end() - 4); + info->pack_index = pack_index; + + auto& frame = info->aseprite.frames[0]; + auto columns = frame.image.width / Game::tile_width; + auto rows = frame.image.height / Game::tile_height; + + for (int x = 0; x < columns; x++) + for (int y = 0; y < rows; y++) + { + auto subrect = RectI(x * Game::tile_width, y * Game::tile_height, Game::tile_width, Game::tile_height); + auto subimage = frame.image.get_sub_image(subrect); + packer.add(pack_index, subimage); + pack_index++; + } + } + } + // build the atlas { packer.pack(); @@ -113,6 +141,24 @@ void Content::load() } } } + + // add tilesets + for (auto& info : tileset_info) + { + auto& frame = info.aseprite.frames[0]; + + Tileset* tileset = tilesets.expand(); + tileset->name = info.name; + tileset->columns = frame.image.width / Game::tile_width; + tileset->rows = frame.image.height / Game::tile_height; + + for (int x = 0, i = info.pack_index; x < tileset->columns; x++) + for (int y = 0; y < tileset->rows; y++) + { + tileset->tiles[x + y * tileset->columns] = subtextures[i]; + i++; + } + } } void Content::unload() @@ -133,3 +179,12 @@ const Sprite* Content::find_sprite(const char* name) return nullptr; } + +const Tileset* Content::find_tileset(const char* name) +{ + for (auto& it : tilesets) + if (it.name == name) + return ⁢ + + return nullptr; +} \ No newline at end of file diff --git a/src/content.h b/src/content.h index 5895516..90016bc 100644 --- a/src/content.h +++ b/src/content.h @@ -6,6 +6,7 @@ using namespace Blah; namespace TL { struct Sprite; + struct Tileset; class Content { @@ -18,5 +19,6 @@ namespace TL static TextureRef atlas(); static const Sprite* find_sprite(const char* name); + static const Tileset* find_tileset(const char* name); }; } \ No newline at end of file diff --git a/src/factory.cpp b/src/factory.cpp index 1678fdc..3b609b3 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -10,8 +10,9 @@ Entity* Factory::player(World* world, Point position) { auto en = world->add_entity(position); - auto an = en->add(Animator("player")); - an->play("idle"); + auto anim = en->add(Animator("player")); + anim->play("idle"); + anim->depth = -10; auto hitbox = en->add(Collider::make_rect(RectI(-4, -8, 8, 8))); diff --git a/src/game.cpp b/src/game.cpp index 5cef3d1..e19769e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,7 +1,9 @@ #include "game.h" #include "content.h" #include "masks.h" +#include "assets/tileset.h" #include "components/collider.h" +#include "components/tilemap.h" #include "factory.h" using namespace TL; @@ -12,7 +14,7 @@ void Game::startup() Content::load(); // framebuffer for the game - buffer = FrameBuffer::create(320, 180); + buffer = FrameBuffer::create(width, height); // set batcher to use Nearest Filter batch.default_sampler = TextureSampler(TextureFilter::Nearest); @@ -27,10 +29,20 @@ void Game::load_map() world.clear(); // add a test player - Factory::player(&world, Point(50, 50)); + Factory::player(&world, Point(width / 2, height - 32)); - auto floor = world.add_entity(Point(0, 100)); - auto c2 = floor->add(Collider::make_rect(RectI(0, 0, 320, 16))); + // get the castle tileset for now + auto castle = Content::find_tileset("castle"); + + // make the floor + auto floor = world.add_entity(); + auto tm = floor->add(Tilemap(8, 8, 40, 23)); + tm->set_cells(0, 20, 40, 3, &castle->tiles[0]); + tm->set_cells(0, 18, 10, 2, &castle->tiles[0]); + + auto c2 = floor->add(Collider::make_grid(8, 40, 23)); + c2->set_cells(0, 20, 40, 3, true); + c2->set_cells(0, 18, 10, 2, true); c2->mask = Mask::solid; } @@ -67,6 +79,7 @@ void Game::render() } } + batch.tex(Content::atlas()); batch.render(buffer); batch.clear(); } diff --git a/src/game.h b/src/game.h index 58c4269..6038a85 100644 --- a/src/game.h +++ b/src/game.h @@ -9,6 +9,11 @@ namespace TL class Game { public: + static constexpr int width = 320; + static constexpr int height = 180; + static constexpr int tile_width = 8; + static constexpr int tile_height = 8; + World world; FrameBufferRef buffer; Batch batch;