1.44mb-gamejam

submission for the 1.44mb game jam - https://2pgarcade.com/contest-144mb.html
git clone git://brookjeynes.dev/bjeynes/1.44mb-gamejam.git
Log | Files | Refs

commit 4f60de7c89963bb3c965c63ade7c4f1eae1ee3a9
parent a7a39a80e476dac702281c6e4adb5e36d208ed0c
Author: brookjeynes <me@brookjeynes.dev>
Date:   Thu, 30 Jul 2026 06:05:39 +1000

feat: add enemies

Signed-off-by: brookjeynes <me@brookjeynes.dev>

Diffstat:
Msrc/bullet.zig | 12++++++------
Asrc/enemy.zig | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.zig | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/player.zig | 35++++++++++++++++++++++++-----------
4 files changed, 148 insertions(+), 17 deletions(-)

diff --git a/src/bullet.zig b/src/bullet.zig @@ -1,9 +1,7 @@ const raylib = @import("raylib"); const Timer = @import("./timer.zig"); -const speed: f16 = 300.0; const radius: f16 = 10.0; -const spawn_cooldown: f16 = 0.4; pub const Bullet = struct { position: raylib.Vector2, @@ -17,7 +15,8 @@ pub fn Pool(comptime upper: usize) type { bullets: [upper]Bullet = undefined, count: usize = 0, - fire_timer: Timer = .{ .duration = spawn_cooldown }, + speed: f16, + fire_timer: Timer, pub fn tick(self: *Self) bool { return self.fire_timer.tick(raylib.GetFrameTime()); @@ -27,11 +26,12 @@ pub fn Pool(comptime upper: usize) type { self: *Self, position: raylib.Vector2, direction: raylib.Vector2, + colour: raylib.Color, ) void { self.bullets[self.count] = .{ .position = position, .direction = direction, - .color = raylib.MAROON, + .color = colour, }; self.count += 1; } @@ -42,8 +42,8 @@ pub fn Pool(comptime upper: usize) type { var i: usize = 0; while (i < self.count) { var bullet = &self.bullets[i]; - bullet.position.x += bullet.direction.x * speed * delta; - bullet.position.y += bullet.direction.y * speed * delta; + bullet.position.x += bullet.direction.x * self.speed * delta; + bullet.position.y += bullet.direction.y * self.speed * delta; if (bullet.position.x < -radius * 2 or bullet.position.x > screen_size.x + radius * 2 or diff --git a/src/enemy.zig b/src/enemy.zig @@ -0,0 +1,64 @@ +const raylib = @import("raylib"); +const raymath = @import("raymath"); + +const bullet = @import("./bullet.zig"); + +const max_spawned_bullets = 2000; +const bullet_speed: f16 = 100.0; +const bullet_spawn_cooldown: f16 = 2.0; + +pub const Enemy = @This(); + +movement_speed: f16 = 40.0, +size: raylib.Vector2 = .{ .x = 40, .y = 40 }, +direction: raylib.Vector2 = .{}, +position: raylib.Vector2 = .{}, + +bullet_pool: bullet.Pool(max_spawned_bullets) = .{ + .speed = bullet_speed, + .fire_timer = .{ + .duration = bullet_spawn_cooldown, + }, +}, + +pub fn handleMovement(self: *Enemy, target: raylib.Vector2) void { + const delta: f32 = raylib.GetFrameTime(); + + const direction: raymath.Vector2 = raymath.Vector2Subtract(@bitCast(target), @bitCast(self.position)); + self.direction = @bitCast(raymath.Vector2Normalize(@bitCast(direction))); + self.position.x += self.direction.x * self.movement_speed * delta; + self.position.y += self.direction.y * self.movement_speed * delta; + + const max_x: f32 = @as(f32, @floatFromInt(raylib.GetScreenWidth())) - self.size.x; + const max_y: f32 = @as(f32, @floatFromInt(raylib.GetScreenHeight())) - self.size.y; + self.position.x = @max(0, @min(max_x, self.position.x)); + self.position.y = @max(0, @min(max_y, self.position.y)); + + self.shoot(); +} + +pub fn shoot(self: *Enemy) void { + if (self.bullet_pool.tick()) { + self.bullet_pool.spawn( + .{ + .x = self.position.x + (self.size.x / 2), + .y = self.position.y + (self.size.y / 2), + }, + self.direction, + raylib.RED, + ); + } +} + +pub fn render(self: *Enemy) void { + raylib.DrawRectangleV( + self.position, + self.size, + raylib.RED, + ); + + self.bullet_pool.render(.{ + .x = @floatFromInt(raylib.GetScreenWidth()), + .y = @floatFromInt(raylib.GetScreenHeight()), + }); +} diff --git a/src/main.zig b/src/main.zig @@ -1,8 +1,10 @@ const std = @import("std"); const raylib = @import("raylib"); +const raymath = @import("raymath"); const bullet = @import("./bullet.zig"); +const Enemy = @import("./enemy.zig"); const Player = @import("./player.zig"); const Timer = @import("./timer.zig"); @@ -23,8 +25,28 @@ pub fn main() !void { }, }; + var enemy_spawn_time: Timer = .{ .duration = 0.2 }; + var enemies: [255]Enemy = undefined; + var enemy_count: u8 = 0; + while (!raylib.WindowShouldClose()) { + const delta = raylib.GetFrameTime(); + player.handleMovement(); + for (0..enemy_count) |i| { + enemies[i].handleMovement(player.position); + } + + if (enemy_spawn_time.tick(delta)) { + enemies[enemy_count] = spawnEnemy( + .{ + .x = screen_width, + .y = screen_height, + }, + player, + ); + enemy_count += 1; + } raylib.BeginDrawing(); defer raylib.EndDrawing(); @@ -32,5 +54,37 @@ pub fn main() !void { raylib.ClearBackground(raylib.RAYWHITE); player.render(); + for (0..enemy_count) |i| { + enemies[i].render(); + } } } + +fn spawnEnemy(screen: raylib.Vector2, player: Player) Enemy { + const min_dist: f32 = 200.0; + const player_cx = player.position.x + player.size.x / 2; + const player_cy = player.position.y + player.size.y / 2; + + var enemy = Enemy{ .position = .{ .x = 0, .y = 0 } }; + while (true) { + const rnd_x = @as(f32, @floatFromInt(raylib.GetRandomValue(0, 100))); + const rnd_y = @as(f32, @floatFromInt(raylib.GetRandomValue(0, 100))); + const spawn_x = (rnd_x / 100.0) * (@as(f32, @floatCast(screen.x)) - enemy.size.x); + const spawn_y = (rnd_y / 100.0) * (@as(f32, @floatCast(screen.y)) - enemy.size.y); + + const center_x = spawn_x + enemy.size.x / 2; + const center_y = spawn_y + enemy.size.y / 2; + + const distance_between_player = raymath.Vector2Distance( + .{ .x = center_x, .y = center_y }, + .{ .x = player_cx, .y = player_cy }, + ); + + if (distance_between_player >= min_dist) { + enemy.position = .{ .x = spawn_x, .y = spawn_y }; + break; + } + } + + return enemy; +} diff --git a/src/player.zig b/src/player.zig @@ -3,27 +3,35 @@ const raymath = @import("raymath"); const bullet = @import("./bullet.zig"); -const max_bullets = 2000; +const max_spawned_bullets = 2000; +const bullet_speed: f16 = 300.0; +const bullet_spawn_cooldown: f16 = 0.4; pub const Player = @This(); movement_speed: f16 = 140.0, size: raylib.Vector2 = .{ .x = 50, .y = 50 }, direction: raylib.Vector2 = .{}, +last_known_direction: raylib.Vector2 = .{}, position: raylib.Vector2 = .{}, -bullet_pool: bullet.Pool(max_bullets) = .{}, +bullet_pool: bullet.Pool(max_spawned_bullets) = .{ + .speed = bullet_speed, + .fire_timer = .{ + .duration = bullet_spawn_cooldown, + }, +}, pub fn handleMovement(self: *Player) void { const delta: f32 = raylib.GetFrameTime(); - self.direction = .{}; - if (raylib.IsKeyDown(raylib.KEY_RIGHT)) self.direction.x += 1; - if (raylib.IsKeyDown(raylib.KEY_LEFT)) self.direction.x -= 1; - if (raylib.IsKeyDown(raylib.KEY_UP)) self.direction.y -= 1; - if (raylib.IsKeyDown(raylib.KEY_DOWN)) self.direction.y += 1; + var direction: raymath.Vector2 = .{}; + if (raylib.IsKeyDown(raylib.KEY_RIGHT)) direction.x += 1; + if (raylib.IsKeyDown(raylib.KEY_LEFT)) direction.x -= 1; + if (raylib.IsKeyDown(raylib.KEY_UP)) direction.y -= 1; + if (raylib.IsKeyDown(raylib.KEY_DOWN)) direction.y += 1; - self.direction = @bitCast(raymath.Vector2Normalize(@bitCast(self.direction))); + self.direction = @bitCast(raymath.Vector2Normalize(direction)); self.position.x += self.direction.x * self.movement_speed * delta; self.position.y += self.direction.y * self.movement_speed * delta; @@ -32,7 +40,11 @@ pub fn handleMovement(self: *Player) void { self.position.x = @max(0, @min(max_x, self.position.x)); self.position.y = @max(0, @min(max_y, self.position.y)); - if (self.direction.x != 0 or self.direction.y != 0) { + if (direction.x != 0 or direction.y != 0) { + self.last_known_direction = @bitCast(direction); + } + + if (self.last_known_direction.x != 0 or self.last_known_direction.y != 0) { self.shoot(); } } @@ -44,7 +56,8 @@ pub fn shoot(self: *Player) void { .x = self.position.x + (self.size.x / 2), .y = self.position.y + (self.size.y / 2), }, - self.direction, + self.last_known_direction, + raylib.BLUE, ); } } @@ -53,7 +66,7 @@ pub fn render(self: *Player) void { raylib.DrawRectangleV( self.position, self.size, - raylib.MAROON, + raylib.BLUE, ); self.bullet_pool.render(.{