Compare commits

...

6 Commits

Author SHA1 Message Date
880cf8ab4e implement collisions 2026-01-30 03:48:02 +02:00
1220b36531 add gun with mouse control 2026-01-29 22:01:56 +02:00
2a55252942 add audio volume 2026-01-28 07:24:10 +02:00
e8b565508f add player animations 2026-01-28 05:12:02 +02:00
97829b485c remove non-tilemap assets 2026-01-27 07:48:07 +02:00
a121b8bcdb add tilemap rendering 2026-01-27 07:42:54 +02:00
535 changed files with 1680 additions and 300 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
zig-out zig-out
.zig-cache .zig-cache
*.tiled-session

View File

@ -1,4 +1,12 @@
# Game template # Gaem
* Run & gun
* Nuclear throne -esque
* Single gun type
* Single map
* Single enemy type
* Collect letters to win, up to 7 letters we have 8 character sprites
* The word is WINNER
## Run ## Run
@ -16,15 +24,3 @@ Cross-compile for Windows from Linux:
```sh ```sh
zig build -Dtarget=x86_64-windows zig build -Dtarget=x86_64-windows
``` ```
## TODO
* Use [Skribidi](https://github.com/memononen/Skribidi) instead of fontstash for text rendering
* Support for audio formats (Might not need all of them, haven't decided):
* QOA, maybe [qoa.h](https://github.com/phoboslab/qoa/blob/master/qoa.h)?
* Flac, maybe [dr_flac.h](https://github.com/mackron/dr_libs/blob/master/dr_flac.h)?
* Wav, maybe [dr_wav.h](https://github.com/mackron/dr_libs/blob/master/dr_wav.h)?
* Mp3, maybe [dr_mp3.h](https://github.com/mackron/dr_libs/blob/master/dr_mp3.h)?
* Gamepad support.
* WASM Support. Currently a build config isn't provided for this target.
* Update build config for other platforms to reduce binary size. All of the video and audio drivers aren't needed, only gamepads

View File

@ -2,6 +2,8 @@ const std = @import("std");
const sokol = @import("sokol"); const sokol = @import("sokol");
const builtin = @import("builtin"); const builtin = @import("builtin");
const project_name = "game-2026-01-18";
pub fn build(b: *std.Build) !void { pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
@ -105,12 +107,12 @@ pub fn build(b: *std.Build) !void {
// from here on different handling for native vs wasm builds // from here on different handling for native vs wasm builds
if (target.result.cpu.arch.isWasm()) { if (target.result.cpu.arch.isWasm()) {
try buildWasm(b, .{ try buildWasm(b, .{
.name = "sokol_template", .name = project_name,
.mod_main = mod_main, .mod_main = mod_main,
.dep_sokol = dep_sokol, .dep_sokol = dep_sokol,
}); });
} else { } else {
try buildNative(b, "sokol_template", mod_main, has_console); try buildNative(b, project_name, mod_main, has_console);
} }
} }

View File

@ -8,6 +8,46 @@ const Position = @import("./position.zig");
const Layer = @This(); const Layer = @This();
pub const Bounds = struct {
left: i32,
right: i32,
top: i32,
bottom: i32,
pub const zero = Bounds{
.left = 0,
.right = 0,
.top = 0,
.bottom = 0,
};
pub fn initFromRect(x: i32, y: i32, width: i32, height: i32) Bounds {
return Bounds{
.left = x,
.right = x + width,
.top = y,
.bottom = y + height,
};
}
pub fn getWidth(self: Bounds) u32 {
return @intCast(self.right - self.left + 1);
}
pub fn getHeight(self: Bounds) u32 {
return @intCast(self.bottom - self.top + 1);
}
pub fn combine(lhs: Bounds, rhs: Bounds) Bounds {
return Bounds{
.left = @min(lhs.left, rhs.left),
.right = @max(lhs.right, rhs.right),
.top = @min(lhs.top, rhs.top),
.bottom = @max(lhs.bottom, rhs.bottom)
};
}
};
pub const TileVariant = struct { pub const TileVariant = struct {
pub const Chunk = struct { pub const Chunk = struct {
x: i32, x: i32,
@ -15,6 +55,10 @@ pub const TileVariant = struct {
width: u32, width: u32,
height: u32, height: u32,
data: []u32, data: []u32,
pub fn getBounds(self: Chunk) Bounds {
return Bounds.initFromRect(self.x, self.y, @intCast(self.width), @intCast(self.height));
}
}; };
pub const Data = union(enum) { pub const Data = union(enum) {
@ -94,7 +138,7 @@ pub const TileVariant = struct {
try temp_tiles.appendSlice(scratch.allocator(), tiles.items); try temp_tiles.appendSlice(scratch.allocator(), tiles.items);
} else if (node.isTag("chunk")) { } else if (node.isTag("chunk")) {
const chunk = try initChunkDataFromXml(scratch.allocator(), scratch, lexer, encoding); const chunk = try initChunkDataFromXml(arena, scratch, lexer, encoding);
try temp_chunks.append(scratch.allocator(), chunk); try temp_chunks.append(scratch.allocator(), chunk);
continue; continue;
@ -105,9 +149,16 @@ pub const TileVariant = struct {
try iter.finish("data"); try iter.finish("data");
return .{ if (temp_chunks.items.len > 0) {
.fixed = try arena.dupe(u32, temp_tiles.items) return .{
}; .chunks = try arena.dupe(Chunk, temp_chunks.items)
};
} else {
return .{
.fixed = try arena.dupe(u32, temp_tiles.items)
};
}
} }
fn parseEncoding( fn parseEncoding(
@ -136,13 +187,46 @@ pub const TileVariant = struct {
return result; return result;
} }
pub fn getBounds(self: TileVariant) void { pub fn getBounds(self: TileVariant) Bounds {
_ = self; // autofix if (self.data == .fixed) {
return Bounds.initFromRect(0, 0, @intCast(self.width), @intCast(self.height));
} else if (self.data == .chunks) {
const chunks = self.data.chunks;
var result: Bounds = .zero;
if (chunks.len > 0) {
result = chunks[0].getBounds();
for (chunks[1..]) |chunk| {
result = result.combine(chunk.getBounds());
}
}
return result;
} else {
unreachable;
}
} }
pub fn get(self: TileVariant, x: usize, y: usize) ?u32 { pub fn get(self: TileVariant, x: i32, y: i32) ?u32 {
if ((0 <= x and x < self.width) and (0 <= y and y < self.height)) { if (self.data == .fixed) {
return self.data[y * self.width + x]; if ((0 <= x and x < self.width) and (0 <= y and y < self.height)) {
const x_u32: u32 = @intCast(x);
const y_u32: u32 = @intCast(y);
return self.data.fixed[y_u32 * self.width + x_u32];
}
} else if (self.data == .chunks) {
for (self.data.chunks) |chunk| {
const ox = x - chunk.x;
const oy = y - chunk.y;
if ((0 <= ox and ox < chunk.width) and (0 <= oy and oy < chunk.height)) {
const ox_u32: u32 = @intCast(ox);
const oy_u32: u32 = @intCast(oy);
return chunk.data[oy_u32 * chunk.width + ox_u32];
}
}
} else {
unreachable;
} }
return null; return null;
} }

View File

@ -74,6 +74,15 @@ pub const List = struct {
} }
return null; return null;
} }
pub fn getBool(self: List, name: []const u8) ?bool {
if (self.get(name)) |value| {
if (value == .bool) {
return value.bool;
}
}
return null;
}
}; };
pub fn init(name: []const u8, value: Value) Property { pub fn init(name: []const u8, value: Value) Property {

View File

@ -215,7 +215,18 @@ fn getTilesetByGid(self: *const Tilemap, gid: u32) ?TilesetReference {
return result; return result;
} }
pub fn getTile(self: *const Tilemap, layer: *const Layer, tilesets: Tileset.List, x: usize, y: usize) ?Tile { pub fn getTile(self: *const Tilemap, tilesets: Tileset.List, gid: u32) ?Tile {
const tileset_ref = self.getTilesetByGid(gid & GlobalTileId.Flag.clear) orelse return null;
const tileset = tilesets.get(tileset_ref.source) orelse return null;
const id = gid - tileset_ref.first_gid;
return Tile{
.tileset = tileset,
.id = id
};
}
pub fn getTileByPosition(self: *const Tilemap, layer: *const Layer, tilesets: Tileset.List, x: usize, y: usize) ?Tile {
assert(layer.variant == .tile); assert(layer.variant == .tile);
const tile_variant = layer.variant.tile; const tile_variant = layer.variant.tile;
@ -230,6 +241,26 @@ pub fn getTile(self: *const Tilemap, layer: *const Layer, tilesets: Tileset.List
}; };
} }
pub fn getTileBounds(self: *const Tilemap) Layer.Bounds {
var result: ?Layer.Bounds = null;
for (self.layers) |layer| {
if (layer.variant != .tile) {
continue;
}
const layer_bounds = layer.variant.tile.getBounds();
if (result == null) {
result = layer_bounds;
} else {
result = layer_bounds.combine(result.?);
}
}
return result orelse .zero;
}
pub fn deinit(self: *const Tilemap) void { pub fn deinit(self: *const Tilemap) void {
self.arena.deinit(); self.arena.deinit();
} }

View File

@ -4,8 +4,11 @@ const tiled = @import("tiled");
const Math = @import("./engine/math.zig"); const Math = @import("./engine/math.zig");
const Engine = @import("./engine/root.zig"); const Engine = @import("./engine/root.zig");
const STBImage = @import("stb_image");
const Gfx = Engine.Graphics; const Gfx = Engine.Graphics;
const Audio = Engine.Audio; const Audio = Engine.Audio;
const Vec2 = Engine.Vec2;
const Rect = Engine.Math.Rect;
const Assets = @This(); const Assets = @This();
@ -17,12 +20,38 @@ const FontName = enum {
const EnumArray = std.EnumArray(FontName, Gfx.Font.Id); const EnumArray = std.EnumArray(FontName, Gfx.Font.Id);
}; };
pub const Tilemap = struct {
texture: Gfx.TextureId,
tile_size: Engine.Vec2,
pub fn getTileUV(self: Tilemap, tile_x: f32, tile_y: f32) Rect {
const texture_info = Engine.Graphics.getTextureInfo(self.texture);
const tilemap_size = Vec2.initFromInt(u32, texture_info.width, texture_info.height);
return .{
.pos = Vec2.init(tile_x, tile_y).multiply(self.tile_size).divide(tilemap_size),
.size = self.tile_size.divide(tilemap_size),
};
}
};
arena: std.heap.ArenaAllocator,
font_id: FontName.EnumArray, font_id: FontName.EnumArray,
wood01: Audio.Data.Id, wood01: Audio.Data.Id,
map: tiled.Tilemap, map: tiled.Tilemap,
tilesets: tiled.Tileset.List, tilesets: tiled.Tileset.List,
move_sound: []Audio.Data.Id,
terrain_tilemap: Tilemap,
players_tilemap: Tilemap,
weapons_tilemap: Tilemap,
pub fn init(gpa: std.mem.Allocator) !Assets { pub fn init(gpa: std.mem.Allocator) !Assets {
var arena = std.heap.ArenaAllocator.init(gpa);
errdefer arena.deinit();
const font_id_array: FontName.EnumArray = .init(.{ const font_id_array: FontName.EnumArray = .init(.{
.regular = try Gfx.addFont("regular", @embedFile("assets/roboto-font/Roboto-Regular.ttf")), .regular = try Gfx.addFont("regular", @embedFile("assets/roboto-font/Roboto-Regular.ttf")),
.bold = try Gfx.addFont("bold", @embedFile("assets/roboto-font/Roboto-Bold.ttf")), .bold = try Gfx.addFont("bold", @embedFile("assets/roboto-font/Roboto-Bold.ttf")),
@ -55,17 +84,72 @@ pub fn init(gpa: std.mem.Allocator) !Assets {
); );
var tilesets: tiled.Tileset.List = .empty; var tilesets: tiled.Tileset.List = .empty;
try tilesets.add(gpa, "tileset.tsx", tileset); try tilesets.add(gpa, "tilemap.tsx", tileset);
const players_image = try STBImage.load(@embedFile("assets/kenney_desert-shooter-pack_1.0/PNG/Players/tilemap_packed.png"));
defer players_image.deinit();
const players_texture = try Gfx.addTexture(&.{
.{
.width = players_image.width,
.height = players_image.height,
.rgba = players_image.rgba8_pixels
}
});
const tileset_image = try STBImage.load(@embedFile("assets/kenney_desert-shooter-pack_1.0/PNG/Tiles/tilemap_packed.png"));
defer tileset_image.deinit();
const tileset_texture = try Gfx.addTexture(&.{
.{
.width = tileset_image.width,
.height = tileset_image.height,
.rgba = tileset_image.rgba8_pixels
}
});
const weapons_tileset = try STBImage.load(@embedFile("assets/kenney_desert-shooter-pack_1.0/PNG/Weapons/tilemap_packed.png"));
defer weapons_tileset.deinit();
const weapons_texture = try Gfx.addTexture(&.{
.{
.width = weapons_tileset.width,
.height = weapons_tileset.height,
.rgba = weapons_tileset.rgba8_pixels
}
});
const move_c = try Audio.load(.{
.data = @embedFile("assets/kenney_desert-shooter-pack_1.0/Sounds/move-c.ogg"),
.format = .vorbis,
});
const move_d = try Audio.load(.{
.data = @embedFile("assets/kenney_desert-shooter-pack_1.0/Sounds/move-d.ogg"),
.format = .vorbis,
});
const move_sound = try arena.allocator().dupe(Audio.Data.Id, &.{ move_c, move_d });
return Assets{ return Assets{
.arena = arena,
.font_id = font_id_array, .font_id = font_id_array,
.wood01 = wood01, .wood01 = wood01,
.map = map, .map = map,
.tilesets = tilesets .tilesets = tilesets,
.move_sound = move_sound,
.terrain_tilemap = .{
.texture = tileset_texture,
.tile_size = .initFromInt(u32, tileset.tile_width, tileset.tile_height)
},
.players_tilemap = .{
.texture = players_texture,
.tile_size = .init(24, 24)
},
.weapons_tilemap = .{
.texture = weapons_texture,
.tile_size = .init(24, 24)
},
}; };
} }
pub fn deinit(self: *Assets, gpa: std.mem.Allocator) void { pub fn deinit(self: *Assets, gpa: std.mem.Allocator) void {
self.map.deinit(); self.map.deinit();
self.tilesets.deinit(gpa); self.tilesets.deinit(gpa);
self.arena.deinit();
} }

View File

@ -1,50 +0,0 @@
{
"Map/SizeTest": {
"height": 4300,
"width": 2
},
"activeFile": "map.tmx",
"expandedProjectPaths": [
],
"fileStates": {
"map.tmx": {
"scale": 1.5,
"selectedLayer": 0,
"viewCenter": {
"x": 49.33333333333337,
"y": 182
}
},
"tilemap.tsx": {
"dynamicWrapping": false,
"scaleInDock": 1.5,
"scaleInEditor": 5.5
},
"tiles.tsx": {
"dynamicWrapping": true,
"scaleInDock": 1,
"scaleInEditor": 1
}
},
"last.imagePath": "/home/rokas/code/games/game-2026-01-18/src/assets/kenney_desert-shooter-pack_1.0/PNG/Tiles/Tilemap",
"map.fixedSize": false,
"map.lastUsedFormat": "tmx",
"map.tileHeight": 16,
"map.tileWidth": 16,
"openFiles": [
"tilemap.tsx",
"map.tmx"
],
"project": "game-2026-01-18.tiled-project",
"recentFiles": [
"tilemap.tsx",
"map.tmx",
"tiles.tsx"
],
"tileset.lastUsedFormat": "tsx",
"tileset.tileSize": {
"height": 16,
"width": 16
},
"tileset.type": 0
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 B

Some files were not shown because too many files have changed in this diff Show More