diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-10 13:39:48 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-10 13:39:48 +0800 |
| commit | 1064801706b24e12229996765daf43fafe651bf0 (patch) | |
| tree | 8a0f84b973179fd495e831e254b1cb0ade053436 /kon | |
| parent | ee3a22ce2987790cec4b47db0b9cc1edf018ff19 (diff) | |
tilemap move that slides along walls
Diffstat (limited to 'kon')
| -rw-r--r-- | kon/kontile.c | 70 | ||||
| -rw-r--r-- | kon/kontile.h | 12 |
2 files changed, 82 insertions, 0 deletions
diff --git a/kon/kontile.c b/kon/kontile.c index b133656..add9ba9 100644 --- a/kon/kontile.c +++ b/kon/kontile.c @@ -26,6 +26,8 @@ #include "kontile.h" +#include <math.h> + static int kon_floorDiv_(int a, int b) { int q = a / b; return (a % b != 0 && (a < 0) != (b < 0)) ? q - 1 : q; @@ -99,3 +101,71 @@ void kon_drawTilemap(kon_framebuffer_t *fb, const kon_tilemap_t *map, int camera } } } + +/*** moving ***/ + +static int kon_tileSolid_(const kon_tilemap_t *map, int column, int row, const uint8_t *solid, int solidCount) { + uint16_t id = kon_tilemapAt(map, column, row); + + return id < solidCount && solid[id]; +} + +/* tiles a box covers, an edge exactly on a tile border does not touch the next tile */ +static void kon_tileSpan_(const kon_tilemap_t *map, kon_box2_t box, int *c0, int *r0, int *c1, int *r1) { + *c0 = (int)floorf(box.x / (float)map->tileWidth); + *r0 = (int)floorf(box.y / (float)map->tileHeight); + *c1 = (int)ceilf((box.x + box.w) / (float)map->tileWidth) - 1; + *r1 = (int)ceilf((box.y + box.h) / (float)map->tileHeight) - 1; +} + +/* moves along x or y by at most one tile at a time. returns 1 if it ran into a tile */ +static int kon_tileStep_(const kon_tilemap_t *map, kon_box2_t *box, int vertical, float amount, const uint8_t *solid, int solidCount) { + float tile = (float)(vertical ? map->tileHeight : map->tileWidth); + int c0, r0, c1, r1, c, r; + + while (amount != 0.0f) { + float step = amount > tile ? tile : (amount < -tile ? -tile : amount); + kon_box2_t next = *box; + + if (vertical) next.y += step; + else next.x += step; + amount -= step; + + kon_tileSpan_(map, next, &c0, &r0, &c1, &r1); + + /* the line of tiles the box is moving into */ + { + int line = vertical ? (step > 0.0f ? r1 : r0) : (step > 0.0f ? c1 : c0); + int hit = 0, b0, br0, b1, br1; + + if (vertical) { + for (c = c0; c <= c1 && !hit; c++) hit = kon_tileSolid_(map, c, line, solid, solidCount); + } else { + for (r = r0; r <= r1 && !hit; r++) hit = kon_tileSolid_(map, line, r, solid, solidCount); + } + + /* a line the box was already in means it started inside a tile, let it move */ + kon_tileSpan_(map, *box, &b0, &br0, &b1, &br1); + if (hit && (vertical ? (line >= br0 && line <= br1) : (line >= b0 && line <= b1))) hit = 0; + + if (hit) { + if (vertical) next.y = step > 0.0f ? (float)(line * map->tileHeight) - next.h : (float)((line + 1) * map->tileHeight); + else next.x = step > 0.0f ? (float)(line * map->tileWidth) - next.w : (float)((line + 1) * map->tileWidth); + *box = next; + return 1; + } + } + *box = next; + } + return 0; +} + +int kon_tilemapMove(const kon_tilemap_t *map, kon_box2_t *box, float dx, float dy, const uint8_t *solid, int solidCount) { + int flags = 0; + + if (!map || !box || !solid || solidCount <= 0 || map->tileWidth <= 0 || map->tileHeight <= 0 || box->w <= 0.0f || box->h <= 0.0f) return 0; + + if (kon_tileStep_(map, box, 0, dx, solid, solidCount)) flags |= dx > 0.0f ? KON_HIT_RIGHT : KON_HIT_LEFT; + if (kon_tileStep_(map, box, 1, dy, solid, solidCount)) flags |= dy > 0.0f ? KON_HIT_DOWN : KON_HIT_UP; + return flags; +} diff --git a/kon/kontile.h b/kon/kontile.h index f671da9..00119b1 100644 --- a/kon/kontile.h +++ b/kon/kontile.h @@ -27,6 +27,7 @@ #define KONTILE_H #include "konsofren.h" +#include "koncollide.h" /*** definition ***/ @@ -52,6 +53,17 @@ int kon_tilemapPixelToTile(const kon_tilemap_t *map, int x, int y, int *column, and everything outside the map counts as. returns 1 if any tile touching the box x, y, width, height (in map pixels) is solid */ int kon_tilemapBoxHits(const kon_tilemap_t *map, int x, int y, int width, int height, const uint8_t *solid, int solidCount); +/* which sides of the box ran into something in kon_tilemapMove */ +#define KON_HIT_LEFT 1 +#define KON_HIT_RIGHT 2 +#define KON_HIT_UP 4 +#define KON_HIT_DOWN 8 + +/* moves the box by dx, dy (in map pixels) and stops it against solid tiles, one axis after the other so it slides along walls. + solid works like in kon_tilemapBoxHits. returns the KON_HIT_ flags of what stopped it, down is the way y grows. + the box can start inside a solid tile, it is left alone on that axis. fast moves do not tunnel through tiles */ +int kon_tilemapMove(const kon_tilemap_t *map, kon_box2_t *box, float dx, float dy, const uint8_t *solid, int solidCount); + /* cameraX, cameraY is the map pixel shown at the top left of fb */ void kon_drawTilemap(kon_framebuffer_t *fb, const kon_tilemap_t *map, int cameraX, int cameraY); |
