zsdl/src/main.zig

182 lines
6.9 KiB
Zig

const std = @import("std");
const sdl = @import("sdl.zig").c;
const gs = @import("./game_state.zig");
const assert = std.debug.assert;
const SCREEN_WIDTH = 640;
const SCREEN_HEIGHT = 480;
const SCREEN_FPS: f64 = 30.0;
const SCREEN_TICKS_PER_FRAME: f64 = 1000 / SCREEN_FPS;
const PHYS_UPS: f64 = 310.0;
const PHYS_TICKS_PER_UPDATE: f64 = 1000 / PHYS_UPS;
const PHYS_UPDATES_PER_RENDER: f64 = PHYS_UPS / SCREEN_FPS;
const phys_updates_per_render: u32 = @intFromFloat(PHYS_UPDATES_PER_RENDER);
const log = sdl.SDL_Log;
const GameText = @import("./text.zig").GameText;
const RGBAColor = @import("./utils/rgb_color.zig").RGBAColor;
const Timer = @import("./utils/timer.zig").Timer;
var window: *sdl.struct_SDL_Window = undefined;
// var screen_surface: *sdl.struct_SDL_Surface = undefined;
var renderer: *sdl.struct_SDL_Renderer = undefined;
const Offset = struct {
x: i32,
y: i32,
};
var img_pos: Offset = .{ .x = 0, .y = 0 };
pub fn main() !void {
errdefer |err| if (err == error.SdlError) std.log.err("SDL error: {s}", .{sdl.SDL_GetError()});
try init();
defer close();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var game_state_controller = try gs.GameStateController.init(allocator, renderer);
defer game_state_controller.deinit();
var quit = false;
var game_timer = Timer{};
game_timer.start();
var frame_time = Timer{};
frame_time.start();
var prev_frame_ms: f64 = 16.0;
var accumulator: f64 = 0.0;
var prev_game_state: ?*const gs.GameState = null;
var current_game_state: ?*const gs.GameState = undefined;
while (!quit) {
// double newTime = time();
// double frameTime = newTime - currentTime;
// if ( frameTime > 0.25 )
// frameTime = 0.25;
// currentTime = newTime;
// accumulator += frameTime;
// while ( accumulator >= dt )
// {
// previousState = currentState;
// integrate( currentState, t, dt );
// t += dt;
// accumulator -= dt;
// }
// const double alpha = accumulator / dt;
// State state = currentState * alpha +
// previousState * ( 1.0 - alpha );
// Wait for time to render screen, keep running physics
accumulator = accumulator + SCREEN_TICKS_PER_FRAME;
const total_phys_ticks: u32 = @intFromFloat(PHYS_TICKS_PER_UPDATE * PHYS_UPDATES_PER_RENDER);
const game_time_ms: u32 = @intFromFloat(game_timer.getTicks());
const phys_done_at_ticks: u32 = game_time_ms + total_phys_ticks;
var phys_ticks_used: f64 = 0;
std.debug.print("Phys ticks/upd: {d}\nAcc: {d}\nTotal phys ticks {d}ms\nPhys done at {d}ms\n", .{ PHYS_TICKS_PER_UPDATE, accumulator, total_phys_ticks, phys_done_at_ticks });
// Run a physics update
while (accumulator >= PHYS_TICKS_PER_UPDATE) {
// Control loop here
var event: sdl.SDL_Event = undefined;
while (sdl.SDL_PollEvent(&event) != 0) {
switch (event.type) {
sdl.SDL_QUIT => {
quit = true;
},
sdl.SDL_KEYDOWN => {
switch (event.key.keysym.sym) {
sdl.SDLK_RETURN => {},
else => {},
}
log("Got key event: %i\n", event.key.keysym.sym);
},
else => {},
}
}
prev_game_state = current_game_state;
current_game_state = try game_state_controller.update_tick();
const phys_ticks_this_update: f64 = current_game_state.?.phys_ticks - phys_ticks_used;
accumulator -= phys_ticks_this_update;
// std.debug.print(" PU in {d}ms ", .{phys_ticks_this_update});
// std.debug.print(" Acc:{d} - ", .{accumulator});
// TODO dont turn it into an int, take the extra phys_frames and do a partial render
// if (phys_ticks_this_update < PHYS_TICKS_PER_UPDATE) {
// const ticks: u32 = @intFromFloat(PHYS_TICKS_PER_UPDATE - phys_ticks_this_update);
// sdl.SDL_Delay(ticks);
// const ticksf: f64 = @floatFromInt(ticks);
// accumulator -= (phys_ticks_this_update + ticksf);
// } else {
// // std.debug.print("PHYSICS JANK: Physics Update took {d}ms, target: {d}ms\n", .{ phys_ticks_this_update, PHYS_TICKS_PER_UPDATE });
// accumulator -= phys_ticks_this_update;
// }
phys_ticks_used = current_game_state.?.phys_ticks;
// std.debug.print(" PhyTicks this render: {d} ", .{current_game_state.?.phys_ticks});
}
// TODO lerp together prev and current states
// Otherwise will have physics jank
// Update screen frame
std.debug.print("Took {d} phys frames\n", .{current_game_state.?.phys_updates_since_render});
try game_state_controller.render(current_game_state.?);
prev_frame_ms = frame_time.getTicks();
std.debug.print("Rendered to screen in {d}ms\n***\n***\n", .{prev_frame_ms});
frame_time.reset();
// const frame_time_ticks = frame_time.getTicks();
// if (frame_time_ticks < SCREEN_TICKS_PER_FRAME) {
// sdl.SDL_Delay(SCREEN_TICKS_PER_FRAME - frame_time_ticks);
// }
}
return;
}
fn init() !void {
// Init SDL with video subsystem flag
if (sdl.SDL_Init(sdl.SDL_INIT_VIDEO) < 0) {
log("Unable to initialize SDL: %s\n", sdl.SDL_GetError());
return error.SDLInitializationFailed;
}
const opt_window = sdl.SDL_CreateWindow("Game Window", sdl.SDL_WINDOWPOS_UNDEFINED, sdl.SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, sdl.SDL_WINDOW_SHOWN);
if (opt_window == null) {
log("Unable to initialize SDL window: %s\n", sdl.SDL_GetError());
return error.SDLInitializationFailed;
}
window = opt_window.?;
renderer = sdl.SDL_CreateRenderer(window, -1, sdl.SDL_RENDERER_ACCELERATED) orelse {
log("Unable to initialize SDL window: %s\n", sdl.SDL_GetError());
return error.SDLInitializationFailed;
};
_ = sdl.SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff);
// screen_surface = sdl.SDL_GetWindowSurface(window);
const img_flags = sdl.IMG_INIT_PNG;
if (sdl.IMG_Init(img_flags) != img_flags) {
// NOTE remember that errors with SDL_Image will be in IMG_GetError() not SDL_GetError()
log("Unable to initialize SDL Image: %s\n", sdl.IMG_GetError());
return error.SDLInitializationFailed;
}
if (sdl.TTF_Init() < 0) {
log("Unable to initialize SDL TTF: %s\n", sdl.TTF_GetError());
return error.SDLInitializationFailed;
}
}
fn close() void {
sdl.SDL_DestroyRenderer(renderer);
sdl.SDL_DestroyWindow(window);
sdl.IMG_Quit();
sdl.TTF_Quit();
sdl.SDL_Quit();
}