summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-10 13:39:48 +0800
committerKonAki <konaki@anokonaki.org>2026-10-10 13:39:48 +0800
commitde91a3df52b25adc8d10ee8759bf969395905052 (patch)
tree04ed818fe9a67bb76c282414dc99060c75ee6dd6
parent1064801706b24e12229996765daf43fafe651bf0 (diff)
add platform example
-rw-r--r--Makefile2
-rw-r--r--examples/platform.c220
2 files changed, 221 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 1329467..76ebcd4 100644
--- a/Makefile
+++ b/Makefile
@@ -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);
+}