From d6e4575a7dff8612026b3eb59ffe0556dd6b79aa Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Mon, 19 Jan 2026 23:11:43 +0200 Subject: [PATCH] undo input system merge --- src/assets.zig | 23 +- src/engine/input.zig | 239 ++++++++++++++++++++ src/engine/root.zig | 507 ++++++++++++++----------------------------- src/game.zig | 46 ++-- src/main.zig | 5 +- 5 files changed, 443 insertions(+), 377 deletions(-) create mode 100644 src/engine/input.zig diff --git a/src/assets.zig b/src/assets.zig index de1ce1d..8ea3e31 100644 --- a/src/assets.zig +++ b/src/assets.zig @@ -5,8 +5,6 @@ const Engine = @import("./engine/root.zig"); const Gfx = Engine.Graphics; const Audio = Engine.Audio; -const STBRectPack = @import("stb_rect_pack"); - const Assets = @This(); const FontName = enum { @@ -17,21 +15,30 @@ const FontName = enum { const EnumArray = std.EnumArray(FontName, Gfx.Font.Id); }; -pub var font_id: FontName.EnumArray = undefined; -pub var wood01: Audio.Data.Id = undefined; +font_id: FontName.EnumArray, -pub fn init() !void { - font_id = .init(.{ +wood01: Audio.Data.Id, + +pub fn init(gpa: std.mem.Allocator) !Assets { + _ = gpa; // autofix + const font_id_array: FontName.EnumArray = .init(.{ .regular = try Gfx.addFont("regular", @embedFile("assets/roboto-font/Roboto-Regular.ttf")), .bold = try Gfx.addFont("bold", @embedFile("assets/roboto-font/Roboto-Bold.ttf")), .italic = try Gfx.addFont("italic", @embedFile("assets/roboto-font/Roboto-Italic.ttf")), }); - wood01 = try Audio.load(.{ + const wood01 = try Audio.load(.{ .format = .vorbis, .data = @embedFile("assets/wood01.ogg"), }); + + return Assets{ + .font_id = font_id_array, + .wood01 = wood01 + }; } -pub fn deinit() void { +pub fn deinit(self: *Assets, gpa: std.mem.Allocator) void { + _ = self; // autofix + _ = gpa; // autofix } diff --git a/src/engine/input.zig b/src/engine/input.zig new file mode 100644 index 0000000..0cf6518 --- /dev/null +++ b/src/engine/input.zig @@ -0,0 +1,239 @@ +const std = @import("std"); +const sokol = @import("sokol"); + +const Engine = @import("./root.zig"); +const Nanoseconds = Engine.Nanoseconds; + +const Math = @import("./math.zig"); +const Vec2 = Math.Vec2; + +const Input = @This(); + +pub const KeyCode = enum(std.math.IntFittingRange(0, sokol.app.max_keycodes-1)) { + SPACE = 32, + APOSTROPHE = 39, + COMMA = 44, + MINUS = 45, + PERIOD = 46, + SLASH = 47, + _0 = 48, + _1 = 49, + _2 = 50, + _3 = 51, + _4 = 52, + _5 = 53, + _6 = 54, + _7 = 55, + _8 = 56, + _9 = 57, + SEMICOLON = 59, + EQUAL = 61, + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + LEFT_BRACKET = 91, + BACKSLASH = 92, + RIGHT_BRACKET = 93, + GRAVE_ACCENT = 96, + WORLD_1 = 161, + WORLD_2 = 162, + ESCAPE = 256, + ENTER = 257, + TAB = 258, + BACKSPACE = 259, + INSERT = 260, + DELETE = 261, + RIGHT = 262, + LEFT = 263, + DOWN = 264, + UP = 265, + PAGE_UP = 266, + PAGE_DOWN = 267, + HOME = 268, + END = 269, + CAPS_LOCK = 280, + SCROLL_LOCK = 281, + NUM_LOCK = 282, + PRINT_SCREEN = 283, + PAUSE = 284, + F1 = 290, + F2 = 291, + F3 = 292, + F4 = 293, + F5 = 294, + F6 = 295, + F7 = 296, + F8 = 297, + F9 = 298, + F10 = 299, + F11 = 300, + F12 = 301, + F13 = 302, + F14 = 303, + F15 = 304, + F16 = 305, + F17 = 306, + F18 = 307, + F19 = 308, + F20 = 309, + F21 = 310, + F22 = 311, + F23 = 312, + F24 = 313, + F25 = 314, + KP_0 = 320, + KP_1 = 321, + KP_2 = 322, + KP_3 = 323, + KP_4 = 324, + KP_5 = 325, + KP_6 = 326, + KP_7 = 327, + KP_8 = 328, + KP_9 = 329, + KP_DECIMAL = 330, + KP_DIVIDE = 331, + KP_MULTIPLY = 332, + KP_SUBTRACT = 333, + KP_ADD = 334, + KP_ENTER = 335, + KP_EQUAL = 336, + LEFT_SHIFT = 340, + LEFT_CONTROL = 341, + LEFT_ALT = 342, + LEFT_SUPER = 343, + RIGHT_SHIFT = 344, + RIGHT_CONTROL = 345, + RIGHT_ALT = 346, + RIGHT_SUPER = 347, + MENU = 348, +}; + +pub const KeyState = struct { + down: bool, + pressed: bool, + released: bool, + down_duration: ?f64, + + pub const RepeatOptions = struct { + first_at: f64 = 0, + period: f64 + }; + + pub fn repeat(self: KeyState, last_repeat_at: *?f64, opts: RepeatOptions) bool { + if (!self.down) { + last_repeat_at.* = null; + return false; + } + + const down_duration = self.down_duration.?; + if (last_repeat_at.* != null) { + if (down_duration >= last_repeat_at.*.? + opts.period) { + last_repeat_at.* = last_repeat_at.*.? + opts.period; + return true; + } + } else { + if (down_duration >= opts.first_at) { + last_repeat_at.* = opts.first_at; + return true; + } + } + + return false; + } +}; + +pub const Mouse = struct { + pub const Button = enum { + left, + right, + middle, + + pub fn fromSokol(mouse_button: sokol.app.Mousebutton) ?Button { + return switch(mouse_button) { + .LEFT => Button.left, + .RIGHT => Button.right, + .MIDDLE => Button.middle, + else => null + }; + } + }; + + position: ?Vec2, + buttons: std.EnumSet(Button), + + pub const empty = Mouse{ + .position = null, + .buttons = .initEmpty() + }; +}; + +down_keys: std.EnumSet(KeyCode), +pressed_keys: std.EnumSet(KeyCode), +released_keys: std.EnumSet(KeyCode), +pressed_keys_at: std.EnumMap(KeyCode, Nanoseconds), + +mouse: Mouse, + +pub const empty = Input{ + .down_keys = .initEmpty(), + .pressed_keys = .initEmpty(), + .released_keys = .initEmpty(), + .pressed_keys_at = .init(.{}), + .mouse = .empty +}; + +pub fn isKeyDown(self: *Input, key_code: KeyCode) bool { + return self.down_keys.contains(key_code); +} + +pub fn getKeyDownDuration(self: *Input, frame: Engine.Frame, key_code: KeyCode) ?f64 { + if (!self.isKeyDown(key_code)) { + return null; + } + + const pressed_at_ns = self.pressed_keys_at.get(key_code).?; + const duration_ns = frame.time_ns - pressed_at_ns; + + return @as(f64, @floatFromInt(duration_ns)) / std.time.ns_per_s; +} + +pub fn isKeyPressed(self: *Input, key_code: KeyCode) bool { + return self.pressed_keys.contains(key_code); +} + +pub fn isKeyReleased(self: *Input, key_code: KeyCode) bool { + return self.released_keys.contains(key_code); +} + +pub fn getKeyState(self: *Input, frame: Engine.Frame, key_code: KeyCode) KeyState { + return KeyState{ + .down = self.isKeyDown(key_code), + .released = self.isKeyReleased(key_code), + .pressed = self.isKeyPressed(key_code), + .down_duration = self.getKeyDownDuration(frame, key_code) + }; +} diff --git a/src/engine/root.zig b/src/engine/root.zig index 28ddb64..1a7bfe2 100644 --- a/src/engine/root.zig +++ b/src/engine/root.zig @@ -8,6 +8,8 @@ const sapp = sokol.app; pub const Math = @import("./math.zig"); pub const Vec2 = Math.Vec2; +pub const Input = @import("./input.zig"); + const ScreenScalar = @import("./screen_scaler.zig"); pub const imgui = @import("./imgui.zig"); pub const Graphics = @import("./graphics.zig"); @@ -25,240 +27,43 @@ const Engine = @This(); pub const Nanoseconds = u64; -const Event = union(enum) { +pub const Event = union(enum) { mouse_pressed: struct { - button: Mouse.Button, + button: Input.Mouse.Button, position: Vec2, }, mouse_released: struct { - button: Mouse.Button, + button: Input.Mouse.Button, position: Vec2, }, mouse_move: Vec2, mouse_enter: Vec2, mouse_leave, mouse_scroll: Vec2, - key_pressed: struct { code: KeyCode, repeat: bool }, - key_released: KeyCode, + key_pressed: struct { + code: Input.KeyCode, + repeat: bool + }, + key_released: Input.KeyCode, window_resize, char: u21, }; -pub const KeyCode = enum(std.math.IntFittingRange(0, sokol.app.max_keycodes-1)) { - SPACE = 32, - APOSTROPHE = 39, - COMMA = 44, - MINUS = 45, - PERIOD = 46, - SLASH = 47, - _0 = 48, - _1 = 49, - _2 = 50, - _3 = 51, - _4 = 52, - _5 = 53, - _6 = 54, - _7 = 55, - _8 = 56, - _9 = 57, - SEMICOLON = 59, - EQUAL = 61, - A = 65, - B = 66, - C = 67, - D = 68, - E = 69, - F = 70, - G = 71, - H = 72, - I = 73, - J = 74, - K = 75, - L = 76, - M = 77, - N = 78, - O = 79, - P = 80, - Q = 81, - R = 82, - S = 83, - T = 84, - U = 85, - V = 86, - W = 87, - X = 88, - Y = 89, - Z = 90, - LEFT_BRACKET = 91, - BACKSLASH = 92, - RIGHT_BRACKET = 93, - GRAVE_ACCENT = 96, - WORLD_1 = 161, - WORLD_2 = 162, - ESCAPE = 256, - ENTER = 257, - TAB = 258, - BACKSPACE = 259, - INSERT = 260, - DELETE = 261, - RIGHT = 262, - LEFT = 263, - DOWN = 264, - UP = 265, - PAGE_UP = 266, - PAGE_DOWN = 267, - HOME = 268, - END = 269, - CAPS_LOCK = 280, - SCROLL_LOCK = 281, - NUM_LOCK = 282, - PRINT_SCREEN = 283, - PAUSE = 284, - F1 = 290, - F2 = 291, - F3 = 292, - F4 = 293, - F5 = 294, - F6 = 295, - F7 = 296, - F8 = 297, - F9 = 298, - F10 = 299, - F11 = 300, - F12 = 301, - F13 = 302, - F14 = 303, - F15 = 304, - F16 = 305, - F17 = 306, - F18 = 307, - F19 = 308, - F20 = 309, - F21 = 310, - F22 = 311, - F23 = 312, - F24 = 313, - F25 = 314, - KP_0 = 320, - KP_1 = 321, - KP_2 = 322, - KP_3 = 323, - KP_4 = 324, - KP_5 = 325, - KP_6 = 326, - KP_7 = 327, - KP_8 = 328, - KP_9 = 329, - KP_DECIMAL = 330, - KP_DIVIDE = 331, - KP_MULTIPLY = 332, - KP_SUBTRACT = 333, - KP_ADD = 334, - KP_ENTER = 335, - KP_EQUAL = 336, - LEFT_SHIFT = 340, - LEFT_CONTROL = 341, - LEFT_ALT = 342, - LEFT_SUPER = 343, - RIGHT_SHIFT = 344, - RIGHT_CONTROL = 345, - RIGHT_ALT = 346, - RIGHT_SUPER = 347, - MENU = 348, +pub const Frame = struct { + time_ns: Nanoseconds, + dt_ns: Nanoseconds, + dt: f32, + input: *Input }; -pub const KeyState = struct { - down: bool, - pressed: bool, - released: bool, - down_duration: ?f64, +allocator: std.mem.Allocator, +started_at: std.time.Instant, +mouse_inside: bool, +last_frame_at: Nanoseconds, +input: Input, - pub const RepeatOptions = struct { - first_at: f64 = 0, - period: f64 - }; - - pub fn repeat(self: KeyState, last_repeat_at: *?f64, opts: RepeatOptions) bool { - if (!self.down) { - last_repeat_at.* = null; - return false; - } - - const down_duration = self.down_duration.?; - if (last_repeat_at.* != null) { - if (down_duration >= last_repeat_at.*.? + opts.period) { - last_repeat_at.* = last_repeat_at.*.? + opts.period; - return true; - } - } else { - if (down_duration >= opts.first_at) { - last_repeat_at.* = opts.first_at; - return true; - } - } - - return false; - } -}; - -pub const Mouse = struct { - pub const Button = enum { - left, - right, - middle, - - pub fn fromSokol(mouse_button: sokol.app.Mousebutton) ?Button { - return switch(mouse_button) { - .LEFT => Button.left, - .RIGHT => Button.right, - .MIDDLE => Button.middle, - else => null - }; - } - }; - - position: ?Vec2, - buttons: std.EnumSet(Button), - - pub const empty = Mouse{ - .position = null, - .buttons = .initEmpty() - }; -}; - -const State = struct { - const Input = struct { - down_keys: std.EnumSet(KeyCode), - pressed_keys: std.EnumSet(KeyCode), - released_keys: std.EnumSet(KeyCode), - pressed_keys_at: std.EnumMap(KeyCode, Nanoseconds), - mouse: Mouse, - - pub const empty = Input{ - .down_keys = .initEmpty(), - .pressed_keys = .initEmpty(), - .released_keys = .initEmpty(), - .pressed_keys_at = .init(.{}), - .mouse = .empty - }; - }; - - allocator: std.mem.Allocator, - started_at: std.time.Instant, - last_frame_at_ns: Nanoseconds, - frame_delta_ns: Nanoseconds = 0, - - // Input - input: Input = .empty, - mouse_inside: bool = false, - - // Graphics - canvas_size: Vec2 = .init(100, 100), - - // Audio -}; - -var state: State = undefined; +game: Game, +assets: Assets, const RunOptions = struct { window_title: [*:0]const u8 = "Game", @@ -266,28 +71,29 @@ const RunOptions = struct { window_height: u31 = 480, }; -pub fn run(opts: RunOptions) !void { +pub fn run(self: *Engine, opts: RunOptions) !void { + self.* = Engine{ + .allocator = undefined, + .started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"), + .input = .empty, + .mouse_inside = false, + .last_frame_at = 0, + .assets = undefined, + .game = undefined + }; + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; defer _ = debug_allocator.deinit(); // TODO: Use tracy TracingAllocator - var allocator: std.mem.Allocator = undefined; if (builtin.cpu.arch.isWasm()) { - allocator = std.heap.wasm_allocator; + self.allocator = std.heap.wasm_allocator; } else if (builtin.mode == .Debug) { - allocator = debug_allocator.allocator(); + self.allocator = debug_allocator.allocator(); } else { - allocator = std.heap.smp_allocator; + self.allocator = std.heap.smp_allocator; } - state = State{ - .allocator = allocator, - .started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"), - .input = .empty, - .mouse_inside = false, - .last_frame_at_ns = 0, - }; - tracy.setThreadName("Main"); if (builtin.os.tag == .linux) { @@ -297,8 +103,6 @@ pub fn run(opts: RunOptions) !void { .flags = std.posix.SA.RESTART, }; std.posix.sigaction(std.posix.SIG.INT, &sa, null); - } else { - // TODO: Windows Ctrl+C handler } // TODO: Don't hard code icon path, allow changing through options @@ -317,10 +121,11 @@ pub fn run(opts: RunOptions) !void { }; sapp.run(.{ - .init_cb = sokolInitCallback, - .frame_cb = sokolFrameCallback, - .cleanup_cb = sokolCleanupCallback, - .event_cb = sokolEventCallback, + .init_userdata_cb = sokolInitCallback, + .frame_userdata_cb = sokolFrameCallback, + .cleanup_userdata_cb = sokolCleanupCallback, + .event_userdata_cb = sokolEventCallback, + .user_data = self, .width = opts.window_width, .height = opts.window_height, .icon = icon, @@ -332,90 +137,45 @@ pub fn run(opts: RunOptions) !void { }); } -pub fn isKeyDown(key_code: KeyCode) bool { - const input = &state.input; - return input.down_keys.contains(key_code); -} - -pub fn getKeyDownDuration(frame: Engine.Frame, key_code: KeyCode) ?f64 { - if (!isKeyDown(key_code)) { - return null; - } - - const input = &state.input; - const pressed_at_ns = input.pressed_keys_at.get(key_code).?; - const duration_ns = frame.time_ns - pressed_at_ns; - - return @as(f64, @floatFromInt(duration_ns)) / std.time.ns_per_s; -} - -pub fn isKeyPressed(key_code: KeyCode) bool { - const input = &state.input; - return input.pressed_keys.contains(key_code); -} - -pub fn isKeyReleased(key_code: KeyCode) bool { - const input = &state.input; - return input.released_keys.contains(key_code); -} - -pub fn getKeyState(key_code: KeyCode) KeyState { - return KeyState{ - .down = isKeyDown(key_code), - .released = isKeyReleased(key_code), - .pressed = isKeyPressed(key_code), - .down_duration = getKeyDownDuration(key_code) - }; -} - -pub fn timePassed() Nanoseconds { - const now = std.time.Instant.now() catch @panic("Instant.now() unsupported"); - return now.since(state.started_at); -} - -pub fn deltaTime() f32 { - return @as(f32, @floatFromInt(state.frame_delta_ns)) / std.time.ns_per_s; -} - -pub fn getCanvasSize() Vec2 { - return state.canvas_size; -} - -fn sokolInit() !void { - const zone = tracy.initZone(@src(), .{}); +fn sokolInit(self: *Engine) !void { + const zone = tracy.initZone(@src(), .{ }); defer zone.deinit(); - try Gfx.init(.{ .allocator = state.allocator, .logger = .{ .func = sokolLogCallback }, .imgui_font = .{ - .ttf_data = @embedFile("../assets/roboto-font/Roboto-Regular.ttf"), - } }); + try Gfx.init(.{ + .allocator = self.allocator, + .logger = .{ .func = sokolLogCallback }, + .imgui_font = .{ + .ttf_data = @embedFile("../assets/roboto-font/Roboto-Regular.ttf"), + } + }); try Audio.init(.{ - .allocator = state.allocator, + .allocator = self.allocator, .logger = .{ .func = sokolLogCallback }, }); - try Assets.init(); - try Game.init(); + self.assets = try Assets.init(self.allocator); + self.game = try Game.init(self.allocator, &self.assets); } -fn sokolCleanup() void { - const zone = tracy.initZone(@src(), .{}); +fn sokolCleanup(self: *Engine) void { + const zone = tracy.initZone(@src(), .{ }); defer zone.deinit(); Audio.deinit(); - Game.deinit(); - Assets.deinit(); + self.game.deinit(); + self.assets.deinit(self.allocator); Gfx.deinit(); } -fn sokolFrame() !void { +fn sokolFrame(self: *Engine) !void { tracy.frameMark(); - const time_passed = timePassed(); - defer state.last_frame_at_ns = time_passed; - state.frame_delta_ns = time_passed - state.last_frame_at_ns; + const time_passed = self.timePassed(); + defer self.last_frame_at = time_passed; + const dt_ns = time_passed - self.last_frame_at; - const zone = tracy.initZone(@src(), .{}); + const zone = tracy.initZone(@src(), .{ }); defer zone.deinit(); Gfx.beginFrame(); @@ -423,26 +183,37 @@ fn sokolFrame() !void { { const window_size: Vec2 = .init(sapp.widthf(), sapp.heightf()); - const ctx = ScreenScalar.push(window_size, state.canvas_size); + const ctx = ScreenScalar.push(window_size, self.game.canvas_size); defer ctx.pop(); Graphics.font_resolution_scale = ctx.scale; - try Game.tick(); + try self.game.tick(Frame{ + .time_ns = time_passed, + .dt_ns = dt_ns, + .dt = @as(f32, @floatFromInt(dt_ns)) / std.time.ns_per_s, + .input = &self.input + }); } - try Game.debug(); + try self.game.debug(); - state.input.pressed_keys = .initEmpty(); - state.input.released_keys = .initEmpty(); + self.input.pressed_keys = .initEmpty(); + self.input.released_keys = .initEmpty(); } -fn processEvent(e: Event) !void { - const input = &state.input; +fn timePassed(self: *Engine) Nanoseconds { + const now = std.time.Instant.now() catch @panic("Instant.now() unsupported"); + return now.since(self.started_at); +} + +fn event(self: *Engine, e: Event) !void { + const input = &self.input; + switch (e) { .key_pressed => |opts| { if (!opts.repeat) { - input.pressed_keys_at.put(opts.code, timePassed()); + input.pressed_keys_at.put(opts.code, self.timePassed()); input.pressed_keys.insert(opts.code); input.down_keys.insert(opts.code); } @@ -476,28 +247,32 @@ fn processEvent(e: Event) !void { input.mouse.position = opts.position; input.mouse.buttons.remove(opts.button); }, - else => {}, + else => {} } } -fn sokolEvent(e_ptr: [*c]const sapp.Event) !bool { - const zone = tracy.initZone(@src(), .{}); +fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool { + const zone = tracy.initZone(@src(), .{ }); defer zone.deinit(); const e = e_ptr.*; + const MouseButton = Input.Mouse.Button; + if (imgui.handleEvent(e)) { - if (state.mouse_inside) { - try processEvent(Event{ .mouse_leave = {} }); + if (self.mouse_inside) { + try self.event(Event{ + .mouse_leave = {} + }); } - state.mouse_inside = false; + self.mouse_inside = false; return true; } blk: switch (e.type) { .MOUSE_DOWN => { - const mouse_button = Mouse.Button.fromSokol(e.mouse_button) orelse break :blk; + const mouse_button = MouseButton.fromSokol(e.mouse_button) orelse break :blk; - try processEvent(Event{ + try self.event(Event{ .mouse_pressed = .{ .button = mouse_button, .position = Vec2.init(e.mouse_x, e.mouse_y) @@ -507,9 +282,9 @@ fn sokolEvent(e_ptr: [*c]const sapp.Event) !bool { return true; }, .MOUSE_UP => { - const mouse_button = Mouse.Button.fromSokol(e.mouse_button) orelse break :blk; + const mouse_button = MouseButton.fromSokol(e.mouse_button) orelse break :blk; - try processEvent(Event{ + try self.event(Event{ .mouse_released = .{ .button = mouse_button, .position = Vec2.init(e.mouse_x, e.mouse_y) @@ -519,54 +294,62 @@ fn sokolEvent(e_ptr: [*c]const sapp.Event) !bool { return true; }, .MOUSE_MOVE => { - if (!state.mouse_inside) { - try processEvent(Event{ + if (!self.mouse_inside) { + try self.event(Event{ .mouse_enter = Vec2.init(e.mouse_x, e.mouse_y) }); } else { - try processEvent(Event{ + try self.event(Event{ .mouse_move = Vec2.init(e.mouse_x, e.mouse_y) }); } - state.mouse_inside = true; + self.mouse_inside = true; return true; }, .MOUSE_ENTER => { - if (!state.mouse_inside) { - try processEvent(Event{ + if (!self.mouse_inside) { + try self.event(Event{ .mouse_enter = Vec2.init(e.mouse_x, e.mouse_y) }); } - state.mouse_inside = true; + self.mouse_inside = true; return true; }, .RESIZED => { - if (state.mouse_inside) { - try processEvent(Event{ .mouse_leave = {} }); + if (self.mouse_inside) { + try self.event(Event{ + .mouse_leave = {} + }); } - try processEvent(Event{ .window_resize = {} }); + try self.event(Event{ + .window_resize = {} + }); - state.mouse_inside = false; + self.mouse_inside = false; return true; }, .MOUSE_LEAVE => { - if (state.mouse_inside) { - try processEvent(Event{ .mouse_leave = {} }); + if (self.mouse_inside) { + try self.event(Event{ + .mouse_leave = {} + }); } - state.mouse_inside = false; + self.mouse_inside = false; return true; }, .MOUSE_SCROLL => { - try processEvent(Event{ .mouse_scroll = Vec2.init(e.scroll_x, e.scroll_y) }); + try self.event(Event{ + .mouse_scroll = Vec2.init(e.scroll_x, e.scroll_y) + }); return true; }, .KEY_DOWN => { - try processEvent(Event{ + try self.event(Event{ .key_pressed = .{ .code = @enumFromInt(@intFromEnum(e.key_code)), .repeat = e.key_repeat @@ -576,14 +359,14 @@ fn sokolEvent(e_ptr: [*c]const sapp.Event) !bool { return true; }, .KEY_UP => { - try processEvent(Event{ + try self.event(Event{ .key_released = @enumFromInt(@intFromEnum(e.key_code)) }); return true; }, .CHAR => { - try processEvent(Event{ + try self.event(Event{ .char = @intCast(e.char_code) }); @@ -592,14 +375,16 @@ fn sokolEvent(e_ptr: [*c]const sapp.Event) !bool { .QUIT_REQUESTED => { // TODO: handle quit request. Maybe show confirmation window in certain cases. }, - else => {}, + else => {} } return false; } -fn sokolEventCallback(e_ptr: [*c]const sapp.Event) callconv(.c) void { - const consume_event = sokolEvent(e_ptr) catch |e| blk: { +fn sokolEventCallback(e_ptr: [*c]const sapp.Event, userdata: ?*anyopaque) callconv(.c) void { + const engine: *Engine = @alignCast(@ptrCast(userdata)); + + const consume_event = engine.sokolEvent(e_ptr) catch |e| blk: { log.err("sokolEvent() failed: {}", .{e}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -612,12 +397,16 @@ fn sokolEventCallback(e_ptr: [*c]const sapp.Event) callconv(.c) void { } } -fn sokolCleanupCallback() callconv(.c) void { - sokolCleanup(); +fn sokolCleanupCallback(userdata: ?*anyopaque) callconv(.c) void { + const engine: *Engine = @alignCast(@ptrCast(userdata)); + + engine.sokolCleanup(); } -fn sokolInitCallback() callconv(.c) void { - sokolInit() catch |e| { +fn sokolInitCallback(userdata: ?*anyopaque) callconv(.c) void { + const engine: *Engine = @alignCast(@ptrCast(userdata)); + + engine.sokolInit() catch |e| { log.err("sokolInit() failed: {}", .{e}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -626,8 +415,10 @@ fn sokolInitCallback() callconv(.c) void { }; } -fn sokolFrameCallback() callconv(.c) void { - sokolFrame() catch |e| { +fn sokolFrameCallback(userdata: ?*anyopaque) callconv(.c) void { + const engine: *Engine = @alignCast(@ptrCast(userdata)); + + engine.sokolFrame() catch |e| { log.err("sokolFrame() failed: {}", .{e}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -658,9 +449,27 @@ fn sokolLogCallback(tag: [*c]const u8, log_level: u32, log_item: u32, message: [ _ = user_data; if (filename != null) { - sokolLogFmt(log_level, "[{s}][id:{}] {s}:{}: {s}", .{ cStrToZig(tag orelse "-"), log_item, std.fs.path.basename(cStrToZig(filename orelse "-")), line_nr, cStrToZig(message orelse "") }); + sokolLogFmt( + log_level, + "[{s}][id:{}] {s}:{}: {s}", + .{ + cStrToZig(tag orelse "-"), + log_item, + std.fs.path.basename(cStrToZig(filename orelse "-")), + line_nr, + cStrToZig(message orelse "") + } + ); } else { - sokolLogFmt(log_level, "[{s}][id:{}] {s}", .{ cStrToZig(tag orelse "-"), log_item, cStrToZig(message orelse "") }); + sokolLogFmt( + log_level, + "[{s}][id:{}] {s}", + .{ + cStrToZig(tag orelse "-"), + log_item, + cStrToZig(message orelse "") + } + ); } } diff --git a/src/game.zig b/src/game.zig index 6327ec3..a3a1836 100644 --- a/src/game.zig +++ b/src/game.zig @@ -13,57 +13,67 @@ const Audio = Engine.Audio; const Game = @This(); -var player_position: Vec2 = undefined; +gpa: Allocator, +assets: *Assets, +canvas_size: Vec2, -pub fn init() !void { - player_position = .init(50, 50); +player: Vec2, + +pub fn init(gpa: Allocator, assets: *Assets) !Game { + return Game{ + .gpa = gpa, + .assets = assets, + .canvas_size = Vec2.init(100, 100), + .player = .init(50, 50) + }; } -pub fn deinit() void { +pub fn deinit(self: *Game) void { + _ = self; // autofix } -pub fn tick() !void { - const dt = Engine.deltaTime(); +pub fn tick(self: *Game, frame: Engine.Frame) !void { var dir = Vec2.init(0, 0); - if (Engine.isKeyDown(.W)) { + if (frame.input.isKeyDown(.W)) { dir.y -= 1; } - if (Engine.isKeyDown(.S)) { + if (frame.input.isKeyDown(.S)) { dir.y += 1; } - if (Engine.isKeyDown(.A)) { + if (frame.input.isKeyDown(.A)) { dir.x -= 1; } - if (Engine.isKeyDown(.D)) { + if (frame.input.isKeyDown(.D)) { dir.x += 1; } dir = dir.normalized(); if (dir.x != 0 or dir.y != 0) { Audio.play(.{ - .id = Assets.wood01 + .id = self.assets.wood01 }); } - player_position = player_position.add(dir.multiplyScalar(50 * dt)); + self.player = self.player.add(dir.multiplyScalar(50 * frame.dt)); - const regular_font = Assets.font_id.get(.regular); + const regular_font = self.assets.font_id.get(.regular); - Gfx.drawRectangle(.init(0, 0), Engine.getCanvasSize(), rgb(20, 20, 20)); + Gfx.drawRectangle(.init(0, 0), self.canvas_size, rgb(20, 20, 20)); const size = Vec2.init(20, 20); - Gfx.drawRectangle(player_position.sub(size.divideScalar(2)), size, rgb(200, 20, 20)); + Gfx.drawRectangle(self.player.sub(size.divideScalar(2)), size, rgb(200, 20, 20)); if (dir.x != 0 or dir.y != 0) { - Gfx.drawRectanglOutline(player_position.sub(size.divideScalar(2)), size, rgb(20, 200, 20), 3); + Gfx.drawRectanglOutline(self.player.sub(size.divideScalar(2)), size, rgb(20, 200, 20), 3); } - Gfx.drawText(player_position, "Player", .{ + Gfx.drawText(self.player, "Player", .{ .font = regular_font, .size = 10 }); } -pub fn debug() !void { +pub fn debug(self: *Game) !void { + _ = self; // autofix if (!imgui.beginWindow(.{ .name = "Debug", .pos = Vec2.init(20, 20), diff --git a/src/main.zig b/src/main.zig index 028aace..9a6aadc 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,7 +1,8 @@ const Engine = @import("./engine/root.zig"); +var engine: Engine = undefined; + pub fn main() !void { - try Engine.run(.{ - }); + try engine.run(.{}); }