tilemap rendering!

This commit is contained in:
Noel Berry 2021-01-02 17:50:25 -08:00
parent c38b1e1418
commit dd1bbbc9de
13 changed files with 228 additions and 14 deletions

View File

@ -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)

BIN
content/tilesets/castle.ase Normal file

Binary file not shown.

0
src/assets/tileset.cpp Normal file
View File

18
src/assets/tileset.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <blah.h>
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];
};
}

View File

@ -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<bool[]>(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<Collider>();
@ -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;
}

View File

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

View File

@ -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<Subtexture[]>(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));
}
}

32
src/components/tilemap.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include "../world.h"
#include <blah.h>
#include <memory>
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<Subtexture[]> m_grid;
int m_tile_width = 0;
int m_tile_height = 0;
int m_columns = 0;
int m_rows = 0;
};
}

View File

@ -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<Sprite> sprites;
Vector<Tileset> tilesets;
Vector<Subtexture> 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<SpriteInfo> 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 &it;
return nullptr;
}

View File

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

View File

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

View File

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

View File

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