/* * corridor: a small first person level, walls from a tilemap, sprites that face you, and collision on the same map * * build: ./build.sh -d -m 3d,font obj/corridor * ./build.sh -d -m 3d,font,assets,image obj/corridor (also loads sprites/marker.png from the assets if there is one) * run: obj/corridor * * click to grab the mouse and look around with it, tab lets go. with the debug module the variable sens sets the mouse speed. w and s walk, a and d strafe, the arrows turn too * the walls are quads made from the tilemap every frame, the markers are billboards drawn with depth so walls hide them * the markers are plain placeholders, put your own sprite in assets/sprites/marker.png to see it in their place */ #include #include #include "konprogram.h" #include "kon3d.h" #if !defined(KON_MODULE_3D) || !defined(KON_MODULE_FONT) #error "build with: ./build.sh -d -m 3d,font obj/corridor" #endif #define COLUMNS 16 #define ROWS 16 #define TILE 32 #define WALL_HEIGHT 1.2f #define MARKERS 5 #define COL_SKY 0xFF1B1826u #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_image *brick, *floor, *marker; int ownsMarker; kon_fp_t fp; double time; kon_vec2_t markers[MARKERS]; /* world units */ } state_t; /*** images made in code ***/ static kon_image *makeBrick(void) { kon_image *image = kon_createFramebuffer(32, 32); int row, i; if (!image) return NULL; kon_clearFramebuffer(image, 0xFF6B5F92); for (row = 0; row < 4; row++) { kon_fillRectangle(image, 0, row * 8, 32, 1, 0xFF3A3558); for (i = 0; i < 2; i++) kon_fillRectangle(image, (row % 2) * 8 + i * 16, row * 8, 1, 8, 0xFF3A3558); } return image; } static kon_image *makeFloor(void) { kon_image *image = kon_createFramebuffer(32, 32); if (!image) return NULL; kon_clearFramebuffer(image, 0xFF2A2640); kon_fillRectangle(image, 0, 0, 16, 16, 0xFF332E4E); kon_fillRectangle(image, 16, 16, 16, 16, 0xFF332E4E); return image; } static kon_image *makeMarker(void) { kon_image *image = kon_createFramebuffer(32, 40); int i; if (!image) return NULL; kon_clearFramebuffer(image, 0xFF6B5F92); kon_fillRectangle(image, 2, 2, 28, 36, 0xFF332E4E); for (i = 0; i < 28; i++) { kon_fillRectangle(image, 2 + i, 2 + i * 36 / 28, 1, 1, 0xFF6B5F92); kon_fillRectangle(image, 2 + i, 37 - i * 36 / 28, 1, 1, 0xFF6B5F92); } return image; } /*** drawing ***/ static void draw(kon_program_t *program, kon_framebuffer_t *game, void *user) { state_t *state = user; kon_light_t light = kon_lightDefault(); kon_material_t brick = kon_materialTexture(state->brick), floor = kon_materialTexture(state->floor); kon_level3d_t level = { &state->map, state->solid, 2, WALL_HEIGHT, &brick, &floor, &floor }; kon_camera_t camera = kon_fpCamera(&state->fp, &state->map); kon_mat4_t vp = kon_cameraViewProjection(&camera, game->width, game->height); int i; kon_clearFramebuffer(game, COL_SKY); light.direction = KON_VEC3(-0.3f, -1.0f, -0.5f); light.ambient = 0.55f; kon_drawLevel(game, &camera, &level, &light); for (i = 0; i < MARKERS; i++) { float dx = state->markers[i].x - camera.position.x, dz = state->markers[i].y - camera.position.z; float bob = 0.08f * sinf((float)state->time * 2.0f + (float)i); kon_drawBillboard(game, vp, KON_VEC3(state->markers[i].x, 0.1f + bob, state->markers[i].y), 0.6f, 0.75f, state->marker, KON_RECT(0, 0, 0, 0), kon_distanceFade(sqrtf(dx * dx + dz * dz), 0.18f), KON_RENDER_DEPTH); } if (program->font) kon_drawText(game, program->font, 8, 16, "click to look with the mouse, tab lets go", COL_DIM); } /*** program ***/ static int event(kon_program_t *program, const kon_event_t *ev, void *user) { state_t *state = user; return kon_fpEvent(&state->fp, program, ev); } static void update(kon_program_t *program, double dt, void *user) { state_t *state = user; state->time += dt; kon_fpUpdate(&state->fp, program, &state->map, state->solid, 2, NULL, dt); } static int init(kon_program_t *program, void *user) { state_t *state = user; static const kon_vec2_t spots[MARKERS] = { { 3.5f, 6.5f }, { 12.5f, 3.5f }, { 8.0f, 13.0f }, { 5.5f, 11.5f }, { 11.5f, 7.5f } }; int row, column, i; 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->brick = makeBrick(); state->floor = makeFloor(); #if defined(KON_MODULE_ASSETS) && defined(KON_MODULE_IMAGE) if (program->assets) state->marker = kon_assetImage(program->assets, "sprites/marker.png"); #endif if (!state->marker) { state->marker = makeMarker(); state->ownsMarker = 1; } if (!state->brick || !state->floor || !state->marker) return 0; kon_fpInit(&state->fp, &state->map, 1, 13); kon_fpDebug(&state->fp, program->debug); for (i = 0; i < MARKERS; i++) state->markers[i] = spots[i]; return 1; } static void shutdown(kon_program_t *program, void *user) { state_t *state = user; (void)program; kon_freeFramebuffer(state->brick); kon_freeFramebuffer(state->floor); if (state->ownsMarker) kon_freeFramebuffer(state->marker); } int main(void) { state_t state = {0}; kon_programConfig_t config = {0}; config.title = "corridor"; config.gameWidth = 480; config.gameHeight = 270; config.windowWidth = 960; config.windowHeight = 540; config.user = &state; config.init = init; config.event = event; config.update = update; config.draw = draw; config.shutdown = shutdown; return kon_programRun(&config); }