add player animations
This commit is contained in:
parent
97829b485c
commit
e8b565508f
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
zig-out
|
zig-out
|
||||||
.zig-cache
|
.zig-cache
|
||||||
|
*.tiled-session
|
||||||
|
|||||||
@ -23,7 +23,9 @@ wood01: Audio.Data.Id,
|
|||||||
map: tiled.Tilemap,
|
map: tiled.Tilemap,
|
||||||
tilesets: tiled.Tileset.List,
|
tilesets: tiled.Tileset.List,
|
||||||
tileset_texture: Gfx.TextureId,
|
tileset_texture: Gfx.TextureId,
|
||||||
|
players_texture: Gfx.TextureId,
|
||||||
tile_size: Engine.Vec2,
|
tile_size: Engine.Vec2,
|
||||||
|
player_size: Engine.Vec2,
|
||||||
|
|
||||||
pub fn init(gpa: std.mem.Allocator) !Assets {
|
pub fn init(gpa: std.mem.Allocator) !Assets {
|
||||||
const font_id_array: FontName.EnumArray = .init(.{
|
const font_id_array: FontName.EnumArray = .init(.{
|
||||||
@ -60,6 +62,15 @@ 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, "tileset.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"));
|
const tileset_image = try STBImage.load(@embedFile("assets/kenney_desert-shooter-pack_1.0/PNG/Tiles/tilemap_packed.png"));
|
||||||
defer tileset_image.deinit();
|
defer tileset_image.deinit();
|
||||||
@ -77,7 +88,9 @@ pub fn init(gpa: std.mem.Allocator) !Assets {
|
|||||||
.map = map,
|
.map = map,
|
||||||
.tilesets = tilesets,
|
.tilesets = tilesets,
|
||||||
.tileset_texture = tileset_texture,
|
.tileset_texture = tileset_texture,
|
||||||
.tile_size = .init(16, 16)
|
.tile_size = .init(16, 16),
|
||||||
|
.players_texture = players_texture,
|
||||||
|
.player_size = .init(24, 24)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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",
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
149
src/game.zig
149
src/game.zig
@ -9,27 +9,91 @@ const imgui = Engine.imgui;
|
|||||||
const Vec2 = Engine.Vec2;
|
const Vec2 = Engine.Vec2;
|
||||||
const Rect = Engine.Math.Rect;
|
const Rect = Engine.Math.Rect;
|
||||||
const rgb = Engine.Math.rgb;
|
const rgb = Engine.Math.rgb;
|
||||||
|
const TextureId = Engine.Graphics.TextureId;
|
||||||
|
|
||||||
const Game = @This();
|
const Game = @This();
|
||||||
|
|
||||||
|
const Animation = struct {
|
||||||
|
texture: TextureId,
|
||||||
|
frames: []Frame,
|
||||||
|
|
||||||
|
const Frame = struct {
|
||||||
|
uv: Rect,
|
||||||
|
duration: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const State = struct {
|
||||||
|
frame_index: usize,
|
||||||
|
timer: f32,
|
||||||
|
|
||||||
|
const default = State{
|
||||||
|
.frame_index = 0,
|
||||||
|
.timer = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn update(self: *State, animation: Animation, dt: f32) void {
|
||||||
|
self.timer += dt;
|
||||||
|
while (true) {
|
||||||
|
self.frame_index = @mod(self.frame_index, animation.frames.len);
|
||||||
|
const frame = animation.frames[self.frame_index];
|
||||||
|
if (self.timer < frame.duration) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.timer -= frame.duration;
|
||||||
|
self.frame_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
arena: std.heap.ArenaAllocator,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
assets: *Assets,
|
assets: *Assets,
|
||||||
|
|
||||||
player: Vec2,
|
player: Vec2,
|
||||||
|
player_anim_state: Animation.State = .default,
|
||||||
|
last_faced_left: bool = false,
|
||||||
|
|
||||||
|
player_anim: Animation,
|
||||||
|
|
||||||
pub fn init(gpa: Allocator, assets: *Assets) !Game {
|
pub fn init(gpa: Allocator, assets: *Assets) !Game {
|
||||||
|
var arena = std.heap.ArenaAllocator.init(gpa);
|
||||||
|
errdefer arena.deinit();
|
||||||
|
|
||||||
|
const texture_info = Engine.Graphics.getTextureInfo(assets.players_texture);
|
||||||
|
const tilemap_size = Vec2.initFromInt(u32, texture_info.width, texture_info.height);
|
||||||
|
const tile_size = assets.player_size;
|
||||||
|
const player_anim = Animation{
|
||||||
|
.texture = assets.players_texture,
|
||||||
|
.frames = try arena.allocator().dupe(Animation.Frame, &.{
|
||||||
|
.{
|
||||||
|
.uv = getUVFromTilemap(tilemap_size, tile_size, 0, 0),
|
||||||
|
.duration = 0.1,
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.uv = getUVFromTilemap(tilemap_size, tile_size, 1, 0),
|
||||||
|
.duration = 0.1,
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.uv = getUVFromTilemap(tilemap_size, tile_size, 2, 0),
|
||||||
|
.duration = 0.15,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
return Game{
|
return Game{
|
||||||
|
.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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Game) void {
|
pub fn deinit(self: *Game) void {
|
||||||
_ = self; // autofix
|
self.arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn findSpawnpoint(assets: *Assets) ?Vec2 {
|
fn findSpawnpoint(assets: *Assets) ?Vec2 {
|
||||||
const map = assets.map;
|
const map = assets.map;
|
||||||
for (map.layers) |layer| {
|
for (map.layers) |layer| {
|
||||||
@ -43,22 +107,34 @@ fn findSpawnpoint(assets: *Assets) ?Vec2 {
|
|||||||
}
|
}
|
||||||
const point = object.shape.point;
|
const point = object.shape.point;
|
||||||
if (std.mem.eql(u8, object.name, "spawnpoint")) {
|
if (std.mem.eql(u8, object.name, "spawnpoint")) {
|
||||||
const tile_height: f32 = @floatFromInt(map.tile_height);
|
return .init(point.x, point.y);
|
||||||
const tile_width: f32 = @floatFromInt(map.tile_width);
|
|
||||||
return .init(point.x / tile_width, point.y / tile_height);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn getUVFromTilemap(tilemap_size: Vec2, tile_size: Vec2, tile_x: f32, tile_y: f32) Rect {
|
||||||
|
return .{
|
||||||
|
.pos = Vec2.init(tile_x, tile_y).multiply(tile_size).divide(tilemap_size),
|
||||||
|
.size = tile_size.divide(tilemap_size),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getUVFromTilemapByID(tilemap_size: Vec2, tile_size: Vec2, tile_id: u32) Rect {
|
||||||
|
const tile_id_f32: f32 = @floatFromInt(tile_id);
|
||||||
|
const width_in_tiles = tilemap_size.x / tile_size.x;
|
||||||
|
const tile_x = @rem(tile_id_f32, width_in_tiles);
|
||||||
|
const tile_y = @divFloor(tile_id_f32, width_in_tiles);
|
||||||
|
return getUVFromTilemap(tilemap_size, tile_size, tile_x, tile_y);
|
||||||
|
}
|
||||||
|
|
||||||
fn drawTilemap(self: *Game, frame: *Engine.Frame) void {
|
fn drawTilemap(self: *Game, frame: *Engine.Frame) void {
|
||||||
const texture = self.assets.tileset_texture;
|
const texture = self.assets.tileset_texture;
|
||||||
const texture_info = Engine.Graphics.getTextureInfo(texture);
|
const texture_info = Engine.Graphics.getTextureInfo(texture);
|
||||||
|
|
||||||
const map = self.assets.map;
|
const map = self.assets.map;
|
||||||
|
|
||||||
const tile_size = Vec2.init(1, 1);
|
|
||||||
for (map.layers) |layer| {
|
for (map.layers) |layer| {
|
||||||
if (layer.variant != .tile) {
|
if (layer.variant != .tile) {
|
||||||
continue;
|
continue;
|
||||||
@ -74,43 +150,29 @@ fn drawTilemap(self: *Game, frame: *Engine.Frame) void {
|
|||||||
const tile_gid = tile_layer.get(x, y) orelse continue;
|
const tile_gid = tile_layer.get(x, y) orelse continue;
|
||||||
const tile = map.getTile(self.assets.tilesets, tile_gid) orelse continue;
|
const tile = map.getTile(self.assets.tilesets, tile_gid) orelse continue;
|
||||||
|
|
||||||
const tilemap_width_in_tiles = @divExact(texture_info.width, tile.tileset.tile_width);
|
const tilemap_size = Vec2.initFromInt(u32,texture_info.width, texture_info.height);
|
||||||
var uv: Rect = .{
|
const tile_size = Vec2.initFromInt(u32, tile.tileset.tile_width, tile.tileset.tile_height);
|
||||||
.pos = Vec2.initFromInt(
|
|
||||||
u32,
|
|
||||||
@rem(tile.id, tilemap_width_in_tiles),
|
|
||||||
@divFloor(tile.id, tilemap_width_in_tiles),
|
|
||||||
).multiply(
|
|
||||||
.initFromInt(u32, tile.tileset.tile_width, tile.tileset.tile_height)
|
|
||||||
),
|
|
||||||
.size = self.assets.tile_size,
|
|
||||||
};
|
|
||||||
uv = uv.divide(.init(@floatFromInt(texture_info.width), @floatFromInt(texture_info.height)));
|
|
||||||
|
|
||||||
var rect = Rect{
|
|
||||||
.pos = .init(@floatFromInt(x), @floatFromInt(y)),
|
|
||||||
.size = .init(1, 1)
|
|
||||||
};
|
|
||||||
rect = rect.multiply(tile_size);
|
|
||||||
|
|
||||||
frame.drawRectangle(.{
|
frame.drawRectangle(.{
|
||||||
.rect = rect,
|
.rect = Rect{
|
||||||
|
.pos = Vec2.initFromInt(i32, x, y).multiply(tile_size),
|
||||||
|
.size = tile_size
|
||||||
|
},
|
||||||
.color = rgb(255, 255, 255),
|
.color = rgb(255, 255, 255),
|
||||||
.texture = .{
|
.texture = .{
|
||||||
.id = self.assets.tileset_texture,
|
.id = self.assets.tileset_texture,
|
||||||
.uv = uv
|
.uv = getUVFromTilemapByID(tilemap_size, tile_size,tile.id)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// map.getTile(layer, tilesets)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
const canvas_size = Vec2.init(20, 15);
|
const canvas_size = Vec2.init(20 * 16, 15 * 16);
|
||||||
frame.graphics.canvas_size = canvas_size;
|
frame.graphics.canvas_size = canvas_size;
|
||||||
|
|
||||||
if (frame.isKeyPressed(.F3)) {
|
if (frame.isKeyPressed(.F3)) {
|
||||||
@ -138,13 +200,19 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
|||||||
}
|
}
|
||||||
dir = dir.normalized();
|
dir = dir.normalized();
|
||||||
|
|
||||||
|
if (dir.x != 0 or dir.y != 0) {
|
||||||
|
self.player_anim_state.update(self.player_anim, dt);
|
||||||
|
} else {
|
||||||
|
self.player_anim_state.frame_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// if (dir.x != 0 or dir.y != 0) {
|
// if (dir.x != 0 or dir.y != 0) {
|
||||||
// try frame.playAudio(.{
|
// try frame.playAudio(.{
|
||||||
// .id = self.assets.wood01
|
// .id = self.assets.wood01
|
||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
|
|
||||||
self.player = self.player.add(dir.multiplyScalar(5 * dt));
|
self.player = self.player.add(dir.multiplyScalar(50 * dt));
|
||||||
|
|
||||||
const regular_font = self.assets.font_id.get(.regular);
|
const regular_font = self.assets.font_id.get(.regular);
|
||||||
|
|
||||||
@ -154,17 +222,28 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
|||||||
.rect = .init(0, 0, canvas_size.x, canvas_size.y),
|
.rect = .init(0, 0, canvas_size.x, canvas_size.y),
|
||||||
.color = rgb(20, 20, 20)
|
.color = rgb(20, 20, 20)
|
||||||
});
|
});
|
||||||
const size = Vec2.init(1, 1);
|
|
||||||
|
if (dir.x < 0) {
|
||||||
|
self.last_faced_left = true;
|
||||||
|
} else if (dir.x > 0) {
|
||||||
|
self.last_faced_left = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var size = self.assets.player_size;
|
||||||
|
if (self.last_faced_left) {
|
||||||
|
size.x *= -1;
|
||||||
|
}
|
||||||
frame.drawRectangle(.{
|
frame.drawRectangle(.{
|
||||||
.rect = .{
|
.rect = .{
|
||||||
.pos = self.player.sub(size.divideScalar(2)),
|
.pos = self.player.sub(size.divideScalar(2)),
|
||||||
.size = size,
|
.size = size,
|
||||||
},
|
},
|
||||||
.color = rgb(200, 20, 20)
|
.color = rgb(255, 255, 255),
|
||||||
});
|
.texture = .{
|
||||||
if (dir.x != 0 or dir.y != 0) {
|
.id = self.player_anim.texture,
|
||||||
frame.drawRectanglOutline(self.player.sub(size.divideScalar(2)), size, rgb(20, 200, 20), 0.1);
|
.uv = self.player_anim.frames[self.player_anim_state.frame_index].uv
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
frame.drawText(self.player, "Player", .{
|
frame.drawText(self.player, "Player", .{
|
||||||
.font = regular_font,
|
.font = regular_font,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user