#include "game.h" #include "sys.h" #include "audio.h" #include #include #include #define DEBUG 0 struct sdl_context { SDL_Window *window; i32 width; i32 height; SDL_Renderer *renderer; SDL_AudioDeviceID audio_device; SDL_AudioStream *streams[max_sounds]; i32 last_sound; u64 last_counter; f32 dt; }; static struct sdl_context ctx; static struct sdl_context init_sdl(i32 width, i32 height, i32 debug) { struct sdl_context ctx = {0}; ctx.width = width; ctx.height = height; SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); u64 flags = SDL_WINDOW_OPENGL; SDL_CreateWindowAndRenderer("breakout", ctx.width, ctx.height, flags, &ctx.window, &ctx.renderer); if (debug) SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); SDL_GLContext glctx = SDL_GL_CreateContext(ctx.window); SDL_GL_MakeCurrent(ctx.window, glctx); SDL_GL_SetSwapInterval(0); ctx.last_counter = SDL_GetPerformanceCounter(); glewInit(); ctx.audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, 0); if (!ctx.audio_device) die("failed to open audio device"); return ctx; } static void process_sdl_key(struct input *input, i32 scancode, i32 pressed) { switch (scancode) { case SDL_SCANCODE_ESCAPE: input->escape.current = pressed; break; case SDL_SCANCODE_RIGHT: input->right.current = pressed; break; case SDL_SCANCODE_LEFT: input->left.current = pressed; break; case SDL_SCANCODE_UP: input->up.current = pressed; break; case SDL_SCANCODE_DOWN: input->down.current = pressed; break; case SDL_SCANCODE_SPACE: input->space.current = pressed; break; case SDL_SCANCODE_RETURN: input->start.current = pressed; break; default: break; } } static void process_sdl_events(struct game *game) { SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: process_sdl_key(&game->input, event.key.scancode, event.key.down); default: break; } } } static f32 get_elapsed(u64 last_counter) { u64 frequency = SDL_GetPerformanceFrequency(); u64 current_counter = SDL_GetPerformanceCounter(); return (f32)(current_counter - last_counter) / (f32)frequency; } static void lock_framerate_sdl(struct sdl_context *ctx, i32 fps) { f32 elapsed = get_elapsed(ctx->last_counter) * 1000.0f; f32 target = 1000.0f / (f32)fps; f32 sleep = target - elapsed - 1.0f; if (sleep > 0) SDL_Delay((u32)sleep); do { elapsed = get_elapsed(ctx->last_counter) * 1000.0f; } while (elapsed < target); ctx->dt = get_elapsed(ctx->last_counter); ctx->last_counter = SDL_GetPerformanceCounter(); } #if DEBUG static void fps_info(f32 dt, i32 again) { static f32 time = 0; static i32 lastsec = 0; static f32 sumdt = 0; static i32 sumfps = 0; static f32 frame_count = 0; time += dt; sumdt += dt; sumfps += (i32)(1.0f / dt); frame_count++; i32 sec = (i32)time; if (sec != 0 && sec % again == 0) { if (lastsec != sec) { f32 mean_dt = sumdt / frame_count; f32 mean_fps = sumfps / frame_count; info("time: %d dt: %f fps: %f", sec, mean_dt, mean_fps); lastsec = sec; sumdt = 0; sumfps = 0; frame_count = 0; } } } #endif void load_wav(const char *dir, const char *filename, const char *name) { char path[512] = {0}; snprintf(path, 512, "%s/%s", (dir ? dir : "."), filename); SDL_AudioSpec spec; u8 *data; u32 size; if (!SDL_LoadWAV(path, &spec, &data, &size)) die("failed to load wav \"%s\"", path); if (ctx.last_sound >= max_sounds) { info("max sounds reached"); return; } sounds[ctx.last_sound] = (struct sound){data, size, name}; if (!(ctx.streams[ctx.last_sound] = SDL_CreateAudioStream(&spec, 0))) die("failed to create audio stream"); if (!SDL_BindAudioStream(ctx.audio_device, ctx.streams[ctx.last_sound])) die("failed to bind audio stream"); ctx.last_sound++; info("sound \"%s\" loaded successfuly", path); } void play_audio(const char *name, i32 loop) { i32 i = get_sound(name); if (i == -1) info("no audio with the name \"%s\"", name); sounds[i].loop = loop; if (SDL_GetAudioStreamQueued(ctx.streams[i])) SDL_ClearAudioStream(ctx.streams[i]); SDL_PutAudioStreamData(ctx.streams[i], sounds[i].data, sounds[i].size); } static void update_audio(void) { for (i32 i = 0; i < max_sounds; i++) { if (sounds[i].loop) { i32 queued = SDL_GetAudioStreamQueued(ctx.streams[i]); if (queued < (i32)sounds[i].size) SDL_PutAudioStreamData(ctx.streams[i], sounds[i].data, sounds[i].size); } } } i32 main(void) { ctx = init_sdl(800, 600, DEBUG); struct game game = init_game(ctx.width, ctx.height); while (game.running) { process_sdl_events(&game); process_input(&game); update_game(&game, ctx.dt); update_audio(); update_input(&game.input); lock_framerate_sdl(&ctx, 60); #if DEBUG fps_info(ctx.dt, 10); #endif render_game(game); SDL_GL_SwapWindow(ctx.window); } return 0; }