add tilemap/tilesheet
This commit is contained in:
parent
c18896def0
commit
ff698730f1
13 changed files with 841 additions and 28 deletions
215
src/pxl8_tilemap.c
Normal file
215
src/pxl8_tilemap.c
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
#include "pxl8_tilemap.h"
|
||||
#include "pxl8_tilesheet.h"
|
||||
#include "pxl8_macros.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
pxl8_result pxl8_tilemap_init(pxl8_tilemap* tilemap, u32 width, u32 height, u32 tile_size) {
|
||||
if (!tilemap) return PXL8_ERROR_NULL_POINTER;
|
||||
if (width > PXL8_MAX_TILEMAP_WIDTH || height > PXL8_MAX_TILEMAP_HEIGHT) {
|
||||
return PXL8_ERROR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
memset(tilemap, 0, sizeof(pxl8_tilemap));
|
||||
tilemap->width = width;
|
||||
tilemap->height = height;
|
||||
tilemap->tile_size = tile_size ? tile_size : PXL8_TILE_SIZE;
|
||||
tilemap->active_layers = 1;
|
||||
|
||||
for (u32 i = 0; i < PXL8_MAX_TILE_LAYERS; i++) {
|
||||
tilemap->layers[i].width = width;
|
||||
tilemap->layers[i].height = height;
|
||||
tilemap->layers[i].visible = (i == 0);
|
||||
tilemap->layers[i].opacity = 255;
|
||||
|
||||
size_t tiles_size = width * height * sizeof(pxl8_tile);
|
||||
tilemap->layers[i].tiles = calloc(1, tiles_size);
|
||||
if (!tilemap->layers[i].tiles) {
|
||||
for (u32 j = 0; j < i; j++) {
|
||||
free(tilemap->layers[j].tiles);
|
||||
}
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
void pxl8_tilemap_free(pxl8_tilemap* tilemap) {
|
||||
if (!tilemap) return;
|
||||
|
||||
for (u32 i = 0; i < PXL8_MAX_TILE_LAYERS; i++) {
|
||||
if (tilemap->layers[i].tiles) {
|
||||
free(tilemap->layers[i].tiles);
|
||||
tilemap->layers[i].tiles = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_result pxl8_tilemap_set_tilesheet(pxl8_tilemap* tilemap, pxl8_tilesheet* tilesheet) {
|
||||
if (!tilemap || !tilesheet) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
tilemap->tilesheet = tilesheet;
|
||||
|
||||
if (tilesheet->tile_size != tilemap->tile_size) {
|
||||
pxl8_warn("Tilesheet tile size (%d) differs from tilemap tile size (%d)",
|
||||
tilesheet->tile_size, tilemap->tile_size);
|
||||
tilemap->tile_size = tilesheet->tile_size;
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_tilemap_set_tile(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, u16 tile_id, u8 flags) {
|
||||
if (!tilemap) return PXL8_ERROR_NULL_POINTER;
|
||||
if (layer >= PXL8_MAX_TILE_LAYERS) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
if (x >= tilemap->width || y >= tilemap->height) return PXL8_ERROR_INVALID_COORDINATE;
|
||||
|
||||
pxl8_tilemap_layer* l = &tilemap->layers[layer];
|
||||
u32 idx = y * tilemap->width + x;
|
||||
l->tiles[idx].id = tile_id;
|
||||
l->tiles[idx].flags = flags;
|
||||
|
||||
if (layer >= tilemap->active_layers) {
|
||||
tilemap->active_layers = layer + 1;
|
||||
l->visible = true;
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
pxl8_tile pxl8_tilemap_get_tile(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y) {
|
||||
pxl8_tile empty_tile = {0, 0, 0};
|
||||
|
||||
if (!tilemap || layer >= PXL8_MAX_TILE_LAYERS) return empty_tile;
|
||||
if (x >= tilemap->width || y >= tilemap->height) return empty_tile;
|
||||
|
||||
return tilemap->layers[layer].tiles[y * tilemap->width + x];
|
||||
}
|
||||
|
||||
void pxl8_tilemap_set_camera(pxl8_tilemap* tilemap, i32 x, i32 y) {
|
||||
if (!tilemap) return;
|
||||
tilemap->camera_x = x;
|
||||
tilemap->camera_y = y;
|
||||
}
|
||||
|
||||
void pxl8_tilemap_get_view(const pxl8_tilemap* tilemap, const pxl8_gfx_ctx* gfx, pxl8_tilemap_view* view) {
|
||||
if (!tilemap || !gfx || !view) return;
|
||||
|
||||
view->x = -tilemap->camera_x;
|
||||
view->y = -tilemap->camera_y;
|
||||
view->width = gfx->framebuffer_width;
|
||||
view->height = gfx->framebuffer_height;
|
||||
|
||||
view->tile_start_x = pxl8_max(0, tilemap->camera_x / (i32)tilemap->tile_size);
|
||||
view->tile_start_y = pxl8_max(0, tilemap->camera_y / (i32)tilemap->tile_size);
|
||||
view->tile_end_x = pxl8_min((i32)tilemap->width,
|
||||
(tilemap->camera_x + view->width) / (i32)tilemap->tile_size + 1);
|
||||
view->tile_end_y = pxl8_min((i32)tilemap->height,
|
||||
(tilemap->camera_y + view->height) / (i32)tilemap->tile_size + 1);
|
||||
}
|
||||
|
||||
void pxl8_tilemap_render_tile(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u16 tile_id, i32 x, i32 y, u8 flags) {
|
||||
if (!tilemap || !gfx || !tilemap->tilesheet) return;
|
||||
pxl8_tilesheet_render_tile(tilemap->tilesheet, gfx, tile_id, x, y, flags);
|
||||
}
|
||||
|
||||
void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u32 layer) {
|
||||
if (!tilemap || !gfx || layer >= tilemap->active_layers) return;
|
||||
if (!tilemap->tilesheet) {
|
||||
pxl8_warn("No tilesheet set for tilemap");
|
||||
return;
|
||||
}
|
||||
|
||||
const pxl8_tilemap_layer* l = &tilemap->layers[layer];
|
||||
if (!l->visible) return;
|
||||
|
||||
pxl8_tilemap_view view;
|
||||
pxl8_tilemap_get_view(tilemap, gfx, &view);
|
||||
|
||||
for (i32 ty = view.tile_start_y; ty < view.tile_end_y; ty++) {
|
||||
for (i32 tx = view.tile_start_x; tx < view.tile_end_x; tx++) {
|
||||
pxl8_tile tile = pxl8_tilemap_get_tile(tilemap, layer, tx, ty);
|
||||
if (tile.id == 0) continue;
|
||||
|
||||
i32 screen_x = tx * tilemap->tile_size - tilemap->camera_x;
|
||||
i32 screen_y = ty * tilemap->tile_size - tilemap->camera_y;
|
||||
|
||||
pxl8_tilesheet_render_tile(tilemap->tilesheet, gfx, tile.id, screen_x, screen_y, tile.flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_tilemap_render(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx) {
|
||||
if (!tilemap || !gfx) return;
|
||||
|
||||
for (u32 layer = 0; layer < tilemap->active_layers; layer++) {
|
||||
pxl8_tilemap_render_layer(tilemap, gfx, layer);
|
||||
}
|
||||
}
|
||||
|
||||
bool pxl8_tilemap_is_solid(const pxl8_tilemap* tilemap, u32 x, u32 y) {
|
||||
if (!tilemap) return true;
|
||||
|
||||
u32 tile_x = x / tilemap->tile_size;
|
||||
u32 tile_y = y / tilemap->tile_size;
|
||||
|
||||
for (u32 layer = 0; layer < tilemap->active_layers; layer++) {
|
||||
pxl8_tile tile = pxl8_tilemap_get_tile(tilemap, layer, tile_x, tile_y);
|
||||
if (tile.flags & PXL8_TILE_SOLID) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 w, i32 h) {
|
||||
if (!tilemap) return true;
|
||||
|
||||
i32 left = x / tilemap->tile_size;
|
||||
i32 top = y / tilemap->tile_size;
|
||||
i32 right = (x + w - 1) / tilemap->tile_size;
|
||||
i32 bottom = (y + h - 1) / tilemap->tile_size;
|
||||
|
||||
for (i32 ty = top; ty <= bottom; ty++) {
|
||||
for (i32 tx = left; tx <= right; tx++) {
|
||||
if (tx < 0 || tx >= (i32)tilemap->width ||
|
||||
ty < 0 || ty >= (i32)tilemap->height) return true;
|
||||
|
||||
for (u32 layer = 0; layer < tilemap->active_layers; layer++) {
|
||||
pxl8_tile tile = pxl8_tilemap_get_tile(tilemap, layer, tx, ty);
|
||||
if (tile.flags & PXL8_TILE_SOLID) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pxl8_tilemap* pxl8_tilemap_new(u32 width, u32 height, u32 tile_size) {
|
||||
pxl8_tilemap* tilemap = calloc(1, sizeof(pxl8_tilemap));
|
||||
if (!tilemap) {
|
||||
pxl8_error("Failed to allocate tilemap");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pxl8_result result = pxl8_tilemap_init(tilemap, width, height, tile_size);
|
||||
if (result != PXL8_OK) {
|
||||
pxl8_error("Failed to initialize tilemap");
|
||||
free(tilemap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tilemap;
|
||||
}
|
||||
|
||||
void pxl8_tilemap_destroy(pxl8_tilemap* tilemap) {
|
||||
if (!tilemap) return;
|
||||
pxl8_tilemap_free(tilemap);
|
||||
free(tilemap);
|
||||
}
|
||||
|
||||
u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y) {
|
||||
if (!tilemap) return 0;
|
||||
pxl8_tile tile = pxl8_tilemap_get_tile(tilemap, layer, x, y);
|
||||
return tile.id;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue