implement collisions
This commit is contained in:
parent
1220b36531
commit
880cf8ab4e
@ -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) {
|
||||||
@ -143,48 +187,18 @@ pub const TileVariant = struct {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Bounds = struct {
|
|
||||||
left: i32,
|
|
||||||
right: i32,
|
|
||||||
top: i32,
|
|
||||||
bottom: i32,
|
|
||||||
|
|
||||||
pub fn width(self: Bounds) u32 {
|
|
||||||
return @intCast(self.right - self.left);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn height(self: Bounds) u32 {
|
|
||||||
return @intCast(self.bottom - self.top);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn getBounds(self: TileVariant) Bounds {
|
pub fn getBounds(self: TileVariant) Bounds {
|
||||||
if (self.data == .fixed) {
|
if (self.data == .fixed) {
|
||||||
return Bounds{
|
return Bounds.initFromRect(0, 0, @intCast(self.width), @intCast(self.height));
|
||||||
.left = 0,
|
|
||||||
.right = @intCast(self.width),
|
|
||||||
.top = 0,
|
|
||||||
.bottom = @intCast(self.height),
|
|
||||||
};
|
|
||||||
} else if (self.data == .chunks) {
|
} else if (self.data == .chunks) {
|
||||||
const chunks = self.data.chunks;
|
const chunks = self.data.chunks;
|
||||||
|
|
||||||
var result = Bounds{
|
var result: Bounds = .zero;
|
||||||
.left = 0,
|
|
||||||
.right = 0,
|
|
||||||
.top = 0,
|
|
||||||
.bottom = 0
|
|
||||||
};
|
|
||||||
if (chunks.len > 0) {
|
if (chunks.len > 0) {
|
||||||
result.left = chunks[0].x;
|
result = chunks[0].getBounds();
|
||||||
result.right = chunks[0].x + @as(i32, @intCast(chunks[0].width));
|
|
||||||
result.top = chunks[0].y;
|
|
||||||
result.bottom = chunks[0].y + @as(i32, @intCast(chunks[0].height));
|
|
||||||
for (chunks[1..]) |chunk| {
|
for (chunks[1..]) |chunk| {
|
||||||
result.left = @min(result.left, chunk.x);
|
result = result.combine(chunk.getBounds());
|
||||||
result.right = @max(result.right, chunk.x + @as(i32, @intCast(chunk.width)));
|
|
||||||
result.top = @min(result.top, chunk.y);
|
|
||||||
result.bottom = @max(result.bottom, chunk.y + @as(i32, @intCast(chunk.height)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -241,6 +241,26 @@ pub fn getTileByPosition(self: *const Tilemap, layer: *const Layer, tilesets: Ti
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,7 +84,7 @@ 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"));
|
const players_image = try STBImage.load(@embedFile("assets/kenney_desert-shooter-pack_1.0/PNG/Players/tilemap_packed.png"));
|
||||||
defer players_image.deinit();
|
defer players_image.deinit();
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="1" nextlayerid="3" nextobjectid="2">
|
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="1" nextlayerid="5" nextobjectid="2">
|
||||||
<tileset firstgid="1" source="tileset.tsx"/>
|
<tileset firstgid="1" source="tilemap.tsx"/>
|
||||||
<tileset firstgid="235" source="tilemap.tsx"/>
|
<layer id="1" name="ground" width="30" height="20">
|
||||||
<layer id="1" name="Tile Layer 1" width="30" height="20">
|
|
||||||
<data encoding="csv">
|
<data encoding="csv">
|
||||||
<chunk x="-32" y="-32" width="16" height="16">
|
<chunk x="-32" y="-32" width="16" height="16">
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
@ -18,9 +17,9 @@
|
|||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,325,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,343,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,343
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109
|
||||||
</chunk>
|
</chunk>
|
||||||
<chunk x="-16" y="-32" width="16" height="16">
|
<chunk x="-16" y="-32" width="16" height="16">
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
@ -35,15 +34,15 @@
|
|||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,65,65,0,0,0,0,0,0,
|
||||||
326,326,326,326,326,326,326,327,0,0,0,0,0,0,0,0,
|
92,92,92,92,92,92,92,93,65,65,0,0,0,0,0,0,
|
||||||
344,254,344,344,344,344,344,345,0,0,0,0,0,0,0,0,
|
110,20,110,110,110,110,110,112,110,65,0,0,0,0,0,0,
|
||||||
344,344,344,344,254,344,344,345,0,0,0,0,0,0,0,0
|
110,110,110,110,20,110,110,110,110,65,65,0,0,0,0,0
|
||||||
</chunk>
|
</chunk>
|
||||||
<chunk x="-32" y="-16" width="16" height="16">
|
<chunk x="-32" y="-16" width="16" height="16">
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,343,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,343,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,361,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
@ -59,20 +58,20 @@
|
|||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
</chunk>
|
</chunk>
|
||||||
<chunk x="-16" y="-16" width="16" height="16">
|
<chunk x="-16" y="-16" width="16" height="16">
|
||||||
344,344,254,344,344,344,344,345,0,0,0,0,0,0,0,0,
|
110,110,20,110,110,110,110,94,110,65,65,0,0,0,65,0,
|
||||||
344,254,344,344,344,344,254,345,0,0,0,0,0,0,0,0,
|
110,20,110,110,110,110,20,111,65,65,65,0,0,0,65,0,
|
||||||
362,362,362,362,362,362,362,363,0,0,0,0,299,299,0,0,
|
128,128,128,128,128,128,128,129,65,65,65,0,65,65,65,0,
|
||||||
0,0,0,299,299,299,299,299,0,299,299,299,429,299,0,0,
|
0,0,0,65,65,65,65,65,65,65,65,65,195,65,65,0,
|
||||||
0,0,0,299,299,299,299,299,299,299,299,299,299,299,0,0,
|
0,0,0,65,65,65,65,65,65,65,65,65,65,65,65,0,
|
||||||
0,0,299,299,299,299,299,299,299,299,299,299,300,299,0,0,
|
0,0,65,65,65,65,65,65,65,65,65,65,66,65,65,65,
|
||||||
0,0,299,299,299,299,300,299,299,300,299,299,299,0,0,0,
|
0,0,65,65,65,65,66,65,65,66,65,65,65,65,65,65,
|
||||||
0,0,0,299,299,299,299,299,299,299,300,299,299,0,0,0,
|
0,0,0,65,65,65,65,65,65,65,66,65,65,65,65,65,
|
||||||
0,0,0,299,299,429,299,299,299,299,299,299,299,299,0,299,
|
0,0,0,65,65,195,65,65,65,65,65,65,65,65,65,65,
|
||||||
0,0,0,299,299,299,299,300,299,299,299,299,299,299,299,299,
|
0,0,0,65,65,65,65,66,65,65,65,65,65,65,65,65,
|
||||||
0,0,0,299,299,299,299,299,299,300,299,299,429,299,299,299,
|
0,0,0,65,65,65,65,65,65,66,65,65,195,65,65,65,
|
||||||
0,0,0,299,299,299,299,299,299,299,299,299,299,299,300,299,
|
0,0,0,65,65,65,65,65,65,65,65,65,65,65,66,65,
|
||||||
0,299,299,299,299,299,300,299,299,299,299,299,299,299,299,299,
|
0,65,65,65,65,65,66,65,65,65,65,65,65,65,65,65,
|
||||||
0,0,299,299,299,299,299,299,299,299,299,299,299,299,299,299,
|
0,0,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
</chunk>
|
</chunk>
|
||||||
@ -82,15 +81,137 @@
|
|||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
65,65,65,65,65,65,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
65,65,65,65,65,65,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
65,65,65,65,65,65,65,0,0,0,0,0,0,0,0,0,
|
||||||
|
65,65,65,65,65,65,65,0,0,0,0,0,0,0,0,0,
|
||||||
|
65,65,65,65,65,65,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
65,65,65,65,65,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
65,65,65,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
</chunk>
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<layer id="3" name="walls" width="30" height="20">
|
||||||
|
<properties>
|
||||||
|
<property name="solid" type="bool" value="true"/>
|
||||||
|
</properties>
|
||||||
|
<data encoding="csv">
|
||||||
|
<chunk x="-16" y="-32" width="16" height="16">
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,1,2,2,2,2,3,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,19,110,20,110,20,21,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,19,110,110,20,20,21,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,37,38,38,38,38,39,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,73,56,174,174,174,57,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,73,174,174,56,174,57,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,73,174,174,174,174,57,0,0
|
||||||
|
</chunk>
|
||||||
|
<chunk x="-16" y="-16" width="16" height="16">
|
||||||
|
0,0,0,0,0,0,0,0,73,174,56,56,174,57,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,73,174,174,174,56,57,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,73,74,74,74,74,75,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
</chunk>
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<layer id="4" name="walls 2" width="30" height="20">
|
||||||
|
<properties>
|
||||||
|
<property name="solid" type="bool" value="true"/>
|
||||||
|
</properties>
|
||||||
|
<data encoding="csv">
|
||||||
|
<chunk x="-16" y="-32" width="16" height="16">
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,37,38,38
|
||||||
|
</chunk>
|
||||||
|
<chunk x="0" y="-32" width="16" height="16">
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
2,2,2,3,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
38,38,38,39,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
</chunk>
|
||||||
|
<chunk x="-16" y="-16" width="16" height="16">
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,73,174,174,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,73,174,174,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,73,56,174,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,73,56,56,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,73,74,74,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
</chunk>
|
||||||
|
<chunk x="0" y="-16" width="16" height="16">
|
||||||
|
56,56,174,75,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
174,174,174,75,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
174,174,56,75,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
174,56,56,75,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
74,74,74,75,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,299,299,299,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
299,299,299,299,299,299,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,299,299,299,299,299,0,0,0,0,0,0,0,0,0,
|
|
||||||
299,299,299,299,299,299,299,0,0,0,0,0,0,0,0,0,
|
|
||||||
299,299,299,299,299,299,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
299,299,299,299,299,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
299,299,299,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
299,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
</chunk>
|
</chunk>
|
||||||
|
|||||||
@ -4,7 +4,7 @@ const log = std.log.scoped(.engine);
|
|||||||
|
|
||||||
const InputSystem = @import("./input.zig");
|
const InputSystem = @import("./input.zig");
|
||||||
const KeyCode = InputSystem.KeyCode;
|
const KeyCode = InputSystem.KeyCode;
|
||||||
const Mouse = InputSystem.Mouse;
|
const MouseButton = InputSystem.MouseButton;
|
||||||
|
|
||||||
const AudioSystem = @import("./audio/root.zig");
|
const AudioSystem = @import("./audio/root.zig");
|
||||||
const AudioData = AudioSystem.Data;
|
const AudioData = AudioSystem.Data;
|
||||||
@ -27,19 +27,14 @@ pub const Nanoseconds = u64;
|
|||||||
const Frame = @This();
|
const Frame = @This();
|
||||||
|
|
||||||
pub const Input = struct {
|
pub const Input = struct {
|
||||||
down_keys: std.EnumSet(KeyCode),
|
keyboard: InputSystem.ButtonStateSet(KeyCode),
|
||||||
pressed_keys: std.EnumSet(KeyCode),
|
mouse_button: InputSystem.ButtonStateSet(MouseButton),
|
||||||
released_keys: std.EnumSet(KeyCode),
|
mouse_position: ?Vec2,
|
||||||
pressed_keys_at: std.EnumMap(KeyCode, Nanoseconds),
|
|
||||||
|
|
||||||
mouse: Mouse,
|
|
||||||
|
|
||||||
pub const empty = Input{
|
pub const empty = Input{
|
||||||
.down_keys = .initEmpty(),
|
.keyboard = .empty,
|
||||||
.pressed_keys = .initEmpty(),
|
.mouse_button = .empty,
|
||||||
.released_keys = .initEmpty(),
|
.mouse_position = null,
|
||||||
.pressed_keys_at = .init(.{}),
|
|
||||||
.mouse = .empty
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -139,7 +134,8 @@ pub fn time(self: Frame) f64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn isKeyDown(self: Frame, key_code: KeyCode) bool {
|
pub fn isKeyDown(self: Frame, key_code: KeyCode) bool {
|
||||||
return self.input.down_keys.contains(key_code);
|
const keyboard = &self.input.keyboard;
|
||||||
|
return keyboard.down.contains(key_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getKeyDownDuration(self: Frame, key_code: KeyCode) ?f32 {
|
pub fn getKeyDownDuration(self: Frame, key_code: KeyCode) ?f32 {
|
||||||
@ -147,18 +143,21 @@ pub fn getKeyDownDuration(self: Frame, key_code: KeyCode) ?f32 {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pressed_at_ns = self.input.pressed_keys_at.get(key_code).?;
|
const keyboard = &self.input.keyboard;
|
||||||
|
const pressed_at_ns = keyboard.pressed_at.get(key_code).?;
|
||||||
const duration_ns = self.time_ns - pressed_at_ns;
|
const duration_ns = self.time_ns - pressed_at_ns;
|
||||||
|
|
||||||
return @as(f32, @floatFromInt(duration_ns)) / std.time.ns_per_s;
|
return @as(f32, @floatFromInt(duration_ns)) / std.time.ns_per_s;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isKeyPressed(self: Frame, key_code: KeyCode) bool {
|
pub fn isKeyPressed(self: Frame, key_code: KeyCode) bool {
|
||||||
return self.input.pressed_keys.contains(key_code);
|
const keyboard = &self.input.keyboard;
|
||||||
|
return keyboard.pressed.contains(key_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isKeyReleased(self: Frame, key_code: KeyCode) bool {
|
pub fn isKeyReleased(self: Frame, key_code: KeyCode) bool {
|
||||||
return self.input.released_keys.contains(key_code);
|
const keyboard = &self.input.keyboard;
|
||||||
|
return keyboard.released.contains(key_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getKeyState(self: Frame, key_code: KeyCode) KeyState {
|
pub fn getKeyState(self: Frame, key_code: KeyCode) KeyState {
|
||||||
@ -170,6 +169,14 @@ pub fn getKeyState(self: Frame, key_code: KeyCode) KeyState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn isMousePressed(self: Frame, button: MouseButton) bool {
|
||||||
|
return self.input.mouse_button.pressed.contains(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isMouseDown(self: Frame, button: MouseButton) bool {
|
||||||
|
return self.input.mouse_button.down.contains(button);
|
||||||
|
}
|
||||||
|
|
||||||
fn pushAudioCommand(self: *Frame, command: AudioCommand) void {
|
fn pushAudioCommand(self: *Frame, command: AudioCommand) void {
|
||||||
const arena = self.arena.allocator();
|
const arena = self.arena.allocator();
|
||||||
|
|
||||||
|
|||||||
@ -19,38 +19,59 @@ pub const KeyCode = @Type(.{
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
pub const Mouse = struct {
|
pub const MouseButton = enum(i3) {
|
||||||
pub const Button = enum {
|
left = @intFromEnum(sokol.app.Mousebutton.LEFT),
|
||||||
left,
|
right = @intFromEnum(sokol.app.Mousebutton.RIGHT),
|
||||||
right,
|
middle = @intFromEnum(sokol.app.Mousebutton.MIDDLE),
|
||||||
middle,
|
_
|
||||||
|
};
|
||||||
|
|
||||||
pub fn fromSokol(mouse_button: sokol.app.Mousebutton) ?Button {
|
pub fn ButtonStateSet(E: type) type {
|
||||||
return switch(mouse_button) {
|
return struct {
|
||||||
.LEFT => Button.left,
|
const Self = @This();
|
||||||
.RIGHT => Button.right,
|
|
||||||
.MIDDLE => Button.middle,
|
down: std.EnumSet(E),
|
||||||
else => null
|
pressed: std.EnumSet(E),
|
||||||
|
released: std.EnumSet(E),
|
||||||
|
pressed_at: std.EnumMap(E, Nanoseconds),
|
||||||
|
|
||||||
|
pub const empty = Self{
|
||||||
|
.down = .initEmpty(),
|
||||||
|
.pressed = .initEmpty(),
|
||||||
|
.released = .initEmpty(),
|
||||||
|
.pressed_at = .init(.{}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn press(self: *Self, button: E, now: Nanoseconds) void {
|
||||||
|
self.pressed_at.put(button, now);
|
||||||
|
self.pressed.insert(button);
|
||||||
|
self.down.insert(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release(self: *Self, button: E) void {
|
||||||
|
self.down.remove(button);
|
||||||
|
self.released.insert(button);
|
||||||
|
self.pressed_at.remove(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn releaseAll(self: *Self) void {
|
||||||
|
var iter = self.down.iterator();
|
||||||
|
while (iter.next()) |key_code| {
|
||||||
|
self.released.insert(key_code);
|
||||||
|
}
|
||||||
|
self.down = .initEmpty();
|
||||||
|
self.pressed_at = .init(.{});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
position: ?Vec2,
|
|
||||||
buttons: std.EnumSet(Button),
|
|
||||||
|
|
||||||
pub const empty = Mouse{
|
|
||||||
.position = null,
|
|
||||||
.buttons = .initEmpty()
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Event = union(enum) {
|
pub const Event = union(enum) {
|
||||||
mouse_pressed: struct {
|
mouse_pressed: struct {
|
||||||
button: Mouse.Button,
|
button: MouseButton,
|
||||||
position: Vec2,
|
position: Vec2,
|
||||||
},
|
},
|
||||||
mouse_released: struct {
|
mouse_released: struct {
|
||||||
button: Mouse.Button,
|
button: MouseButton,
|
||||||
position: Vec2,
|
position: Vec2,
|
||||||
},
|
},
|
||||||
mouse_move: Vec2,
|
mouse_move: Vec2,
|
||||||
@ -72,39 +93,31 @@ pub fn processEvent(frame: *Frame, event: Event) void {
|
|||||||
switch (event) {
|
switch (event) {
|
||||||
.key_pressed => |opts| {
|
.key_pressed => |opts| {
|
||||||
if (!opts.repeat) {
|
if (!opts.repeat) {
|
||||||
input.pressed_keys_at.put(opts.code, frame.time_ns);
|
input.keyboard.press(opts.code, frame.time_ns);
|
||||||
input.pressed_keys.insert(opts.code);
|
|
||||||
input.down_keys.insert(opts.code);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.key_released => |key_code| {
|
.key_released => |key_code| {
|
||||||
input.down_keys.remove(key_code);
|
input.keyboard.release(key_code);
|
||||||
input.released_keys.insert(key_code);
|
|
||||||
input.pressed_keys_at.remove(key_code);
|
|
||||||
},
|
},
|
||||||
.mouse_leave => {
|
.mouse_leave => {
|
||||||
var iter = input.down_keys.iterator();
|
input.keyboard.releaseAll();
|
||||||
while (iter.next()) |key_code| {
|
|
||||||
input.released_keys.insert(key_code);
|
|
||||||
}
|
|
||||||
input.down_keys = .initEmpty();
|
|
||||||
input.pressed_keys_at = .init(.{});
|
|
||||||
|
|
||||||
input.mouse = .empty;
|
input.mouse_position = null;
|
||||||
|
input.mouse_button = .empty;
|
||||||
},
|
},
|
||||||
.mouse_enter => |pos| {
|
.mouse_enter => |pos| {
|
||||||
input.mouse.position = pos;
|
input.mouse_position = pos;
|
||||||
},
|
},
|
||||||
.mouse_move => |pos| {
|
.mouse_move => |pos| {
|
||||||
input.mouse.position = pos;
|
input.mouse_position = pos;
|
||||||
},
|
},
|
||||||
.mouse_pressed => |opts| {
|
.mouse_pressed => |opts| {
|
||||||
input.mouse.position = opts.position;
|
input.mouse_position = opts.position;
|
||||||
input.mouse.buttons.insert(opts.button);
|
input.mouse_button.press(opts.button, frame.time_ns);
|
||||||
},
|
},
|
||||||
.mouse_released => |opts| {
|
.mouse_released => |opts| {
|
||||||
input.mouse.position = opts.position;
|
input.mouse_position = opts.position;
|
||||||
input.mouse.buttons.remove(opts.button);
|
input.mouse_button.release(opts.button);
|
||||||
},
|
},
|
||||||
else => {}
|
else => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -124,14 +124,22 @@ pub const Vec2 = extern struct {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lengthSqr(self: Vec2) f32 {
|
||||||
|
return self.x*self.x + self.y*self.y;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn length(self: Vec2) f32 {
|
pub fn length(self: Vec2) f32 {
|
||||||
return @sqrt(self.x*self.x + self.y*self.y);
|
return @sqrt(self.lengthSqr());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn distance(self: Vec2, other: Vec2) f32 {
|
pub fn distance(self: Vec2, other: Vec2) f32 {
|
||||||
return self.sub(other).length();
|
return self.sub(other).length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn distanceSqr(self: Vec2, other: Vec2) f32 {
|
||||||
|
return self.sub(other).lengthSqr();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn limitLength(self: Vec2, max_length: f32) Vec2 {
|
pub fn limitLength(self: Vec2, max_length: f32) Vec2 {
|
||||||
const self_length = self.length();
|
const self_length = self.length();
|
||||||
if (self_length > max_length) {
|
if (self_length > max_length) {
|
||||||
@ -395,6 +403,10 @@ pub const Rect = struct {
|
|||||||
return self.pos.y + self.size.y;
|
return self.pos.y + self.size.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn center(self: Rect) Vec2 {
|
||||||
|
return self.pos.add(self.size.multiplyScalar(0.5));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn multiply(self: Rect, xy: Vec2) Rect {
|
pub fn multiply(self: Rect, xy: Vec2) Rect {
|
||||||
return Rect{
|
return Rect{
|
||||||
.pos = self.pos.multiply(xy),
|
.pos = self.pos.multiply(xy),
|
||||||
|
|||||||
@ -165,11 +165,11 @@ fn sokolFrame(self: *Engine) !void {
|
|||||||
|
|
||||||
var revert_mouse_position: ?Vec2 = null;
|
var revert_mouse_position: ?Vec2 = null;
|
||||||
if (self.canvas_size) |canvas_size| {
|
if (self.canvas_size) |canvas_size| {
|
||||||
if (self.frame.input.mouse.position) |mouse| {
|
if (self.frame.input.mouse_position) |mouse| {
|
||||||
const transform = ScreenScalar.init(screen_size, canvas_size);
|
const transform = ScreenScalar.init(screen_size, canvas_size);
|
||||||
|
|
||||||
revert_mouse_position = mouse;
|
revert_mouse_position = mouse;
|
||||||
self.frame.input.mouse.position = mouse.sub(transform.translation).divideScalar(transform.scale);
|
self.frame.input.mouse_position = mouse.sub(transform.translation).divideScalar(transform.scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,8 +195,10 @@ fn sokolFrame(self: *Engine) !void {
|
|||||||
|
|
||||||
try self.game.tick(&self.frame);
|
try self.game.tick(&self.frame);
|
||||||
|
|
||||||
frame.input.pressed_keys = .initEmpty();
|
frame.input.keyboard.pressed = .initEmpty();
|
||||||
frame.input.released_keys = .initEmpty();
|
frame.input.keyboard.released = .initEmpty();
|
||||||
|
frame.input.mouse_button.pressed = .initEmpty();
|
||||||
|
frame.input.mouse_button.released = .initEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.canvas_size) |canvas_size| {
|
if (self.canvas_size) |canvas_size| {
|
||||||
@ -234,7 +236,7 @@ fn sokolFrame(self: *Engine) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (revert_mouse_position) |pos| {
|
if (revert_mouse_position) |pos| {
|
||||||
self.frame.input.mouse.position = pos;
|
self.frame.input.mouse_position = pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +264,6 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool {
|
|||||||
defer zone.deinit();
|
defer zone.deinit();
|
||||||
|
|
||||||
const e = e_ptr.*;
|
const e = e_ptr.*;
|
||||||
const MouseButton = Input.Mouse.Button;
|
|
||||||
|
|
||||||
if (imgui.handleEvent(e)) {
|
if (imgui.handleEvent(e)) {
|
||||||
if (self.mouse_inside) {
|
if (self.mouse_inside) {
|
||||||
@ -274,13 +275,11 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
blk: switch (e.type) {
|
switch (e.type) {
|
||||||
.MOUSE_DOWN => {
|
.MOUSE_DOWN => {
|
||||||
const mouse_button = MouseButton.fromSokol(e.mouse_button) orelse break :blk;
|
|
||||||
|
|
||||||
Input.processEvent(&self.frame, .{
|
Input.processEvent(&self.frame, .{
|
||||||
.mouse_pressed = .{
|
.mouse_pressed = .{
|
||||||
.button = mouse_button,
|
.button = @enumFromInt(@intFromEnum(e.mouse_button)),
|
||||||
.position = Vec2.init(e.mouse_x, e.mouse_y)
|
.position = Vec2.init(e.mouse_x, e.mouse_y)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -288,11 +287,9 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
.MOUSE_UP => {
|
.MOUSE_UP => {
|
||||||
const mouse_button = MouseButton.fromSokol(e.mouse_button) orelse break :blk;
|
|
||||||
|
|
||||||
Input.processEvent(&self.frame, .{
|
Input.processEvent(&self.frame, .{
|
||||||
.mouse_released = .{
|
.mouse_released = .{
|
||||||
.button = mouse_button,
|
.button = @enumFromInt(@intFromEnum(e.mouse_button)),
|
||||||
.position = Vec2.init(e.mouse_x, e.mouse_y)
|
.position = Vec2.init(e.mouse_x, e.mouse_y)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
442
src/game.zig
442
src/game.zig
@ -2,6 +2,7 @@ const std = @import("std");
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const clamp = std.math.clamp;
|
const clamp = std.math.clamp;
|
||||||
|
const log = std.log.scoped(.game);
|
||||||
|
|
||||||
const Assets = @import("./assets.zig");
|
const Assets = @import("./assets.zig");
|
||||||
const Tilemap = Assets.Tilemap;
|
const Tilemap = Assets.Tilemap;
|
||||||
@ -17,6 +18,9 @@ const rgba = Engine.Math.rgba;
|
|||||||
const Range = Engine.Math.Range;
|
const Range = Engine.Math.Range;
|
||||||
const TextureId = Engine.Graphics.TextureId;
|
const TextureId = Engine.Graphics.TextureId;
|
||||||
const AudioId = Engine.Audio.Data.Id;
|
const AudioId = Engine.Audio.Data.Id;
|
||||||
|
const Sprite = Engine.Graphics.Sprite;
|
||||||
|
|
||||||
|
const RaycastTileIterator = @import("./raycast_tile_iterator.zig");
|
||||||
|
|
||||||
const Game = @This();
|
const Game = @This();
|
||||||
|
|
||||||
@ -94,6 +98,69 @@ const AudioBundle = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Bullet = struct {
|
||||||
|
position: Vec2,
|
||||||
|
velocity: Vec2
|
||||||
|
};
|
||||||
|
|
||||||
|
const Tile = struct {
|
||||||
|
sprites_buffer: [4]Sprite,
|
||||||
|
sprites_len: usize,
|
||||||
|
|
||||||
|
solid: bool,
|
||||||
|
|
||||||
|
const empty = Tile{
|
||||||
|
.sprites_buffer = undefined,
|
||||||
|
.sprites_len = 0,
|
||||||
|
.solid = false
|
||||||
|
};
|
||||||
|
|
||||||
|
fn sprites(self: *Tile) []Sprite {
|
||||||
|
return self.sprites_buffer[0..self.sprites_len];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn appendSprite(self: *Tile, sprite: Sprite) void {
|
||||||
|
var list: std.ArrayList(Sprite) = .{
|
||||||
|
.items = self.sprites(),
|
||||||
|
.capacity = self.sprites_buffer.len
|
||||||
|
};
|
||||||
|
|
||||||
|
if (list.items.len == list.capacity) {
|
||||||
|
_ = list.orderedRemove(0);
|
||||||
|
log.warn("Too many sprites on a single tile", .{});
|
||||||
|
}
|
||||||
|
list.appendAssumeCapacity(sprite);
|
||||||
|
|
||||||
|
self.sprites_len = list.items.len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const TileGrid = struct {
|
||||||
|
origin: Vec2,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
tile_size: Vec2,
|
||||||
|
tiles: []Tile,
|
||||||
|
|
||||||
|
pub fn get(self: TileGrid, x: usize, y: usize) *Tile {
|
||||||
|
assert(0 <= x and x < self.width);
|
||||||
|
assert(0 <= y and y < self.height);
|
||||||
|
return &self.tiles[y * self.width + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isInBounds(self: TileGrid, x: i32, y: i32) bool {
|
||||||
|
return (0 <= x and x < @as(i32, @intCast(self.width))) and (0 <= y and y < @as(i32, @intCast(self.height)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toTileSpace(self: TileGrid, pos: Vec2) Vec2 {
|
||||||
|
return pos.sub(self.origin).divide(self.tile_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fromTileSpace(self: TileGrid, pos: Vec2) Vec2 {
|
||||||
|
return pos.multiply(self.tile_size).add(self.origin);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
rng: RNGState,
|
rng: RNGState,
|
||||||
@ -105,6 +172,9 @@ last_faced_left: bool = false,
|
|||||||
player_walk_sound: AudioBundle = .empty,
|
player_walk_sound: AudioBundle = .empty,
|
||||||
hand_offset: Vec2 = .zero,
|
hand_offset: Vec2 = .zero,
|
||||||
|
|
||||||
|
bullets: std.ArrayList(Bullet) = .empty,
|
||||||
|
tilegrid: TileGrid,
|
||||||
|
|
||||||
player_anim: Animation,
|
player_anim: Animation,
|
||||||
|
|
||||||
pub fn init(gpa: Allocator, seed: u64, assets: *Assets) !Game {
|
pub fn init(gpa: Allocator, seed: u64, assets: *Assets) !Game {
|
||||||
@ -125,18 +195,76 @@ pub fn init(gpa: Allocator, seed: u64, assets: *Assets) !Game {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const tilemap = assets.terrain_tilemap;
|
||||||
|
const map_bounds = assets.map.getTileBounds();
|
||||||
|
|
||||||
|
const map_width = map_bounds.getWidth();
|
||||||
|
const map_height = map_bounds.getHeight();
|
||||||
|
const tilegrid = TileGrid{
|
||||||
|
.origin = Vec2.initFromInt(i32, map_bounds.left, map_bounds.top).multiply(tilemap.tile_size),
|
||||||
|
.width = map_width,
|
||||||
|
.height = map_height,
|
||||||
|
.tile_size = tilemap.tile_size,
|
||||||
|
.tiles = try arena.allocator().alloc(Tile, map_width * map_height),
|
||||||
|
};
|
||||||
|
|
||||||
|
@memset(tilegrid.tiles, .empty);
|
||||||
|
|
||||||
|
{
|
||||||
|
const texture_info = Engine.Graphics.getTextureInfo(tilemap.texture);
|
||||||
|
const tilemap_size = Vec2.initFromInt(u32, texture_info.width, texture_info.height);
|
||||||
|
|
||||||
|
for (assets.map.layers) |layer| {
|
||||||
|
if (layer.variant != .tile) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const is_layer_solid = layer.properties.getBool("solid") orelse false;
|
||||||
|
|
||||||
|
const tile_layer = layer.variant.tile;
|
||||||
|
const layer_bounds = tile_layer.getBounds();
|
||||||
|
for (0..layer_bounds.getHeight()) |oy| {
|
||||||
|
const y = layer_bounds.top + @as(i32, @intCast(oy));
|
||||||
|
|
||||||
|
for (0..layer_bounds.getWidth()) |ox| {
|
||||||
|
const x = layer_bounds.left + @as(i32, @intCast(ox));
|
||||||
|
const tile_gid = tile_layer.get(x, y) orelse continue;
|
||||||
|
if (tile_gid == 0) continue;
|
||||||
|
const tile = assets.map.getTile(assets.tilesets, tile_gid) orelse continue;
|
||||||
|
|
||||||
|
const tile_id_f32: f32 = @floatFromInt(tile.id);
|
||||||
|
const width_in_tiles = tilemap_size.x / tilemap.tile_size.x;
|
||||||
|
const tile_x = @rem(tile_id_f32, width_in_tiles);
|
||||||
|
const tile_y =@divFloor(tile_id_f32, width_in_tiles);
|
||||||
|
|
||||||
|
const tilegrid_tile = tilegrid.get(
|
||||||
|
@intCast(x - map_bounds.left),
|
||||||
|
@intCast(y - map_bounds.top)
|
||||||
|
);
|
||||||
|
tilegrid_tile.solid |= is_layer_solid;
|
||||||
|
tilegrid_tile.appendSprite(.{
|
||||||
|
.texture = tilemap.texture,
|
||||||
|
.uv = tilemap.getTileUV(tile_x, tile_y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Game{
|
return Game{
|
||||||
.arena = arena,
|
.arena = arena,
|
||||||
.gpa = gpa,
|
.gpa = gpa,
|
||||||
.assets = assets,
|
.assets = assets,
|
||||||
.player = findSpawnpoint(assets) orelse .init(0, 0),
|
.player = findSpawnpoint(assets) orelse .init(0, 0),
|
||||||
.player_anim = player_anim,
|
.player_anim = player_anim,
|
||||||
.rng = RNGState.init(seed)
|
.rng = RNGState.init(seed),
|
||||||
|
.tilegrid = tilegrid
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Game) void {
|
pub fn deinit(self: *Game) void {
|
||||||
self.arena.deinit();
|
self.arena.deinit();
|
||||||
|
self.bullets.deinit(self.gpa);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn findSpawnpoint(assets: *Assets) ?Vec2 {
|
fn findSpawnpoint(assets: *Assets) ?Vec2 {
|
||||||
@ -187,42 +315,181 @@ fn drawTile(frame: *Engine.Frame, opts: DrawTileOptions) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn drawTilemap(self: *Game, frame: *Engine.Frame) void {
|
fn drawTilemap(self: *Game, frame: *Engine.Frame) void {
|
||||||
const tilemap = self.assets.terrain_tilemap;
|
for (0..self.tilegrid.height) |y| {
|
||||||
const texture_info = Engine.Graphics.getTextureInfo(tilemap.texture);
|
for (0..self.tilegrid.width) |x| {
|
||||||
const tilemap_size = Vec2.initFromInt(u32,texture_info.width, texture_info.height);
|
const tile = self.tilegrid.get(x, y);
|
||||||
|
if (tile.sprites_len == 0) continue;
|
||||||
|
|
||||||
const map = self.assets.map;
|
const tile_pos = Vec2.initFromInt(usize, x, y).multiply(self.tilegrid.tile_size);
|
||||||
|
const tile_rect = Rect{
|
||||||
|
.pos = self.tilegrid.origin.add(tile_pos),
|
||||||
|
.size = self.tilegrid.tile_size
|
||||||
|
};
|
||||||
|
|
||||||
for (map.layers) |layer| {
|
const color = rgb(255, 255, 255);
|
||||||
if (layer.variant != .tile) {
|
for (tile.sprites()) |sprite| {
|
||||||
continue;
|
frame.drawRectangle(.{
|
||||||
}
|
.rect = tile_rect,
|
||||||
|
.color = color,
|
||||||
const tile_layer = layer.variant.tile;
|
.sprite = sprite
|
||||||
const layer_bounds = tile_layer.getBounds();
|
|
||||||
for (0..layer_bounds.height()) |oy| {
|
|
||||||
const y = layer_bounds.top + @as(i32, @intCast(oy));
|
|
||||||
|
|
||||||
for (0..layer_bounds.width()) |ox| {
|
|
||||||
const x = layer_bounds.left + @as(i32, @intCast(ox));
|
|
||||||
const tile_gid = tile_layer.get(x, y) orelse continue;
|
|
||||||
const tile = map.getTile(self.assets.tilesets, tile_gid) orelse continue;
|
|
||||||
|
|
||||||
const tile_id_f32: f32 = @floatFromInt(tile.id);
|
|
||||||
const width_in_tiles = tilemap_size.x / tilemap.tile_size.x;
|
|
||||||
const tile_x = @rem(tile_id_f32, width_in_tiles);
|
|
||||||
const tile_y = @divFloor(tile_id_f32, width_in_tiles);
|
|
||||||
|
|
||||||
drawTile(frame, .{
|
|
||||||
.pos = Vec2.initFromInt(i32, x, y).multiply(tilemap.tile_size),
|
|
||||||
.tilemap = tilemap,
|
|
||||||
.tile = .init(tile_x, tile_y)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CollisionResult = struct {
|
||||||
|
time: f32,
|
||||||
|
normal: Vec2,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn sweptAABB(b1: Rect, v1: Vec2, b2: Rect) ?CollisionResult {
|
||||||
|
var entry_x: f32 = undefined;
|
||||||
|
var exit_x: f32 = undefined;
|
||||||
|
if (v1.x > 0) {
|
||||||
|
entry_x = b2.left() - b1.right();
|
||||||
|
exit_x = b2.right() - b1.left();
|
||||||
|
} else {
|
||||||
|
entry_x = b2.right() - b1.left();
|
||||||
|
exit_x = b2.left() - b1.right();
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry_y: f32 = undefined;
|
||||||
|
var exit_y: f32 = undefined;
|
||||||
|
if (v1.y > 0) {
|
||||||
|
entry_y = b2.top() - b1.bottom();
|
||||||
|
exit_y = b2.bottom() - b1.top();
|
||||||
|
} else {
|
||||||
|
entry_y = b2.bottom() - b1.top();
|
||||||
|
exit_y = b2.top() - b1.bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
const inf = std.math.inf(f32);
|
||||||
|
|
||||||
|
var entry_x_time: f32 = undefined;
|
||||||
|
var exit_x_time: f32 = undefined;
|
||||||
|
if (v1.x == 0) {
|
||||||
|
entry_x_time = -inf;
|
||||||
|
exit_x_time = inf;
|
||||||
|
} else {
|
||||||
|
entry_x_time = entry_x / v1.x;
|
||||||
|
exit_x_time = exit_x / v1.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry_y_time: f32 = undefined;
|
||||||
|
var exit_y_time: f32 = undefined;
|
||||||
|
if (v1.y == 0) {
|
||||||
|
entry_y_time = -inf;
|
||||||
|
exit_y_time = inf;
|
||||||
|
} else {
|
||||||
|
entry_y_time = entry_y / v1.y;
|
||||||
|
exit_y_time = exit_y / v1.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entry_time = @max(entry_x_time, entry_y_time);
|
||||||
|
const exit_time = @min(exit_x_time, exit_y_time);
|
||||||
|
|
||||||
|
if (entry_time > exit_time or
|
||||||
|
(entry_x_time < 0 and entry_y_time < 0) or
|
||||||
|
entry_x_time > 1.0 or
|
||||||
|
entry_y_time > 1.0
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = CollisionResult{
|
||||||
|
.time = entry_time,
|
||||||
|
.normal = .zero
|
||||||
|
};
|
||||||
|
|
||||||
|
if (entry_x_time > entry_y_time) {
|
||||||
|
result.normal.x = if ((exit_x - entry_x) < 0.0) 1 else -1;
|
||||||
|
} else {
|
||||||
|
result.normal.y = if ((exit_y - entry_y) < 0.0) 1 else -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getStepBounds(rect: Rect, step: Vec2) Rect {
|
||||||
|
var result = rect;
|
||||||
|
|
||||||
|
if (step.x >= 0) {
|
||||||
|
result.size.x += step.x;
|
||||||
|
} else {
|
||||||
|
result.pos.x += step.x;
|
||||||
|
result.size.x -= step.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step.y >= 0) {
|
||||||
|
result.size.y += step.y;
|
||||||
|
} else {
|
||||||
|
result.pos.y += step.y;
|
||||||
|
result.size.y -= step.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collisionResponseSlide(step: *Vec2, collision: CollisionResult) void {
|
||||||
|
var new_step = step.multiplyScalar(collision.time);
|
||||||
|
|
||||||
|
const remaining_time = 1 - collision.time;
|
||||||
|
const dotprod = (step.x * collision.normal.y + step.y * collision.normal.x) * remaining_time;
|
||||||
|
new_step.x += dotprod * collision.normal.y;
|
||||||
|
new_step.y += dotprod * collision.normal.x;
|
||||||
|
|
||||||
|
step.* = new_step;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn listNearestSolids(self: *Game, gpa: Allocator, bounds: Rect) ![]Rect {
|
||||||
|
var result: std.ArrayList(Rect) = .empty;
|
||||||
|
errdefer result.deinit(gpa);
|
||||||
|
|
||||||
|
var distances: std.ArrayList(f32) = .empty;
|
||||||
|
defer distances.deinit(gpa);
|
||||||
|
|
||||||
|
const top_left = self.tilegrid.toTileSpace(Vec2.init(bounds.left(), bounds.top()));
|
||||||
|
const bottom_right = self.tilegrid.toTileSpace(Vec2.init(bounds.right(), bounds.bottom()));
|
||||||
|
|
||||||
|
var y = @floor(top_left.y);
|
||||||
|
while (y < bottom_right.y) : (y += 1) {
|
||||||
|
var x = @floor(top_left.x);
|
||||||
|
while (x < bottom_right.x) : (x += 1) {
|
||||||
|
const x_i32: i32 = @intFromFloat(x);
|
||||||
|
const y_i32: i32 = @intFromFloat(y);
|
||||||
|
if (!self.tilegrid.isInBounds(x_i32, y_i32)) continue;
|
||||||
|
|
||||||
|
const tile = self.tilegrid.get(@intCast(x_i32), @intCast(y_i32));
|
||||||
|
if (tile.solid) {
|
||||||
|
const tile_collider = Rect{
|
||||||
|
.pos = self.tilegrid.fromTileSpace(Vec2.init(x, y)),
|
||||||
|
.size = self.tilegrid.tile_size
|
||||||
|
};
|
||||||
|
try result.append(gpa, tile_collider);
|
||||||
|
try distances.append(gpa, Vec2.distanceSqr(tile_collider.center(), bounds.center()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Context = struct {
|
||||||
|
bounds_center: Vec2,
|
||||||
|
|
||||||
|
fn lessThanFn(ctx: @This(), lhs: Rect, rhs: Rect) bool {
|
||||||
|
const lhs_distance = Vec2.distanceSqr(lhs.center(), ctx.bounds_center);
|
||||||
|
const rhs_distance = Vec2.distanceSqr(rhs.center(), ctx.bounds_center);
|
||||||
|
return lhs_distance < rhs_distance;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ctx = Context{
|
||||||
|
.bounds_center = bounds.center()
|
||||||
|
};
|
||||||
|
std.mem.sort(Rect, result.items, ctx, Context.lessThanFn);
|
||||||
|
|
||||||
|
return try result.toOwnedSlice(gpa);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
||||||
const dt = frame.deltaTime();
|
const dt = frame.deltaTime();
|
||||||
|
|
||||||
@ -242,6 +509,8 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
|||||||
frame.pushTransform(camera_offset, .init(1, 1));
|
frame.pushTransform(camera_offset, .init(1, 1));
|
||||||
defer frame.popTransform();
|
defer frame.popTransform();
|
||||||
|
|
||||||
|
self.drawTilemap(frame);
|
||||||
|
|
||||||
var dir = Vec2.init(0, 0);
|
var dir = Vec2.init(0, 0);
|
||||||
if (frame.isKeyDown(.W)) {
|
if (frame.isKeyDown(.W)) {
|
||||||
dir.y -= 1;
|
dir.y -= 1;
|
||||||
@ -269,11 +538,59 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
|||||||
self.player_anim_state.frame_index = 0;
|
self.player_anim_state.frame_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.player = self.player.add(dir.multiplyScalar(50 * dt));
|
const velocity = dir.multiplyScalar(50);
|
||||||
|
var step = velocity.multiplyScalar(dt);
|
||||||
|
|
||||||
const regular_font = self.assets.font_id.get(.regular);
|
const player_collider_size = Vec2.init(20, 20);
|
||||||
|
const player_collider = Rect{
|
||||||
|
.pos = self.player.sub(player_collider_size.divideScalar(2)),
|
||||||
|
.size = player_collider_size,
|
||||||
|
};
|
||||||
|
|
||||||
self.drawTilemap(frame);
|
{
|
||||||
|
// const solids = try self.listNearestSolids(frame.arena.allocator(), getStepBounds(player_collider, step));
|
||||||
|
// for (solids) |solid| {
|
||||||
|
// if (sweptAABB(player_collider, step, solid)) |collision| {
|
||||||
|
// collisionResponseSlide(&step, collision);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
inline for (.{
|
||||||
|
// Vec2.init(0, 0),
|
||||||
|
// Vec2.init(1, 0),
|
||||||
|
Vec2.init(0, 1),
|
||||||
|
// Vec2.init(1, 1),
|
||||||
|
}) |corner_offset| {
|
||||||
|
const corner = player_collider.pos.add(player_collider.size.multiply(corner_offset));
|
||||||
|
var iter = RaycastTileIterator.init(
|
||||||
|
self.tilegrid.toTileSpace(corner),
|
||||||
|
self.tilegrid.toTileSpace(corner.add(step)),
|
||||||
|
);
|
||||||
|
while (iter.next()) |collision| {
|
||||||
|
if (!self.tilegrid.isInBounds(collision.tile_x, collision.tile_y)) break;
|
||||||
|
const tile = self.tilegrid.get(@intCast(collision.tile_x), @intCast(collision.tile_y));
|
||||||
|
if (tile.solid) {
|
||||||
|
const time = collision.distance / step.divide(self.tilegrid.tile_size).length();
|
||||||
|
collisionResponseSlide(&step, .{
|
||||||
|
.normal = .initFromInt(i32, collision.normal_x, collision.normal_y),
|
||||||
|
.time = time
|
||||||
|
});
|
||||||
|
|
||||||
|
const pos = Vec2.initFromInt(i32, collision.tile_x, collision.tile_y).multiply(self.tilegrid.tile_size).add(self.tilegrid.origin);
|
||||||
|
frame.drawRectangle(.{
|
||||||
|
.rect = Rect{
|
||||||
|
.pos = pos,
|
||||||
|
.size = self.tilegrid.tile_size,
|
||||||
|
},
|
||||||
|
.color = rgba(200, 20, 20, 0.5)
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.player = self.player.add(step);
|
||||||
|
|
||||||
if (dir.x < 0) {
|
if (dir.x < 0) {
|
||||||
self.last_faced_left = true;
|
self.last_faced_left = true;
|
||||||
@ -297,8 +614,14 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frame.drawRectangle(.{
|
||||||
|
.rect = player_collider,
|
||||||
|
.color = rgba(20, 255, 255, 0.5)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const max_hand_length = 32;
|
const max_hand_length = 32;
|
||||||
if (frame.input.mouse.position) |mouse_screen| {
|
if (frame.input.mouse_position) |mouse_screen| {
|
||||||
const mouse = mouse_screen.sub(camera_offset);
|
const mouse = mouse_screen.sub(camera_offset);
|
||||||
const player_to_mouse = mouse.sub(self.player);
|
const player_to_mouse = mouse.sub(self.player);
|
||||||
self.hand_offset = mouse.sub(self.player).limitLength(max_hand_length);
|
self.hand_offset = mouse.sub(self.player).limitLength(max_hand_length);
|
||||||
@ -311,6 +634,38 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
|||||||
.tile = .init(4, 2)
|
.tile = .init(4, 2)
|
||||||
});
|
});
|
||||||
frame.hide_cursor = true;
|
frame.hide_cursor = true;
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
var iter = RaycastTileIterator.init(
|
||||||
|
self.tilegrid.toTileSpace(self.player),
|
||||||
|
self.tilegrid.toTileSpace(mouse),
|
||||||
|
);
|
||||||
|
while (iter.next()) |item| {
|
||||||
|
if (!self.tilegrid.isInBounds(item.tile_x, item.tile_y)) break;
|
||||||
|
|
||||||
|
const tile = self.tilegrid.get(@intCast(item.tile_x), @intCast(item.tile_y));
|
||||||
|
const pos = Vec2.initFromInt(i32, item.tile_x, item.tile_y).multiply(self.tilegrid.tile_size).add(self.tilegrid.origin);
|
||||||
|
|
||||||
|
frame.drawRectangle(.{
|
||||||
|
.rect = Rect{
|
||||||
|
.pos = pos,
|
||||||
|
.size = self.tilegrid.tile_size,
|
||||||
|
},
|
||||||
|
.color = rgba(200, 20, 20, 0.5)
|
||||||
|
});
|
||||||
|
frame.drawRectangle(.{
|
||||||
|
.rect = Rect{
|
||||||
|
.pos = pos.add(self.tilegrid.tile_size.multiplyScalar(0.25)).add(self.tilegrid.tile_size.multiplyScalar(0.25).multiply(.initFromInt(i32, item.normal_x, item.normal_y))),
|
||||||
|
.size = self.tilegrid.tile_size.divideScalar(2),
|
||||||
|
},
|
||||||
|
.color = rgba(20, 200, 20, 0.5)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tile.solid) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hand = self.player.add(self.hand_offset);
|
const hand = self.player.add(self.hand_offset);
|
||||||
@ -329,10 +684,25 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
|||||||
.rotation = self.hand_offset.getAngle()
|
.rotation = self.hand_offset.getAngle()
|
||||||
});
|
});
|
||||||
|
|
||||||
frame.drawText(self.player, "Player", .{
|
if (frame.isMousePressed(.left)) {
|
||||||
.font = regular_font,
|
try self.bullets.append(self.gpa, .{
|
||||||
.size = 16
|
.position = hand,
|
||||||
|
.velocity = self.hand_offset.normalized().multiplyScalar(100)
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (self.bullets.items) |*bullet| {
|
||||||
|
bullet.position = bullet.position.add(bullet.velocity.multiplyScalar(dt));
|
||||||
|
|
||||||
|
const bullet_size = Vec2.init(16, 16);
|
||||||
|
frame.drawRectangle(.{
|
||||||
|
.rect = .{
|
||||||
|
.pos = bullet.position.sub(bullet_size.multiplyScalar(0.5)),
|
||||||
|
.size = bullet_size
|
||||||
|
},
|
||||||
|
.color = rgb(200, 10, 10)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug(self: *Game) !void {
|
pub fn debug(self: *Game) !void {
|
||||||
|
|||||||
97
src/raycast_tile_iterator.zig
Normal file
97
src/raycast_tile_iterator.zig
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
const Engine = @import("./engine/root.zig");
|
||||||
|
const Vec2 = Engine.Vec2;
|
||||||
|
|
||||||
|
// DDA Algorithm: https://lodev.org/cgtutor/raycasting.html
|
||||||
|
const RaycastTileIterator = @This();
|
||||||
|
|
||||||
|
max_distance: f32,
|
||||||
|
ray_length_1d: Vec2,
|
||||||
|
check_x: i32,
|
||||||
|
check_y: i32,
|
||||||
|
step_x: i32,
|
||||||
|
step_y: i32,
|
||||||
|
unit_step_size: Vec2,
|
||||||
|
|
||||||
|
const Item = struct {
|
||||||
|
tile_x: i32,
|
||||||
|
tile_y: i32,
|
||||||
|
normal_x: i32,
|
||||||
|
normal_y: i32,
|
||||||
|
distance: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(origin: Vec2, target: Vec2) RaycastTileIterator {
|
||||||
|
const target_origin = target.sub(origin);
|
||||||
|
const max_distance = target_origin.length();
|
||||||
|
const dir = target_origin.normalized();
|
||||||
|
|
||||||
|
var unit_step_size_x: f32 = 0;
|
||||||
|
if (dir.x != 0) {
|
||||||
|
unit_step_size_x = dir.y / dir.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
var unit_step_size_y: f32= 0;
|
||||||
|
if (dir.y != 0) {
|
||||||
|
unit_step_size_y = dir.x / dir.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unit_step_size = Vec2{
|
||||||
|
.x = Vec2.init(1, unit_step_size_x).length(),
|
||||||
|
.y = Vec2.init(unit_step_size_y, 1).length()
|
||||||
|
};
|
||||||
|
|
||||||
|
const step_x: i32 = if (dir.x < 0) -1 else 1;
|
||||||
|
const step_y: i32 = if (dir.y < 0) -1 else 1;
|
||||||
|
|
||||||
|
const step_x_f32: f32 = @floatFromInt(step_x);
|
||||||
|
const step_y_f32: f32 = @floatFromInt(step_y);
|
||||||
|
|
||||||
|
const ray_length_1d = (Vec2{
|
||||||
|
.x = ((step_x_f32 + 1)/2 - step_x_f32 * @mod(origin.x, 1)),
|
||||||
|
.y = ((step_y_f32 + 1)/2 - step_y_f32 * @mod(origin.y, 1))
|
||||||
|
}).multiply(unit_step_size);
|
||||||
|
|
||||||
|
const check_x: i32 = @intFromFloat(origin.x);
|
||||||
|
const check_y: i32 = @intFromFloat(origin.y);
|
||||||
|
|
||||||
|
return RaycastTileIterator{
|
||||||
|
.max_distance = max_distance,
|
||||||
|
.ray_length_1d = ray_length_1d,
|
||||||
|
.check_x = check_x,
|
||||||
|
.check_y = check_y,
|
||||||
|
.step_x = step_x,
|
||||||
|
.step_y = step_y,
|
||||||
|
.unit_step_size = unit_step_size
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(self: *RaycastTileIterator) ?Item {
|
||||||
|
if (self.ray_length_1d.x > self.max_distance and self.ray_length_1d.y > self.max_distance) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var normal_x: i32 = 0;
|
||||||
|
var normal_y: i32 = 0;
|
||||||
|
var distance: f32 = undefined;
|
||||||
|
if (self.ray_length_1d.x < self.ray_length_1d.y) {
|
||||||
|
distance = self.ray_length_1d.x;
|
||||||
|
|
||||||
|
normal_x = -self.step_x;
|
||||||
|
self.check_x += self.step_x;
|
||||||
|
self.ray_length_1d.x += self.unit_step_size.x;
|
||||||
|
} else {
|
||||||
|
distance = self.ray_length_1d.y;
|
||||||
|
|
||||||
|
normal_y = -self.step_y;
|
||||||
|
self.check_y += self.step_y;
|
||||||
|
self.ray_length_1d.y += self.unit_step_size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.distance = distance,
|
||||||
|
.tile_x = self.check_x,
|
||||||
|
.tile_y = self.check_y,
|
||||||
|
.normal_x = normal_x,
|
||||||
|
.normal_y = normal_y
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user