Added animation and color changing support, bouncing slime!!

This commit is contained in:
Nate Anderson
2025-01-02 17:20:39 -07:00
parent f2bf87c063
commit fa4f24b689
8 changed files with 197 additions and 155 deletions
+28
View File
@@ -0,0 +1,28 @@
const sdl = @import("./sdl.zig").c;
const log = sdl.SDL_Log;
pub fn loadTexture(renderer: *sdl.struct_SDL_Renderer, path: [*c]const u8) !*sdl.struct_SDL_Texture {
const new_texture = sdl.IMG_LoadTexture(renderer, path) orelse {
log("Unable to load texture from surface %s\n", sdl.SDL_GetError());
return error.SDLLoadError;
};
return new_texture;
}
// fn loadSurface(path: [*c]const u8) !*sdl.struct_SDL_Surface {
// // TODO check file exists
// const loaded_surface: [*c]sdl.struct_SDL_Surface = sdl.IMG_Load(path);
// if (loaded_surface == null) {
// log("Unable to load media at path %s: %s\n", path, sdl.IMG_GetError());
// return error.SDLLoadError;
// }
// defer sdl.SDL_FreeSurface(loaded_surface);
// // Converting 24bit bmp to 32bit to match display
// const optimized_surface = sdl.SDL_ConvertSurface(loaded_surface, screen_surface.format, 0);
// if (optimized_surface == null) {
// log("Unable to optimize media at path %s: %s\n", path, sdl.SDL_GetError());
// return error.SDLLoadError;
// }
// return optimized_surface;
// }
+25
View File
@@ -0,0 +1,25 @@
const sdl = @import("./sdl.zig").c;
const std = @import("std");
const sf = @import("./slime_factory.zig");
pub const GameState = struct {
r: u8 = 0xff,
g: u8 = 0xff,
b: u8 = 0xff,
slime_factory: sf.SlimeFactory = undefined,
renderer: *sdl.struct_SDL_Renderer = undefined,
pub fn init(allocator: std.mem.Allocator, renderer: *sdl.struct_SDL_Renderer) !GameState {
var slime_factory = try sf.SlimeFactory.init(allocator, renderer);
try slime_factory.add(.{});
return .{ .slime_factory = slime_factory, .renderer = renderer };
}
pub fn deinit(self: *GameState) void {
self.slime_factory.deinit();
}
pub fn update_tick(self: *GameState) void {
self.slime_factory.render(self);
}
};
+92 -55
View File
@@ -1,16 +1,16 @@
const c = @cImport({
@cInclude("SDL2/SDL.h");
@cInclude("SDL2/SDL_image.h");
});
const std = @import("std");
const sdl = @import("sdl.zig").c;
const atils = @import("./asset_utils.zig");
const gs = @import("./game_state.zig");
const assert = std.debug.assert;
const SCREEN_WIDTH = 640;
const SCREEN_HEIGHT = 480;
const log = c.SDL_Log;
const log = sdl.SDL_Log;
var window: *sdl.struct_SDL_Window = undefined;
// var screen_surface: *sdl.struct_SDL_Surface = undefined;
var renderer: *sdl.struct_SDL_Renderer = undefined;
var window: *c.struct_SDL_Window = undefined;
var screen_surface: *c.struct_SDL_Surface = undefined;
const Offset = struct {
x: i32,
y: i32,
@@ -18,36 +18,65 @@ const Offset = struct {
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();
const zig_image = try loadSurface("assets/stretch.bmp");
defer c.SDL_FreeSurface(zig_image);
const texture = try atils.loadTexture(renderer, "assets/png_test.png");
defer sdl.SDL_DestroyTexture(texture);
var src_rect: c.struct_SDL_Rect = c.SDL_Rect{ .x = 0, .y = 0, .w = SCREEN_WIDTH / 2, .h = SCREEN_HEIGHT / 2 };
// var slime_factory = try sf.SlimeFactory.init(allocator, renderer);
// defer slime_factory.deinit();
// try slime_factory.add(.{});
var game_state = try gs.GameState.init(allocator, renderer);
defer game_state.deinit();
var quit = false;
var shifted = false;
while (!quit) {
var event: c.SDL_Event = undefined;
while (c.SDL_PollEvent(&event) != 0) {
var event: sdl.SDL_Event = undefined;
while (sdl.SDL_PollEvent(&event) != 0) {
switch (event.type) {
c.SDL_QUIT => {
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();
}
+4
View File
@@ -0,0 +1,4 @@
pub const c = @cImport({
@cInclude("SDL2/SDL.h");
@cInclude("SDL2/SDL_image.h");
});
+46
View File
@@ -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;
}
}
};