Leaky mess rn...
This commit is contained in:
parent
a43b46449f
commit
7716ddf26a
@ -2,30 +2,64 @@ const sdl = @import("./sdl.zig").c;
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const sf = @import("./slime_factory.zig");
|
const sf = @import("./slime_factory.zig");
|
||||||
const atils = @import("./asset_utils.zig");
|
const atils = @import("./asset_utils.zig");
|
||||||
const GameText = @import("./text.zig").GameText;
|
const text = @import("./text.zig");
|
||||||
const RGBAColor = @import("./utils/rgb_color.zig").RGBAColor;
|
const RGBAColor = @import("./utils/rgb_color.zig").RGBAColor;
|
||||||
|
const Timer = @import("./utils/timer.zig").Timer;
|
||||||
|
|
||||||
pub const GameState = struct {
|
const SCREEN_FPS: f64 = 30.0;
|
||||||
r: u8 = 0xff,
|
const SCREEN_TICKS_PER_FRAME: f64 = 1000 / SCREEN_FPS;
|
||||||
g: u8 = 0xff,
|
const PHYS_UPS: f64 = 310.0;
|
||||||
b: u8 = 0xff,
|
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);
|
||||||
|
|
||||||
|
pub const GameStateController = struct {
|
||||||
SCREEN_WIDTH: u32 = 640,
|
SCREEN_WIDTH: u32 = 640,
|
||||||
SCREEN_HEIGHT: u32 = 480,
|
SCREEN_HEIGHT: u32 = 480,
|
||||||
slime_factory: sf.SlimeFactory = undefined,
|
slime_factory_controller: sf.SlimeFactoryController = undefined,
|
||||||
|
game_text_factory_controller: text.GameTextFactoryController = undefined,
|
||||||
|
prev_game_state: *GameState = undefined,
|
||||||
|
current_game_state: *GameState = undefined,
|
||||||
renderer: *sdl.struct_SDL_Renderer = undefined,
|
renderer: *sdl.struct_SDL_Renderer = undefined,
|
||||||
|
rng: std.Random = undefined,
|
||||||
|
allocator: std.mem.Allocator = undefined,
|
||||||
|
fps_timer: Timer,
|
||||||
|
phys_timer: Timer,
|
||||||
|
frames: u32 = 0,
|
||||||
|
phys_updates_since_last_render: u16 = 0,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, renderer: *sdl.struct_SDL_Renderer) !GameState {
|
pub fn init(allocator: std.mem.Allocator, renderer: *sdl.struct_SDL_Renderer) !GameStateController {
|
||||||
var slime_factory = try sf.SlimeFactory.init(allocator, renderer);
|
var slime_factory = try sf.SlimeFactoryController.init(allocator, renderer);
|
||||||
|
const text_factory = try text.GameTextFactoryController.init(allocator);
|
||||||
|
var xosh = std.rand.DefaultPrng.init(0);
|
||||||
try slime_factory.add(.{});
|
try slime_factory.add(.{});
|
||||||
return .{ .slime_factory = slime_factory, .renderer = renderer };
|
var timer = Timer{};
|
||||||
|
timer.start();
|
||||||
|
var phys_timer = Timer{};
|
||||||
|
phys_timer.start();
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.slime_factory_controller = slime_factory,
|
||||||
|
.game_text_factory_controller = text_factory,
|
||||||
|
.renderer = renderer,
|
||||||
|
.rng = xosh.random(),
|
||||||
|
.allocator = allocator,
|
||||||
|
.fps_timer = timer,
|
||||||
|
.phys_timer = phys_timer,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *GameState) void {
|
pub fn deinit(self: *GameStateController) void {
|
||||||
self.slime_factory.deinit();
|
self.slime_factory_controller.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Physics main loop that processes variable time steps
|
// Physics main loop that processes variable time steps
|
||||||
pub fn update_tick(self: *GameState) !void {
|
pub fn update_tick(self: *GameStateController) !*const GameState {
|
||||||
|
// DEBUG random wait time
|
||||||
|
// std.debug.print("getting rng\n", .{});
|
||||||
|
// const wait_time_ms: u32 = 6; //self.rng.int(u32);
|
||||||
|
// sdl.SDL_Delay(wait_time_ms % 15);
|
||||||
|
// DEFINITELY REMOVE BEFORE PROD
|
||||||
const bg_texture = try atils.loadTexture(self.renderer, "assets/background.png");
|
const bg_texture = try atils.loadTexture(self.renderer, "assets/background.png");
|
||||||
defer sdl.SDL_DestroyTexture(bg_texture);
|
defer sdl.SDL_DestroyTexture(bg_texture);
|
||||||
|
|
||||||
@ -33,16 +67,16 @@ pub const GameState = struct {
|
|||||||
_ = sdl.SDL_RenderClear(self.renderer);
|
_ = sdl.SDL_RenderClear(self.renderer);
|
||||||
_ = sdl.SDL_RenderCopy(self.renderer, bg_texture, null, null);
|
_ = sdl.SDL_RenderCopy(self.renderer, bg_texture, null, null);
|
||||||
|
|
||||||
var text = try GameText.loadFromRenderedText("Press S to start / stop", RGBAColor.whiteSmoke().tosdl());
|
// var text = try GameText.loadFromRenderedText("Press S to start / stop", RGBAColor.whiteSmoke().tosdl());
|
||||||
try text.render(
|
// try text.render(
|
||||||
self,
|
// self,
|
||||||
.{ .x = @intCast(self.SCREEN_WIDTH - text.w), .y = 2 },
|
// .{ .x = @intCast(self.SCREEN_WIDTH - text.w), .y = 2 },
|
||||||
);
|
// );
|
||||||
text = try GameText.loadFromRenderedText("Press P to pause / unpause", RGBAColor.whiteSmoke().tosdl());
|
// text = try GameText.loadFromRenderedText("Press P to pause / unpause", RGBAColor.whiteSmoke().tosdl());
|
||||||
try text.render(
|
// try text.render(
|
||||||
self,
|
// self,
|
||||||
.{ .x = @intCast(self.SCREEN_WIDTH - text.w), .y = 24 },
|
// .{ .x = @intCast(self.SCREEN_WIDTH - text.w), .y = 24 },
|
||||||
);
|
// );
|
||||||
|
|
||||||
// var buf: [16]u8 = undefined;
|
// var buf: [16]u8 = undefined;
|
||||||
// const timer_ms = timer.getTicks();
|
// const timer_ms = timer.getTicks();
|
||||||
@ -79,11 +113,78 @@ pub const GameState = struct {
|
|||||||
// _ = sdl.SDL_UpdateWindowSurface(window);
|
// _ = sdl.SDL_UpdateWindowSurface(window);
|
||||||
// sdl.SDL_Delay(100);
|
// sdl.SDL_Delay(100);
|
||||||
|
|
||||||
self.slime_factory.render(self);
|
var game_state = try self.allocator.create(GameState);
|
||||||
|
game_state.allocator = self.allocator;
|
||||||
|
game_state.slime_factory = try self.slime_factory_controller.spawnFactory(self.allocator);
|
||||||
|
game_state.fps_timer = &self.fps_timer;
|
||||||
|
game_state.phys_ticks = @floatFromInt(self.phys_timer.getTicks());
|
||||||
|
game_state.phys_updates_since_render = self.phys_updates_since_last_render;
|
||||||
|
|
||||||
|
// self.allocator.destroy(self.prev_game_state);
|
||||||
|
self.prev_game_state = self.current_game_state;
|
||||||
|
self.current_game_state = game_state;
|
||||||
|
self.phys_updates_since_last_render += 1;
|
||||||
|
|
||||||
|
return game_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renders the current frame to screen
|
// Renders the current frame to screen
|
||||||
pub fn render(self: *GameState) void {
|
pub fn render(self: *GameStateController, game_state: *const GameState) !void {
|
||||||
_ = sdl.SDL_RenderPresent(self.renderer);
|
try self.game_text_factory_controller.addText(.{ .text = "Test new text factory", .offset = .{ .x = 15, .y = 15 } });
|
||||||
|
var buf: [8]u8 = undefined;
|
||||||
|
const fps_text = try self.getFpsText(&buf);
|
||||||
|
try self.game_text_factory_controller.addText(.{ .text = fps_text, .offset = .{ .x = 400, .y = 15 } });
|
||||||
|
|
||||||
|
try self.game_text_factory_controller.render(self.renderer, game_state);
|
||||||
|
try self.slime_factory_controller.render(self.renderer);
|
||||||
|
|
||||||
|
sdl.SDL_RenderPresent(self.renderer);
|
||||||
|
self.frames += 1;
|
||||||
|
self.phys_updates_since_last_render = 0;
|
||||||
|
self.phys_timer.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getFpsText(self: *GameStateController, buf: []u8) ![:0]const u8 {
|
||||||
|
const ticks_f: f64 = @floatFromInt(self.fps_timer.getTicks());
|
||||||
|
const frames_f: f64 = @floatFromInt(self.frames);
|
||||||
|
const avg_fps: f64 = @round(frames_f / (ticks_f / 1000.0));
|
||||||
|
return try std.fmt.bufPrintZ(buf, "{d} fps", .{avg_fps});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const GameState = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
slime_factory: sf.SlimeFactory = undefined,
|
||||||
|
fps_timer: *Timer,
|
||||||
|
phys_ticks: f64,
|
||||||
|
phys_updates_since_render: u32,
|
||||||
|
// Checks all struct fields in GameState and calls deinit
|
||||||
|
pub fn deinit(self: *const GameState) void {
|
||||||
|
const game_state_info = @typeInfo(GameState);
|
||||||
|
switch (game_state_info) {
|
||||||
|
.Struct => {
|
||||||
|
inline for (game_state_info.Struct.fields) |field| {
|
||||||
|
switch (@typeInfo(field.type)) {
|
||||||
|
.Struct => {
|
||||||
|
// const inner_struct_info = @typeInfo(field.type);
|
||||||
|
|
||||||
|
// Check if struct has `deinit` method
|
||||||
|
if (@hasDecl(field.type, "deinit")) {
|
||||||
|
const field_ptr = @field(self, field.name);
|
||||||
|
field_ptr.deinit();
|
||||||
|
std.debug.print("Deinit called on {s}\n", .{field.name});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop over fields of `GameState`
|
||||||
|
|
||||||
|
self.allocator.destroy(self);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
94
src/main.zig
94
src/main.zig
@ -4,8 +4,13 @@ const gs = @import("./game_state.zig");
|
|||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const SCREEN_WIDTH = 640;
|
const SCREEN_WIDTH = 640;
|
||||||
const SCREEN_HEIGHT = 480;
|
const SCREEN_HEIGHT = 480;
|
||||||
const SCREEN_FPS: f64 = 60.0;
|
const SCREEN_FPS: f64 = 30.0;
|
||||||
const SCREEN_TICKS_PER_FRAME: f64 = 1000 / SCREEN_FPS;
|
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 log = sdl.SDL_Log;
|
||||||
const GameText = @import("./text.zig").GameText;
|
const GameText = @import("./text.zig").GameText;
|
||||||
const RGBAColor = @import("./utils/rgb_color.zig").RGBAColor;
|
const RGBAColor = @import("./utils/rgb_color.zig").RGBAColor;
|
||||||
@ -28,36 +33,20 @@ pub fn main() !void {
|
|||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
try GameText.initFont();
|
var game_state_controller = try gs.GameStateController.init(allocator, renderer);
|
||||||
defer GameText.deinitFont();
|
defer game_state_controller.deinit();
|
||||||
|
|
||||||
var game_state = try gs.GameState.init(allocator, renderer);
|
|
||||||
defer game_state.deinit();
|
|
||||||
|
|
||||||
var quit = false;
|
var quit = false;
|
||||||
var fps_timer = Timer{};
|
|
||||||
fps_timer.start();
|
|
||||||
var frame_time = Timer{};
|
var frame_time = Timer{};
|
||||||
|
frame_time.start();
|
||||||
|
|
||||||
var prev_frame_ms: f64 = 16.0;
|
var prev_frame_ms: f64 = 16.0;
|
||||||
var accumulator: f64 = 0.0;
|
var accumulator: f64 = 0.0;
|
||||||
|
var prev_game_state: ?*const gs.GameState = null;
|
||||||
|
var current_game_state: ?*const gs.GameState = undefined;
|
||||||
|
|
||||||
while (!quit) {
|
while (!quit) {
|
||||||
frame_time.reset();
|
|
||||||
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) {
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
log("Got key event: %i\n", event.key.keysym.sym);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// double newTime = time();
|
// double newTime = time();
|
||||||
// double frameTime = newTime - currentTime;
|
// double frameTime = newTime - currentTime;
|
||||||
@ -80,17 +69,60 @@ pub fn main() !void {
|
|||||||
// State state = currentState * alpha +
|
// State state = currentState * alpha +
|
||||||
// previousState * ( 1.0 - alpha );
|
// previousState * ( 1.0 - alpha );
|
||||||
|
|
||||||
// render( state );
|
// Wait for time to render screen, keep running physics
|
||||||
accumulator = accumulator + prev_frame_ms;
|
accumulator = accumulator + SCREEN_TICKS_PER_FRAME;
|
||||||
while (accumulator >= SCREEN_TICKS_PER_FRAME) {
|
const total_phys_ticks: u32 = @intFromFloat(PHYS_TICKS_PER_UPDATE * PHYS_UPDATES_PER_RENDER);
|
||||||
try game_state.update_tick();
|
const phys_done_at_ticks: u32 = sdl.SDL_GetTicks() + total_phys_ticks;
|
||||||
accumulator = accumulator - SCREEN_TICKS_PER_FRAME;
|
var phys_ticks_used: f64 = 0;
|
||||||
std.debug.print("Tick update done. Acc: {d}\n", .{accumulator});
|
std.debug.print("phys ticks/upd: {d}, Acc: {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) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
accumulator -= PHYS_TICKS_PER_UPDATE;
|
||||||
|
} else {
|
||||||
|
std.debug.print("\nPHYSICS JANK\nPhysics 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});
|
||||||
}
|
}
|
||||||
|
|
||||||
game_state.render();
|
// 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 = @floatFromInt(frame_time.getTicks());
|
prev_frame_ms = @floatFromInt(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();
|
// const frame_time_ticks = frame_time.getTicks();
|
||||||
// if (frame_time_ticks < SCREEN_TICKS_PER_FRAME) {
|
// if (frame_time_ticks < SCREEN_TICKS_PER_FRAME) {
|
||||||
// sdl.SDL_Delay(SCREEN_TICKS_PER_FRAME - frame_time_ticks);
|
// sdl.SDL_Delay(SCREEN_TICKS_PER_FRAME - frame_time_ticks);
|
||||||
|
@ -18,11 +18,11 @@ pub const Slime = struct {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SlimeFactory = struct {
|
pub const SlimeFactoryController = struct {
|
||||||
slimes: std.ArrayList(Slime) = undefined,
|
slimes: std.ArrayList(Slime) = undefined,
|
||||||
slime_sprite_sheet_texture: *sdl.struct_SDL_Texture = undefined,
|
slime_sprite_sheet_texture: *sdl.struct_SDL_Texture = undefined,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, renderer: *sdl.struct_SDL_Renderer) !SlimeFactory {
|
pub fn init(allocator: std.mem.Allocator, renderer: *sdl.struct_SDL_Renderer) !SlimeFactoryController {
|
||||||
const texture = try atils.loadTexture(renderer, slime_sprite_sheet_texture_path);
|
const texture = try atils.loadTexture(renderer, slime_sprite_sheet_texture_path);
|
||||||
const val = sdl.SDL_SetTextureBlendMode(texture, sdl.SDL_BLENDMODE_BLEND);
|
const val = sdl.SDL_SetTextureBlendMode(texture, sdl.SDL_BLENDMODE_BLEND);
|
||||||
if (val != 0) {
|
if (val != 0) {
|
||||||
@ -35,33 +35,61 @@ pub const SlimeFactory = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(sf: *SlimeFactory) void {
|
pub fn deinit(sf: *SlimeFactoryController) void {
|
||||||
sdl.SDL_DestroyTexture(sf.slime_sprite_sheet_texture);
|
sdl.SDL_DestroyTexture(sf.slime_sprite_sheet_texture);
|
||||||
sf.slimes.deinit();
|
sf.slimes.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(sf: *SlimeFactory, slime: Slime) !void {
|
pub fn add(sf: *SlimeFactoryController, slime: Slime) !void {
|
||||||
try sf.slimes.append(slime);
|
try sf.slimes.append(slime);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(sf: *SlimeFactory, game_state: *GameState) void {
|
pub fn render(sf: *SlimeFactoryController, renderer: *sdl.SDL_Renderer) !void {
|
||||||
if (sf.slimes.items.len == 0) return;
|
if (sf.slimes.items.len == 0) return;
|
||||||
|
|
||||||
const slime_color = sf.slimes.items[0].color;
|
const slime_color = sf.slimes.items[0].color;
|
||||||
|
|
||||||
_ = sdl.SDL_SetTextureColorMod(
|
var res = sdl.SDL_SetTextureColorMod(
|
||||||
sf.slime_sprite_sheet_texture,
|
sf.slime_sprite_sheet_texture,
|
||||||
slime_color.r,
|
slime_color.r,
|
||||||
slime_color.g,
|
slime_color.g,
|
||||||
slime_color.b,
|
slime_color.b,
|
||||||
);
|
);
|
||||||
_ = sdl.SDL_SetTextureAlphaMod(sf.slime_sprite_sheet_texture, slime_color.a);
|
if (res > 0) {
|
||||||
|
sdl.SDL_Log("Error setting slime texture color: %s\n", sdl.SDL_GetError());
|
||||||
|
return error.FailedToRenderSlimes;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = sdl.SDL_SetTextureAlphaMod(sf.slime_sprite_sheet_texture, slime_color.a);
|
||||||
|
if (res > 0) {
|
||||||
|
sdl.SDL_Log("Error setting slime texture alpha: %s\n", sdl.SDL_GetError());
|
||||||
|
return error.FailedToRenderSlimes;
|
||||||
|
}
|
||||||
for (sf.slimes.items, 0..) |slime, s_idx| {
|
for (sf.slimes.items, 0..) |slime, s_idx| {
|
||||||
const sprite_frame_x_dim: u16 = slime.f * 64;
|
const sprite_frame_x_dim: u16 = slime.f * 64;
|
||||||
const sprite_frame_rect: sdl.struct_SDL_Rect = .{ .x = sprite_frame_x_dim, .y = 0, .w = 64, .h = 64 };
|
const sprite_frame_rect: sdl.struct_SDL_Rect = .{ .x = sprite_frame_x_dim, .y = 0, .w = 64, .h = 64 };
|
||||||
const dest_rect: sdl.struct_SDL_Rect = .{ .x = slime.x, .y = slime.y, .w = 64, .h = 64 };
|
const dest_rect: sdl.struct_SDL_Rect = .{ .x = slime.x, .y = slime.y, .w = 64, .h = 64 };
|
||||||
_ = sdl.SDL_RenderCopy(game_state.renderer, sf.slime_sprite_sheet_texture, &sprite_frame_rect, &dest_rect);
|
if (sdl.SDL_RenderCopy(renderer, sf.slime_sprite_sheet_texture, &sprite_frame_rect, &dest_rect) > 0) {
|
||||||
|
sdl.SDL_Log("Error copying slime texture to renderer: %s\n", sdl.SDL_GetError());
|
||||||
|
return error.FailedToRenderSlimes;
|
||||||
|
}
|
||||||
sf.slimes.items[s_idx].f = (sf.slimes.items[s_idx].f + 1) % sf.slimes.items[s_idx].total_frames;
|
sf.slimes.items[s_idx].f = (sf.slimes.items[s_idx].f + 1) % sf.slimes.items[s_idx].total_frames;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn spawnFactory(sfc: *SlimeFactoryController, allocator: std.mem.Allocator) !SlimeFactory {
|
||||||
|
const slimes = sfc.slimes.items;
|
||||||
|
const slimes_copy = try allocator.alloc(Slime, slimes.len);
|
||||||
|
errdefer allocator.free(slimes_copy);
|
||||||
|
|
||||||
|
std.mem.copyForwards(Slime, slimes_copy, slimes);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.slimes = &slimes_copy,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SlimeFactory = struct {
|
||||||
|
slimes: *const []Slime,
|
||||||
};
|
};
|
||||||
|
160
src/text.zig
160
src/text.zig
@ -2,38 +2,109 @@ const sdl = @import("./sdl.zig").c;
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const GameState = @import("./game_state.zig").GameState;
|
const GameState = @import("./game_state.zig").GameState;
|
||||||
const Offset = @import("./utils/offset.zig").Offset;
|
const Offset = @import("./utils/offset.zig").Offset;
|
||||||
|
const RGBAColor = @import("./utils/rgb_color.zig").RGBAColor;
|
||||||
const FONT_SIZE = 24;
|
const FONT_SIZE = 24;
|
||||||
|
|
||||||
var font: ?*sdl.TTF_Font = null;
|
var font: ?*sdl.TTF_Font = null;
|
||||||
var text_texture: ?*sdl.SDL_Texture = null;
|
|
||||||
var text_init = false;
|
var text_init = false;
|
||||||
|
|
||||||
|
pub const GameTextFactoryController = struct {
|
||||||
|
texts: std.ArrayList(GameText),
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator) !GameTextFactoryController {
|
||||||
|
try initFont();
|
||||||
|
return .{
|
||||||
|
.texts = std.ArrayList(GameText).init(allocator),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initFont() !void {
|
||||||
|
const loaded_font = sdl.TTF_OpenFont("./assets/fonts/DepartureMonoNF-Regular.ttf", FONT_SIZE) orelse {
|
||||||
|
sdl.SDL_Log("Error loading ttf font: %s\n", sdl.TTF_GetError());
|
||||||
|
return error.FailedToLoadFont;
|
||||||
|
};
|
||||||
|
|
||||||
|
font = loaded_font;
|
||||||
|
text_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *GameTextFactoryController) void {
|
||||||
|
self.texts.deinit();
|
||||||
|
deinitFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinitFont() void {
|
||||||
|
sdl.TTF_CloseFont(font);
|
||||||
|
text_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addText(self: *GameTextFactoryController, text_ctx: GameTextContext) !void {
|
||||||
|
try self.texts.append(GameText.initFromContext(text_ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(self: *GameTextFactoryController, renderer: *sdl.SDL_Renderer, game_state: *const GameState) !void {
|
||||||
|
_ = game_state;
|
||||||
|
for (self.texts.items) |text| {
|
||||||
|
const c_text: [*c]const u8 = @ptrCast(text.text);
|
||||||
|
// TODO dont create new surface if unchanged
|
||||||
|
const text_surface: [*c]sdl.SDL_Surface = sdl.TTF_RenderText_Solid(font.?, c_text, text.color) orelse {
|
||||||
|
sdl.SDL_Log("Error loading text surface: %s\n", sdl.SDL_GetError());
|
||||||
|
return error.FailedToRenderSurface;
|
||||||
|
};
|
||||||
|
defer sdl.SDL_FreeSurface(text_surface);
|
||||||
|
|
||||||
|
if (!text_init) {
|
||||||
|
return error.TextNotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO dont create new texture if unchanged
|
||||||
|
const text_texture = sdl.SDL_CreateTextureFromSurface(renderer, text_surface) orelse {
|
||||||
|
sdl.SDL_Log("Error loading text texture: %s\n", sdl.SDL_GetError());
|
||||||
|
return error.FailedToRenderTexture;
|
||||||
|
};
|
||||||
|
defer sdl.SDL_DestroyTexture(text_texture);
|
||||||
|
|
||||||
|
const w: c_int = @intCast(text_surface.*.w);
|
||||||
|
const h: c_int = @intCast(text_surface.*.h);
|
||||||
|
const srcr = sdl.SDL_Rect{ .x = 0, .y = 0, .w = w, .h = h };
|
||||||
|
const destr = sdl.SDL_Rect{ .x = @intCast(text.offset.x), .y = @intCast(text.offset.y), .w = w, .h = h };
|
||||||
|
_ = sdl.SDL_RenderCopy(renderer, text_texture, &srcr, &destr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawnFactory(self: *GameTextFactoryController, allocator: std.mem.Allocator) void {
|
||||||
|
const texts = self.texts.items;
|
||||||
|
var texts_copy = try allocator.alloc(GameText, texts.len);
|
||||||
|
errdefer allocator.free(texts_copy);
|
||||||
|
|
||||||
|
std.mem.copyForwards(GameText, texts_copy, texts);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.texts = &texts_copy,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const GameTextFactory = struct {
|
||||||
|
texts: []GameText,
|
||||||
|
text_surfaces: [*c]sdl.SDL_Surface,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const GameTextContext = struct {
|
||||||
|
text: [*:0]const u8,
|
||||||
|
color: sdl.SDL_Color = RGBAColor.whiteSmoke().tosdl(),
|
||||||
|
// TODO Constraints??
|
||||||
|
offset: Offset = .{ .x = 0, .y = 0 },
|
||||||
|
};
|
||||||
|
|
||||||
pub const GameText = struct {
|
pub const GameText = struct {
|
||||||
text: [*:0]const u8,
|
text: [*:0]const u8,
|
||||||
color: sdl.SDL_Color,
|
color: sdl.SDL_Color,
|
||||||
surface: *sdl.SDL_Surface,
|
offset: Offset,
|
||||||
w: u32,
|
|
||||||
h: u32,
|
|
||||||
|
|
||||||
pub fn loadFromRenderedText(text: [*:0]const u8, color: sdl.SDL_Color) !GameText {
|
// pub fn deinit(self: *GameText) void {
|
||||||
const c_text: [*c]const u8 = @ptrCast(text);
|
// sdl.SDL_FreeSurface(self.text_surface);
|
||||||
const text_surface: [*c]sdl.SDL_Surface = sdl.TTF_RenderText_Solid(font.?, c_text, color) orelse {
|
// }
|
||||||
sdl.SDL_Log("Error loading text surface: %s\n", sdl.SDL_GetError());
|
|
||||||
return error.FailedToRenderSurface;
|
|
||||||
};
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.surface = text_surface,
|
|
||||||
.text = text,
|
|
||||||
.color = color,
|
|
||||||
.w = @intCast(text_surface.*.w),
|
|
||||||
.h = @intCast(text_surface.*.h),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *GameText) void {
|
|
||||||
sdl.SDL_FreeSurface(self.text_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn setBlendMode(self: *GameText, blend: sdl.SDL_BlendMode) !void {
|
pub fn setBlendMode(self: *GameText, blend: sdl.SDL_BlendMode) !void {
|
||||||
_ = self;
|
_ = self;
|
||||||
@ -47,44 +118,11 @@ pub const GameText = struct {
|
|||||||
return error.Unimplemented;
|
return error.Unimplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(
|
pub fn initFromContext(ctx: GameTextContext) GameText {
|
||||||
self: *GameText,
|
return .{
|
||||||
game_state: *GameState,
|
.text = ctx.text,
|
||||||
offset: Offset,
|
.color = ctx.color,
|
||||||
) !void {
|
.offset = ctx.offset,
|
||||||
if (!text_init) {
|
|
||||||
return error.TextNotInitialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text_texture != null) {
|
|
||||||
sdl.SDL_DestroyTexture(text_texture);
|
|
||||||
text_texture = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO dont create new texture if unchanged
|
|
||||||
text_texture = sdl.SDL_CreateTextureFromSurface(game_state.renderer, self.surface) orelse {
|
|
||||||
sdl.SDL_Log("Error loading text texture: %s\n", sdl.SDL_GetError());
|
|
||||||
return error.FailedToRenderTexture;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const srcr = sdl.SDL_Rect{ .x = 0, .y = 0, .w = @intCast(self.w), .h = @intCast(self.h) };
|
|
||||||
const destr = sdl.SDL_Rect{ .x = @intCast(offset.x), .y = @intCast(offset.y), .w = @intCast(self.w), .h = @intCast(self.h) };
|
|
||||||
_ = sdl.SDL_RenderCopy(game_state.renderer, text_texture, &srcr, &destr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initFont() !void {
|
|
||||||
const loaded_font = sdl.TTF_OpenFont("./assets/fonts/DepartureMonoNF-Regular.ttf", FONT_SIZE) orelse {
|
|
||||||
sdl.SDL_Log("Error loading ttf font: %s\n", sdl.TTF_GetError());
|
|
||||||
return error.FailedToLoadFont;
|
|
||||||
};
|
|
||||||
|
|
||||||
font = loaded_font;
|
|
||||||
text_init = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinitFont() void {
|
|
||||||
sdl.SDL_DestroyTexture(text_texture);
|
|
||||||
sdl.TTF_CloseFont(font);
|
|
||||||
text_init = false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
pub const Offset = struct {
|
pub const Offset = struct {
|
||||||
x: i32,
|
x: i32 = 0,
|
||||||
y: i32,
|
y: i32 = 0,
|
||||||
};
|
};
|
||||||
|
@ -41,6 +41,7 @@ pub const Timer = struct {
|
|||||||
self.pause_ticks_ms = 0;
|
self.pause_ticks_ms = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets the current tick/ms of the timer
|
||||||
pub fn getTicks(self: *Timer) u32 {
|
pub fn getTicks(self: *Timer) u32 {
|
||||||
switch (self.status) {
|
switch (self.status) {
|
||||||
.stopped => {
|
.stopped => {
|
||||||
|
Loading…
Reference in New Issue
Block a user