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 zcmeAS@N?(olHy`uVBq!ia0y~yV02($VEDkn#K6Gt&tJ=ifq}6l)7d$|)7e>}peR2r zGbfdSL1SX=L|c!;0V1vb!Aq4y1+69d-FQJbxjICm>=m`Xzudp{I1$6W*Aqor~mUi%RS}t3MN`Un|D2GSav(j zYNo*EouP(bx)@k^5||8UMjo4^vh+c8{P!!qrQ(kx|JBxq&7ZYhLO`JN@fm~MN4$?_ z`rdx9$6qEA2>${Wj66aoPpN?5>+N2`3k%d!9P*GnQiE>|8pZvW<& zYLVs>t>%fzjG-A5V(ov(|CzTobjGYNlMZn%_p7`Ptdg zuVt3=JG7-u-Nneq*SJwBb^Y%>yO$T|{r|S**>~pcGl~wyTsf1@z`(#+;1OBOz@VoL z!i*J5?aLS#7}!fZeO=jKGBODo%6U6{a%W&*kSuYHC~?lu%}vcKVQ?-=O)N=GQ7F$W z$xv|j^bH7a+RjMeviEb8Uh_674H3y6y#oVhN;uB#Ugii{iTow_iV1 z|MYvmf4ksf8~+;X#IIpTdm5hmgdgUxDBLcU)*=4Y?~jkbhAPvZhDNqD2@&>Vu~Tzb z8Fn|!&MxW9N2l8Rk&j3Tk{zQF22^C`Tu_Xog_v^_1Syv|NprC zdg+FTx7kJatC~!GT`O_*t^ECejrr@Rum8X1pJ$z2^32VC;`Mb**+y zm;2{+`@`?HeYe}vqq|Uej#}2$Xp4~IxdLmS_&&Jfyzd%^RgBYA#hPV@d(}U@J}z}# ztziG0pexdqJn!}A8}NaF77+0 zm48y>zv6DacwU>yN)K3?Q~PEvyJr`tQu2Gpv{v=Yy#MO*8U1g32;c52zwzR3BWc^` z$@XE>rd@Dt4?N18`mr+lWs5@d8tN2ET z0}Tz4)4msRNU-qq?);^Z;Bepu8@B`~jT#lmdLL+L=sZ%Y!`R%w$avUxT{5Qx3lAt2 zZ)lF+efHV&^+B;3MvW(<-cSDS$}wZl*Q^MT2J@MM1{-r?HeK4#z{r@)5iPtd58IjFZtUmi@lxinTI;s=J$ozjN7cV4-LEHw( zVGair5ep}Id{dnIp=J>r+)u+Ys2Sjs}ot~?2Z#C_*K=~+u(R0!e;X2 z)Mb87f2-W~&N%Sfs`IkXLt{ZpZ+4l_*Nr#19EkiKsLymRYGTz(osZ$GZPqOPY`XUC z^Uwc2R=%&#_-n)MuYGUf+>2*~AHV&2>Ft7=i2OfK8>)467K+Du`v<&VZ=bEN@E}x5 zZ@u}6q62kt2Uho7GxmS{_p$NaH&-__h_9;Meb3VB;)aIjHUimKrT3fjx=;Ih-J@PE zH2!Ic$LllBIX0iKExlVkWs*zmEVD-In-*81_$P00|M{(LPs;9hm$o$g)_par&E(Zz ziM7+ev`S{X9q2Sa{C9nOX`Dz##DRyf%~^*#1`1 zg~KBIaJt==vmCAm4okiJdAI(hUCAX)hXda4Tc%g>aa25On{1ZG!7cHcgGGnIS%Oi2 z>i2cucoG?tIl?P+?knm%cp7$btKEY1gv0xHUER>I@=n3-6>7g7lo-z!e_4Jz`rCh} zfQ4KZFD}|Yow~4V@r^Qbj{j`IuF;<|x9sOdL*vsGPMM^%;{0|Nttr>mdKI;Vst03huh AN&o-= literal 0 HcmV?d00001 diff --git a/assets/slime_still.png b/assets/slime_still.png new file mode 100644 index 0000000000000000000000000000000000000000..8e13f88a2fb2b1005eeebf8b10509dac38651606 GIT binary patch literal 2484 zcmeAS@N?(olHy`uVBq!ia0y~yU~FJuU~u4IV_;wqj##jtfq{XsILO_JVcj{ImkbP? zd7dtgAr*7p&h^c|9VXT$e`(X&0~`-5I=WakZg2a`zifuyt+OJ*CidGrcX^~=SJ|9l z#>s++ifUJ~)`6ggR4X@8DcVp&emqw06I?=7cKNflsmB!mu%Doxh^ z_VSI+-n%yJZmn-W-#)&0^5x*g&s8~^7D!9~^?%JR%5r#9z}JYsbyI%-y!20f`SSaj z)}GBbEEriFFEn%iTYfadZtmQ_^RFgJKb~c{%Yc#Ppol7OKxCZ7eTJP+>gU=$TwAb1 z_1qw0*VZ%~Fc#KOHu<~I)Isl8yt@CDu!>pBTmq-$#>d7A9yi>4Q|I>c zhlkt0_ifhojG4n=b@Kqj6}xk@9p(jmZNJ8_^G*HQ`3qe3+*;>5Ynh7Yp=;N~7(V}; z^RwpM*|Wae{3ogWR7mmmkQO@plyd=dEE~hOtaHMv>V$bG<4n2UEynz$S$ zshoNK`3w(xGvnr)YckrNGt`PQT~~LQJ|l{;vDS?t@wL8)?OWb}`@d>G?ss!M!@I34 zZF8iV;FLhIGX{sBEcuYiRN@$~t?=SLhr^fYYbr|29QK;q+x@(MjQx(({A=%)b%3m3 zxO3;umnfE+Sq!-^Gg(>0Bp6%P)*tnKdoAYzgJgbP&FB5SWoDT*sZY*+c=oK#qVMMI z+q<{V_f!6o%VctNk!U~}pRI)Bw_7dC?{|n_$!(~~{&VWW!}`MV<;&+EOkmJaJM`_F zndc;fnLeL(&zUZFL$EdPyhQWChJy(XGRM8=COKMfNb$LK;wYoo#up}X(b3Uw-n=o8 z;k$YFu4NKw?<~A!8RMs_ulu{U{R@v|$l+b_s^e10!{V%jHgPn`JjUEJQdFC*RTJKFTce3(~j{~ zLNHi^m08n;ueW;|*-p$V)W6jFf2}UV_Gjr|Cs}z4J*rSlwwwRFeyP>MZJrOlvJ}V$ zYb!(>mT+F#rM6-fh>X04U0Akwyd*n-)b3fmEmil z-?Gfd_1fGGy5@VW4*bqus3?BGlx;yqHp{}-+h=vLtuS2pW4^dHgKUctV~qQB#vM(5 z%q6Pq*GwGZ&i~bmxNa0>|H0Jo*G^lz3)kgsOkMYCr|~ZKPfyRhd)|XHc0YrK(KpkG z*Y%2P7-s1;*3M#R+MqeN{#YRYi972*>|e$7z?q@;!i6?9)(h=V&MW^qyyWd)e)&h& zUd1}tsWkFSwUkxVDsN$1_&UFf|K7$hXAbQKvzB%70#3(%|A`N0aN1ufwX2Rpfl-Si z*1ko$S>aQB7{hDM(EpZ=DhIfnuJa2qb13|G-oegrwzOs4{ogDr6iWUIl=192|9>j$ zgKz)4e;NFE?tAaQ%&vdYHJ%I^%T!DL=JsuFb$c%F^{;&cQ^sONkH4T2r*aCzhD1T0 zSo;d$X0x;WEEE1m+1VUZikY`3_CnZxeolsmW&67VW-ClTzn5F*WAnevd0uk{yqrdvAW9Zml=Mg@1R>_QL5&W*SnU!D0jAh(dqc&*TH`h zFZQG-?k+p3W|&bVd}FEC3g(OQW(!}pZ%BSH^;_@irB6dY#C)Gye=&b+-36KE3tyYJ z8eUi@z2@q=8y0N6)4aw0t-nxg@O@tS#r(N-1`;3zp@s&lq}Ng6uxPpvZm>o+wtSSP*aB1k&S+(*v7ZqB~HpZ)dp^eVo*2%Nn<%V&Q5icS0X&;R%R z@9zg+>?=Mfu>QDzyuasoG9MqG&-_C|%aS+j-#?$}i|x0reP7%IkNhrwU)Ak+zkbQH z=KrB`HAO{}{M3XQ)FyLQS6AQM_ULK;e#RMmO%qSm*VN4MQxl$~^6=HGuDf?*8J1nv z+~Co4KYl;kpG*J0yu4guHFwf7&C4&R%$+N{NaNA%?fEW&Q)>S{@3&|8rNI>SHN4mQ zi`BP;&VL$R6@On%HT)|sF8=4&Bi8u+b*4%G_Wh2}6!7=+n>Nk7a^vf|b*mI#@E+S= z_s>$g=lp#8ay!Ak{(kST>Yh%MRF=Gabp7&66K^Jk*KG%U|F*ZcYaYlkV}9`Km(}If z`yQNQd1Rs1C!VkJ`^Q7(#~&pYbugdp{P5V{KJ~ew4ByXBPrcO*Z=~F5j5{VL_IUld z^`|W#H2tgp_u=p)&etCrCj5DS@1VQ8`%12YdAl4N-Z2HnL_KA<_n$8O;OFP(OMmI> z>u-Lm;>ob-oyB~PK#`B{-bO$F((kr7q_naioaKrZ!;bH^>uvjz7il~?9A9s^Z->I7 zS|O3I{~!N;&nUsy{{35SG>e&R`HuSxC6?a;?EW=OvrL%%FZ7mUf1PW_zwoLAamR)# zZ{@3wT9Z^xHZA}0^=Nm_?ut08FoxMG%o|D~Sx)TRU-eU{!2SPZb^oB$b*o-C|8`aA zTJ)yyG03SS?6<^TKmy2z>+9fsJo3}&+BFUsCr{k&ef{(?FCuWRw{ z)-S#{O!%|@nk~cDy_pPMBKb2`y!}(IAz!jXc>Uoo;S=M { + 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; + } + } +};