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 | de91a3df52b25adc8d10ee8759bf969395905052 (patch) | |
| tree | 04ed818fe9a67bb76c282414dc99060c75ee6dd6 | |
| parent | 1064801706b24e12229996765daf43fafe651bf0 (diff) | |
add platform example
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | examples/platform.c | 220 |
2 files changed, 221 insertions, 1 deletions
@@ -22,7 +22,7 @@ PACKFLAGS ?= BIN = $(OBJDIR)/$(PROGRAM)$(EXE) PACKER = $(OBJDIR)/konpack$(EXE) MODELER = $(OBJDIR)/konmodel$(EXE) -EXAMPLES = modelview sound ui +EXAMPLES = modelview sound ui platform EXAMPLEBINS = $(EXAMPLES:%=$(OBJDIR)/%$(EXE)) PACKFILE = $(OBJDIR)/data.kpk CORE = $(OBJDIR)/engine.o $(OBJDIR)/konwinlib.o $(OBJDIR)/konsofren.o \ diff --git a/examples/platform.c b/examples/platform.c new file mode 100644 index 0000000..869e820 --- /dev/null +++ b/examples/platform.c @@ -0,0 +1,220 @@ +/* + * platform: a box that runs and jumps around a tilemap, and a ray that follows the mouse + * + * build: ./build.sh -d -m tile,font obj/platform + * run: obj/platform + * + * left and right or a and d run, space or up jumps. the line from the box to the mouse stops at the first solid tile + * the console is not needed, but with the debug module the grave key opens it and the variables gravity and jump are there + */ + +#include <stdio.h> +#include <math.h> + +#include "konprogram.h" +#include "kontile.h" + +#if !defined(KON_MODULE_TILE) || !defined(KON_MODULE_FONT) +#error "build with: ./build.sh -d -m tile,font obj/platform" +#endif + +#define TILE 16 +#define COLUMNS 40 +#define ROWS 22 +#define STEP (1.0 / 120.0) + +#define COL_BACKGROUND 0xFF1B1826u +#define COL_TILE 0xFF4A4468u +#define COL_TILE_TOP 0xFF6B5F92u +#define COL_PLAYER 0xFFF2A7C3u +#define COL_RAY 0xFF8FD3C8u +#define COL_HIT 0xFFF3DDA0u +#define COL_DIM 0xFF9A93B3u + +static const char *level[ROWS] = { + "########################################", + "#......................................#", + "#......................................#", + "#......................................#", + "#......................................#", + "#..............#####...................#", + "#......................................#", + "#.........###..........................#", + "#......................................#", + "#.....................#####............#", + "#......................................#", + "#..####................................#", + "#......................................#", + "#..............####........#...........#", + "#.....................................##", + "#..........#.....................#######", + "#..........##..........................#", + "#..........###..................###....#", + "#..........####........................#", + "#.....#####............................#", + "#......................................#", + "########################################", +}; + +typedef struct state { + kon_tilemap_t map; + uint16_t tiles[COLUMNS * ROWS]; + uint8_t solid[2]; + + kon_box2_t player; + kon_vec2_t velocity; + int onGround; + double leftover, coyote; + int left, right, jumpHeld; + float gravity, jump; + + int mouseX, mouseY; + int rayHit; + kon_hit2_t hit; +} state_t; + +/*** program ***/ + +static int event(kon_program_t *program, const kon_event_t *ev, void *user) +{ + state_t *state = user; + + if (ev->type == KON_EVENT_MOUSE_MOVE) { + kon_windowToGame(program->display, ev->mouseX, ev->mouseY, &state->mouseX, &state->mouseY); + } else if (ev->type == KON_EVENT_KEY_DOWN || ev->type == KON_EVENT_KEY_UP) { + int down = ev->type == KON_EVENT_KEY_DOWN; + + if (ev->key == KON_KEY_LEFT || ev->key == KON_KEY_A) state->left = down; + else if (ev->key == KON_KEY_RIGHT || ev->key == KON_KEY_D) state->right = down; + else if (ev->key == KON_KEY_SPACE || ev->key == KON_KEY_UP) state->jumpHeld = down; + else return 0; + } else { + return 0; + } + return 1; +} + +static void step(state_t *state, float dt) +{ + float run = (float)(state->right - state->left) * 120.0f; + int flags; + + state->velocity.x += (run - state->velocity.x) * (state->onGround ? 18.0f : 8.0f) * dt; + state->velocity.y += state->gravity * dt; + + if (state->coyote > 0.0) state->coyote -= dt; + if (state->jumpHeld && state->coyote > 0.0) { + state->velocity.y = -state->jump; + state->coyote = 0.0; + } + /* letting go early cuts the jump short */ + if (!state->jumpHeld && state->velocity.y < -60.0f) state->velocity.y = -60.0f; + + flags = kon_tilemapMove(&state->map, &state->player, state->velocity.x * dt, state->velocity.y * dt, state->solid, 2); + + if (flags & (KON_HIT_LEFT | KON_HIT_RIGHT)) state->velocity.x = 0.0f; + if (flags & (KON_HIT_UP | KON_HIT_DOWN)) state->velocity.y = 0.0f; + state->onGround = (flags & KON_HIT_DOWN) != 0; + if (state->onGround) state->coyote = 0.08; +} + +static void update(kon_program_t *program, double dt, void *user) +{ + state_t *state = user; + kon_vec2_t from = KON_VEC2(state->player.x + state->player.w * 0.5f, state->player.y + state->player.h * 0.5f); + kon_vec2_t dir = KON_VEC2((float)state->mouseX - from.x, (float)state->mouseY - from.y); + float length = kon_vec2Length(dir); + int row, column; + + (void)program; + state->leftover += dt; + while (state->leftover >= STEP) { + step(state, (float)STEP); + state->leftover -= STEP; + } + + /* the ray against every solid tile, nothing clever, the nearest one wins */ + state->rayHit = 0; + if (length < 1.0f) return; + dir = kon_vec2Scale(dir, 1.0f / length); + for (row = 0; row < ROWS; row++) { + for (column = 0; column < COLUMNS; column++) { + kon_hit2_t hit; + + if (!state->solid[kon_tilemapAt(&state->map, column, row)]) continue; + if (!kon_rayBox(from, dir, length, KON_BOX2(column * TILE, row * TILE, TILE, TILE), &hit)) continue; + if (!state->rayHit || hit.time < state->hit.time) { + state->hit = hit; + state->rayHit = 1; + } + } + } +} + +static void draw(kon_program_t *program, kon_framebuffer_t *game, void *user) +{ + state_t *state = user; + int row, column, ex, ey; + int fromX = (int)(state->player.x + state->player.w * 0.5f), fromY = (int)(state->player.y + state->player.h * 0.5f); + + kon_clearFramebuffer(game, COL_BACKGROUND); + + for (row = 0; row < ROWS; row++) { + for (column = 0; column < COLUMNS; column++) { + if (!state->solid[kon_tilemapAt(&state->map, column, row)]) continue; + kon_fillRectangle(game, column * TILE, row * TILE, TILE, TILE, COL_TILE); + if (!state->solid[kon_tilemapAt(&state->map, column, row - 1)]) kon_fillRectangle(game, column * TILE, row * TILE, TILE, 2, COL_TILE_TOP); + } + } + + ex = state->rayHit ? (int)state->hit.point.x : state->mouseX; + ey = state->rayHit ? (int)state->hit.point.y : state->mouseY; + kon_drawLine(game, fromX, fromY, ex, ey, COL_RAY); + if (state->rayHit) kon_fillCircle(game, ex, ey, 2, COL_HIT); + + kon_fillRectangle(game, (int)state->player.x, (int)state->player.y, (int)state->player.w, (int)state->player.h, COL_PLAYER); + + if (program->font) kon_drawText(game, program->font, 24, 40, "arrows or a d to run, space to jump", COL_DIM); +} + +static int init(kon_program_t *program, void *user) +{ + state_t *state = user; + int row, column; + + state->map.columns = COLUMNS; + state->map.rows = ROWS; + state->map.tileWidth = TILE; + state->map.tileHeight = TILE; + state->map.tiles = state->tiles; + state->solid[1] = 1; + for (row = 0; row < ROWS; row++) { + for (column = 0; column < COLUMNS; column++) state->tiles[row * COLUMNS + column] = level[row][column] == '#'; + } + + state->player = KON_BOX2(40, 280, 10, 14); + state->gravity = 700.0f; + state->jump = 260.0f; + + kon_debugVar(program->debug, "gravity", KON_VAR_FLOAT, &state->gravity); + kon_debugVar(program->debug, "jump", KON_VAR_FLOAT, &state->jump); + return 1; +} + +int main(void) +{ + state_t state = {0}; + kon_programConfig_t config = {0}; + + config.title = "platform"; + config.gameWidth = 640; + config.gameHeight = 352; + config.windowWidth = 1280; + config.windowHeight = 704; + config.user = &state; + config.init = init; + config.event = event; + config.update = update; + config.draw = draw; + return kon_programRun(&config); +} |
