From fa4f24b68904c67f5f205ad32952d8a4fcb27795 Mon Sep 17 00:00:00 2001 From: Nate Anderson Date: Thu, 2 Jan 2025 17:20:39 -0700 Subject: [PATCH] Added animation and color changing support, bouncing slime!! --- assets/png_test.png | Bin 0 -> 2264 bytes assets/slime_still.png | Bin 0 -> 2484 bytes build.zig | 102 +--------------------------- src/asset_utils.zig | 28 ++++++++ src/game_state.zig | 25 +++++++ src/main.zig | 147 ++++++++++++++++++++++++++--------------- src/sdl.zig | 4 ++ src/slime_factory.zig | 46 +++++++++++++ 8 files changed, 197 insertions(+), 155 deletions(-) create mode 100644 assets/png_test.png create mode 100644 assets/slime_still.png create mode 100644 src/asset_utils.zig create mode 100644 src/game_state.zig create mode 100644 src/sdl.zig create mode 100644 src/slime_factory.zig diff --git a/assets/png_test.png b/assets/png_test.png new file mode 100644 index 0000000000000000000000000000000000000000..fb9dfe968171e0c1b080022fdc671582d5805065 GIT binary patch literal 2264 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 { + sdl.SDL_QUIT => { quit = true; }, - c.SDL_KEYDOWN => { + sdl.SDL_KEYUP => { switch (event.key.keysym.sym) { - c.SDLK_UP => { - img_pos.y = img_pos.y - 5; + sdl.SDLK_LSHIFT => { + shifted = false; }, - c.SDLK_DOWN => { - img_pos.y = img_pos.y + 5; + else => {}, + } + }, + sdl.SDL_KEYDOWN => { + switch (event.key.keysym.sym) { + sdl.SDLK_r => { + game_state.r = if (shifted) game_state.r - 5 else game_state.r + 5; }, - c.SDLK_LEFT => { - img_pos.x = img_pos.x - 5; + sdl.SDLK_g => { + game_state.g = if (shifted) game_state.g - 5 else game_state.g + 5; }, - c.SDLK_RIGHT => { - img_pos.x = img_pos.x + 5; + sdl.SDLK_b => { + game_state.b = if (shifted) game_state.b - 5 else game_state.b + 5; }, + sdl.SDLK_LSHIFT => { + shifted = true; + }, + // sdl.SDLK_UP => { + // img_pos.y = img_pos.y - 5; + // }, + // sdl.SDLK_DOWN => { + // img_pos.y = img_pos.y + 5; + // }, + // sdl.SDLK_LEFT => { + // img_pos.x = img_pos.x - 5; + // }, + // sdl.SDLK_RIGHT => { + // img_pos.x = img_pos.x + 5; + // }, else => {}, } log("Got key event: %i\n", event.key.keysym.sym); @@ -55,57 +84,65 @@ pub fn main() !void { else => {}, } } - _ = c.SDL_FillRect(screen_surface, null, c.SDL_MapRGB(screen_surface.format, 0xff, 0xff, 0xff)); - var dest_rect = c.SDL_Rect{ .x = src_rect.x + img_pos.x, .y = src_rect.y + img_pos.y, .w = SCREEN_WIDTH, .h = SCREEN_HEIGHT }; - _ = c.SDL_BlitScaled(zig_image, &src_rect, screen_surface, &dest_rect); + _ = sdl.SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff); + _ = sdl.SDL_RenderClear(renderer); + std.debug.print("r:{x} g:{x} b:{x}\n", .{ + game_state.r, + game_state.g, + game_state.b, + }); - _ = c.SDL_UpdateWindowSurface(window); - c.SDL_Delay(17); + game_state.update_tick(); + + // Render red rect + // const fill_rect: sdl.struct_SDL_Rect = sdl.SDL_Rect{ .x = SCREEN_WIDTH / 4, .y = SCREEN_HEIGHT / 4, .w = SCREEN_WIDTH / 2 + img_pos.x, .h = SCREEN_HEIGHT / 2 + img_pos.y }; + // _ = sdl.SDL_SetRenderDrawColor(renderer, 0xff, 0x00, 0x00, 0xff); + // _ = sdl.SDL_RenderFillRect(renderer, &fill_rect); + // _ = sdl.SDL_RenderSetViewport(renderer, &.{ .x = SCREEN_WIDTH / 2, .y = SCREEN_HEIGHT / 2, .w = SCREEN_WIDTH, .h = SCREEN_HEIGHT / 2 }); + // _ = sdl.SDL_RenderCopy(renderer, texture, null, null); + _ = sdl.SDL_RenderPresent(renderer); + // var dest_rect = sdl.SDL_Rect{ .x = src_rect.x + img_pos.x, .y = src_rect.y + img_pos.y, .w = SCREEN_WIDTH, .h = SCREEN_HEIGHT }; + // _ = sdl.SDL_BlitScaled(zig_image, &src_rect, screen_surface, &dest_rect); + + // _ = sdl.SDL_UpdateWindowSurface(window); + sdl.SDL_Delay(117); } return; } fn init() !void { // Init SDL with video subsystem flag - if (c.SDL_Init(c.SDL_INIT_VIDEO) < 0) { - log("Unable to initialize SDL: %s\n", c.SDL_GetError()); + 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 = c.SDL_CreateWindow("Game Window", c.SDL_WINDOWPOS_UNDEFINED, c.SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, c.SDL_WINDOW_SHOWN); + 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", c.SDL_GetError()); + log("Unable to initialize SDL window: %s\n", sdl.SDL_GetError()); return error.SDLInitializationFailed; } window = opt_window.?; - screen_surface = c.SDL_GetWindowSurface(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); - const img_flags = c.IMG_INIT_PNG; - if (c.IMG_Init(img_flags) != img_flags) { - log("Unable to initialize SDL Image: %s\n", c.IMG_GetError()); + // 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; } } -fn loadSurface(path: [*c]const u8) !*c.struct_SDL_Surface { - // TODO check file exists - const loaded_surface: [*c]c.struct_SDL_Surface = c.SDL_LoadBMP(path); - if (loaded_surface == null) { - log("Unable to load media at path %s: %s\n", path, c.SDL_GetError()); - return error.SDLLoadError; - } - defer c.SDL_FreeSurface(loaded_surface); - // Converting 24bit bmp to 32bit to match display - const optimized_surface = c.SDL_ConvertSurface(loaded_surface, screen_surface.format, 0); - if (optimized_surface == null) { - log("Unable to optimize media at path %s: %s\n", path, c.SDL_GetError()); - return error.SDLLoadError; - } - return optimized_surface; -} - fn close() void { - c.SDL_DestroyWindow(window); - c.SDL_Quit(); + sdl.SDL_DestroyRenderer(renderer); + sdl.SDL_DestroyWindow(window); + sdl.IMG_Quit(); + sdl.SDL_Quit(); } diff --git a/src/sdl.zig b/src/sdl.zig new file mode 100644 index 0000000..aa3f9e5 --- /dev/null +++ b/src/sdl.zig @@ -0,0 +1,4 @@ +pub const c = @cImport({ + @cInclude("SDL2/SDL.h"); + @cInclude("SDL2/SDL_image.h"); +}); diff --git a/src/slime_factory.zig b/src/slime_factory.zig new file mode 100644 index 0000000..90b0f94 --- /dev/null +++ b/src/slime_factory.zig @@ -0,0 +1,46 @@ +const std = @import("std"); +const sdl = @import("sdl.zig").c; +const atils = @import("./asset_utils.zig"); +const GameState = @import("./game_state.zig").GameState; +const slime_sprite_sheet_texture_path = "assets/slime_still.png"; + +pub const Slime = struct { + total_frames: u8 = 6, + x: i32 = 64, + y: i32 = 64, + f: u16 = 0, +}; + +pub const SlimeFactory = struct { + slimes: std.ArrayList(Slime) = undefined, + slime_sprite_sheet_texture: *sdl.struct_SDL_Texture = undefined, + + pub fn init(allocator: std.mem.Allocator, renderer: *sdl.struct_SDL_Renderer) !SlimeFactory { + const texture = try atils.loadTexture(renderer, slime_sprite_sheet_texture_path); + return .{ + .slimes = std.ArrayList(Slime).init(allocator), + .slime_sprite_sheet_texture = texture, + }; + } + + pub fn deinit(sf: *SlimeFactory) void { + sdl.SDL_DestroyTexture(sf.slime_sprite_sheet_texture); + sf.slimes.deinit(); + } + + pub fn add(sf: *SlimeFactory, slime: Slime) !void { + try sf.slimes.append(slime); + } + + pub fn render(sf: *SlimeFactory, game_state: *GameState) void { + _ = sdl.SDL_SetTextureColorMod(sf.slime_sprite_sheet_texture, game_state.r, game_state.g, game_state.b); + for (sf.slimes.items, 0..) |slime, s_idx| { + std.debug.print("Slime {d}:\t{}\n", .{ s_idx + 1, slime }); + 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 dest_rect: sdl.struct_SDL_Rect = .{ .x = slime.x, .y = slime.y, .w = 128, .h = 128 }; + _ = sdl.SDL_RenderCopy(game_state.renderer, sf.slime_sprite_sheet_texture, &sprite_frame_rect, &dest_rect); + sf.slimes.items[s_idx].f = (sf.slimes.items[s_idx].f + 1) % sf.slimes.items[s_idx].total_frames; + } + } +};