/* * platform: a box that runs and jumps around a tilemap with a camera that follows it, and a ray that follows the mouse * * build: ./build.sh -d -m 2d,platformer,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 #include #include "konprogram.h" #include "kontile.h" #include "kon2d.h" #include "konplatformer.h" #if !defined(KON_MODULE_2D) || !defined(KON_MODULE_PLATFORMER) || !defined(KON_MODULE_TILE) || !defined(KON_MODULE_FONT) #error "build with: ./build.sh -d -m 2d,platformer,font obj/platform" #endif #define TILE 16 #define COLUMNS 40 #define ROWS 22 #define VIEW_WIDTH 320 #define VIEW_HEIGHT 176 #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_platformer_t player; kon_camera2_t camera; int left, right; 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->player.jumpHeld = down; else return 0; } else { return 0; } return 1; } static void update(kon_program_t *program, double dt, void *user) { state_t *state = user; kon_box2_t *box = &state->player.box; kon_vec2_t from = KON_VEC2(box->x + box->w * 0.5f, box->y + box->h * 0.5f); kon_vec2_t dir; float length; int row, column; (void)program; state->player.moveX = state->right - state->left; kon_platformerUpdate(&state->player, &state->map, state->solid, 2, dt); kon_camera2Follow(&state->camera, from.x, from.y, (float)dt); /* the ray against every solid tile, nothing clever, the nearest one wins */ dir = KON_VEC2((float)(state->mouseX + kon_camera2X(&state->camera)) - from.x, (float)(state->mouseY + kon_camera2Y(&state->camera)) - from.y); length = kon_vec2Length(dir); 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; kon_box2_t *box = &state->player.box; int camX = kon_camera2X(&state->camera), camY = kon_camera2Y(&state->camera); int row, column, ex, ey; int fromX = (int)(box->x + box->w * 0.5f) - camX, fromY = (int)(box->y + box->h * 0.5f) - camY; kon_clearFramebuffer(game, COL_BACKGROUND); for (row = 0; row < ROWS; row++) { for (column = 0; column < COLUMNS; column++) { int x = column * TILE - camX, y = row * TILE - camY; if (!state->solid[kon_tilemapAt(&state->map, column, row)]) continue; kon_fillRectangle(game, x, y, TILE, TILE, COL_TILE); if (!state->solid[kon_tilemapAt(&state->map, column, row - 1)]) kon_fillRectangle(game, x, y, TILE, 2, COL_TILE_TOP); } } ex = state->rayHit ? (int)state->hit.point.x - camX : state->mouseX; ey = state->rayHit ? (int)state->hit.point.y - camY : 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)box->x - camX, (int)box->y - camY, (int)box->w, (int)box->h, COL_PLAYER); if (program->font) kon_drawText(game, program->font, 8, 16, "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] == '#'; } kon_platformerInit(&state->player, KON_BOX2(40, 280, 10, 14)); kon_camera2Init(&state->camera, VIEW_WIDTH, VIEW_HEIGHT, COLUMNS * TILE, ROWS * TILE); kon_camera2Snap(&state->camera, 45.0f, 287.0f); kon_debugVar(program->debug, "gravity", KON_VAR_FLOAT, &state->player.gravity); kon_debugVar(program->debug, "jump", KON_VAR_FLOAT, &state->player.jumpSpeed); return 1; } int main(void) { state_t state = {0}; kon_programConfig_t config = {0}; config.title = "platform"; config.gameWidth = VIEW_WIDTH; config.gameHeight = VIEW_HEIGHT; 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); }