HelloWorld
This commit is contained in:
commit
e7c630411b
34 changed files with 873 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.zig-cache/
|
||||||
|
zig-pkg/
|
||||||
|
zig-out/
|
||||||
66
build.zig
Normal file
66
build.zig
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
// --- TARGET ---
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
// --- EXE ---
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "vampire_survivors",
|
||||||
|
.root_module = b.createModule(.{
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
|
||||||
|
.imports = &.{},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const exe_tests = b.addTest(.{
|
||||||
|
.root_module = exe.root_module,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_exe_tests = b.addRunArtifact(exe_tests);
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run tests");
|
||||||
|
test_step.dependOn(&run_exe_tests.step);
|
||||||
|
|
||||||
|
//
|
||||||
|
// DEPENDENCIES
|
||||||
|
//
|
||||||
|
|
||||||
|
const app = b.dependency("app", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
exe.root_module.addImport("app", app.module("app"));
|
||||||
|
|
||||||
|
//
|
||||||
|
// SYSTEM DEPENDENCIES
|
||||||
|
//
|
||||||
|
|
||||||
|
const sdl = b.addTranslateC(.{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.root_source_file = b.path("./src/vendors/sdl3/sdl.h"),
|
||||||
|
});
|
||||||
|
exe.root_module.addImport("sdl", sdl.createModule());
|
||||||
|
exe.root_module.linkSystemLibrary("SDL3", .{});
|
||||||
|
exe.root_module.linkSystemLibrary("SDL3_ttf", .{});
|
||||||
|
exe.root_module.linkSystemLibrary("SDL3_image", .{});
|
||||||
|
}
|
||||||
22
build.zig.zon
Normal file
22
build.zig.zon
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
.{
|
||||||
|
.name = .vampire_survivors,
|
||||||
|
|
||||||
|
.version = "0.0.0",
|
||||||
|
|
||||||
|
.fingerprint = 0x27782ab1dd9d7c0f,
|
||||||
|
|
||||||
|
.minimum_zig_version = "0.16.0",
|
||||||
|
|
||||||
|
.dependencies = .{
|
||||||
|
.app = .{
|
||||||
|
.url = "git+http://2.120.146.66:25569/abux/NYXA#93a4606c94f67b1f0a559c2efff3d8ff030b3300",
|
||||||
|
.hash = "app-0.0.0-AOV9_ea_AACRsUJ2lZqYiUkPed0op9qgFSWB6ihDSrll",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
}
|
||||||
BIN
src/assets/textures/player.png
Normal file
BIN
src/assets/textures/player.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
0
src/component/camera.zig
Normal file
0
src/component/camera.zig
Normal file
0
src/component/camera_target.zig
Normal file
0
src/component/camera_target.zig
Normal file
2
src/component/caster.zig
Normal file
2
src/component/caster.zig
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
cooldown: f32 = 0.2,
|
||||||
|
current_cooldown: f32 = 0.0,
|
||||||
1
src/component/friction.zig
Normal file
1
src/component/friction.zig
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
value: f32 = 5.0,
|
||||||
9
src/component/material.zig
Normal file
9
src/component/material.zig
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
const Handle = @import("app").template.Handle;
|
||||||
|
const Texture = @import("../resource/textures.zig").Texture;
|
||||||
|
|
||||||
|
pub const Style = union(enum) {
|
||||||
|
solid: @Vector(4, u8),
|
||||||
|
textured: Handle(Texture),
|
||||||
|
};
|
||||||
|
|
||||||
|
style: Style,
|
||||||
0
src/component/player.zig
Normal file
0
src/component/player.zig
Normal file
0
src/component/projectile.zig
Normal file
0
src/component/projectile.zig
Normal file
19
src/component/root.zig
Normal file
19
src/component/root.zig
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
// --- POSITION ---
|
||||||
|
pub const Transform = @import("transform.zig");
|
||||||
|
pub const Velocity = @import("velocity.zig");
|
||||||
|
pub const Friction = @import("friction.zig");
|
||||||
|
|
||||||
|
// --- STYLE ---
|
||||||
|
pub const Material = @import("material.zig");
|
||||||
|
|
||||||
|
// --- CAMERA ---
|
||||||
|
pub const Camera = @import("camera.zig");
|
||||||
|
pub const CameraTarget = @import("camera_target.zig");
|
||||||
|
|
||||||
|
// --- TIME ---
|
||||||
|
pub const Timer = @import("timer.zig");
|
||||||
|
|
||||||
|
// --- MARKERS ---
|
||||||
|
pub const Player = @import("player.zig");
|
||||||
|
pub const Caster = @import("caster.zig");
|
||||||
|
pub const Projectile = @import("projectile.zig");
|
||||||
0
src/component/shape.zig
Normal file
0
src/component/shape.zig
Normal file
24
src/component/timer.zig
Normal file
24
src/component/timer.zig
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
duration: f32 = 0.0,
|
||||||
|
remaining: f32 = 0.0,
|
||||||
|
|
||||||
|
repeat: bool = false,
|
||||||
|
finished: bool = false,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn set(
|
||||||
|
seconds: f32,
|
||||||
|
repeat: bool,
|
||||||
|
) Self {
|
||||||
|
return .{
|
||||||
|
.duration = seconds,
|
||||||
|
.remaining = seconds,
|
||||||
|
.repeat = repeat,
|
||||||
|
};
|
||||||
|
}
|
||||||
2
src/component/transform.zig
Normal file
2
src/component/transform.zig
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pos: @Vector(2, f32) = .{ 0, 0 },
|
||||||
|
size: @Vector(2, f32) = .{ 32, 32 },
|
||||||
1
src/component/velocity.zig
Normal file
1
src/component/velocity.zig
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
value: @Vector(2, f32) = .{ 0, 0 },
|
||||||
4
src/data/camera.zig
Normal file
4
src/data/camera.zig
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
const component = @import("../component/root.zig");
|
||||||
|
|
||||||
|
value: *const component.Camera,
|
||||||
|
transform: *const component.Transform,
|
||||||
1
src/data/root.zig
Normal file
1
src/data/root.zig
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub const Camera = @import("camera.zig");
|
||||||
0
src/event/root.zig
Normal file
0
src/event/root.zig
Normal file
45
src/main.zig
Normal file
45
src/main.zig
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const App = @import("app");
|
||||||
|
|
||||||
|
const plugin = @import("plugin/root.zig");
|
||||||
|
const resource = @import("resource/root.zig");
|
||||||
|
const template = @import("template/root.zig");
|
||||||
|
|
||||||
|
pub fn main(init: std.process.Init) !void {
|
||||||
|
// --- APP ---
|
||||||
|
var app: App = .init(init.gpa);
|
||||||
|
defer app.deinit();
|
||||||
|
|
||||||
|
// --- ADD DEFAULT PLUGIN & BUILD ---
|
||||||
|
try app.plugins.add(plugin.default);
|
||||||
|
try app.plugins.build(.{ .app = &app });
|
||||||
|
|
||||||
|
// --- LIFETIME ---
|
||||||
|
try app.schedules.run(App.stage.init, .{ .app = &app });
|
||||||
|
defer app.schedules.run(App.stage.deinit, .{ .app = &app }) catch unreachable;
|
||||||
|
|
||||||
|
// errdefer {
|
||||||
|
// app.schedules.run(App.stage.deinit, .{ .app = &app }) catch unreachable;
|
||||||
|
// }
|
||||||
|
|
||||||
|
try app.schedules.run(App.stage.post_init, .{ .app = &app });
|
||||||
|
|
||||||
|
// --- MAIN GAME LOOP ---
|
||||||
|
while (true) {
|
||||||
|
// --- BREAK ON EXIT ---
|
||||||
|
if (app.events.consume(App.event.AppExit)) |_| {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- FRAME ---
|
||||||
|
try app.schedules.runMany(&.{
|
||||||
|
App.stage.begin_frame,
|
||||||
|
App.stage.inputs,
|
||||||
|
App.stage.update,
|
||||||
|
App.stage.draw,
|
||||||
|
App.stage.end_frame,
|
||||||
|
}, .{
|
||||||
|
.app = &app,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/plugin/default.zig
Normal file
37
src/plugin/default.zig
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
const App = @import("app");
|
||||||
|
|
||||||
|
const component = @import("../component/root.zig");
|
||||||
|
const resource = @import("../resource/root.zig");
|
||||||
|
const system = @import("../system/root.zig");
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const plugin: App.plugin.Plugin = .{
|
||||||
|
.name = "Default",
|
||||||
|
.build = build,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
fn build(
|
||||||
|
_: *App.plugin.Plugin,
|
||||||
|
ctx: App.plugin.Context,
|
||||||
|
) !void {
|
||||||
|
// --- LIFETIME ---
|
||||||
|
try ctx.app.schedules.add(App.stage.init, system.core.init);
|
||||||
|
try ctx.app.schedules.add(App.stage.post_init, system.core.build);
|
||||||
|
try ctx.app.schedules.add(App.stage.deinit, system.core.deinit);
|
||||||
|
|
||||||
|
// --- FRAME ---
|
||||||
|
try ctx.app.schedules.add(App.stage.begin_frame, system.frame.beginFrame);
|
||||||
|
try ctx.app.schedules.add(App.stage.end_frame, system.frame.endFrame);
|
||||||
|
|
||||||
|
// --- UPDATE ---
|
||||||
|
try ctx.app.schedules.add(App.stage.inputs, system.frame.inputs);
|
||||||
|
try ctx.app.schedules.addMany(App.stage.update, &.{
|
||||||
|
system.frame.updateTimers,
|
||||||
|
system.frame.updateTransforms,
|
||||||
|
system.frame.cast,
|
||||||
|
});
|
||||||
|
try ctx.app.schedules.add(App.stage.draw, system.frame.draw);
|
||||||
|
}
|
||||||
1
src/plugin/root.zig
Normal file
1
src/plugin/root.zig
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub const default = @import("default.zig").plugin;
|
||||||
7
src/resource/frame.zig
Normal file
7
src/resource/frame.zig
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
const data = @import("../data/root.zig");
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
camera: ?data.Camera = null,
|
||||||
3
src/resource/renderer.zig
Normal file
3
src/resource/renderer.zig
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
const sdl = @import("sdl");
|
||||||
|
|
||||||
|
raw: *sdl.SDL_Renderer,
|
||||||
8
src/resource/root.zig
Normal file
8
src/resource/root.zig
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// --- CORE ---
|
||||||
|
pub const Window = @import("window.zig");
|
||||||
|
pub const Renderer = @import("renderer.zig");
|
||||||
|
pub const Frame = @import("frame.zig");
|
||||||
|
pub const Time = @import("time.zig");
|
||||||
|
|
||||||
|
// --- ASSETS ---
|
||||||
|
pub const Textures = @import("textures.zig");
|
||||||
132
src/resource/textures.zig
Normal file
132
src/resource/textures.zig
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const sdl = @import("sdl");
|
||||||
|
const Renderer = @import("../resource/renderer.zig");
|
||||||
|
const Handle = @import("app").template.Handle;
|
||||||
|
const Storage = @import("../template/storage.zig").Storage;
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const Size = union(enum) {
|
||||||
|
source,
|
||||||
|
fixed: [2]u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const Filter = enum {
|
||||||
|
linear,
|
||||||
|
nearest,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const Texture = struct {
|
||||||
|
path: []const u8,
|
||||||
|
size: [2]u32 = .{ 0, 0 },
|
||||||
|
filter: Filter = .nearest,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const GPUTexture = struct {
|
||||||
|
raw: *sdl.SDL_Texture,
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
textures: Storage(Texture, GPUTexture),
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.textures = .init(alloc),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.textures.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn load(
|
||||||
|
self: *Self,
|
||||||
|
name: []const u8,
|
||||||
|
path: []const u8,
|
||||||
|
) !Handle(Texture) {
|
||||||
|
return try self.textures.put(name, .{
|
||||||
|
.path = path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn get(
|
||||||
|
self: *Self,
|
||||||
|
value: Handle(Texture),
|
||||||
|
) ?*GPUTexture {
|
||||||
|
const record = self.textures.get(value) orelse return null;
|
||||||
|
return if (record.gpu) |*v| v else null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn build(
|
||||||
|
self: *Self,
|
||||||
|
renderer: *const Renderer,
|
||||||
|
) !void {
|
||||||
|
for (self.textures.items()) |*record| {
|
||||||
|
// --- SKIP IF BUILD ---
|
||||||
|
if (record.gpu != null) continue;
|
||||||
|
|
||||||
|
// --- LOAD SURFACE ---
|
||||||
|
const surface = sdl.IMG_Load(
|
||||||
|
record.cpu.path.ptr,
|
||||||
|
) orelse {
|
||||||
|
std.debug.print("\x1b[31mFailed to load texture:\x1b[0m {s}\n", .{sdl.SDL_GetError()});
|
||||||
|
return error.Texture;
|
||||||
|
};
|
||||||
|
defer sdl.SDL_DestroySurface(surface);
|
||||||
|
|
||||||
|
// --- CREATE TEXTURE ---
|
||||||
|
const texture = sdl.SDL_CreateTextureFromSurface(
|
||||||
|
renderer.raw,
|
||||||
|
surface,
|
||||||
|
) orelse return error.Texture;
|
||||||
|
|
||||||
|
// --- SET FILTER ---
|
||||||
|
_ = sdl.SDL_SetTextureScaleMode(texture, switch (record.cpu.filter) {
|
||||||
|
.linear => sdl.SDL_SCALEMODE_LINEAR,
|
||||||
|
.nearest => sdl.SDL_SCALEMODE_PIXELART,
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- ASSIGN CPU ---
|
||||||
|
record.cpu.size = .{
|
||||||
|
@intCast(surface.*.w),
|
||||||
|
@intCast(surface.*.h),
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- ASSIGN GPU ---
|
||||||
|
record.gpu = .{
|
||||||
|
.raw = texture,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DEBUG ---
|
||||||
|
var it = self.textures.lookup.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
const record = self.textures.get(entry.key_ptr.*) orelse continue;
|
||||||
|
|
||||||
|
std.debug.print("\x1b[35m┏━ TEXTURE\n", .{});
|
||||||
|
std.debug.print("\x1b[35m┃\x1b[0m Name: \"{s}\"\n", .{entry.value_ptr.*});
|
||||||
|
std.debug.print("\x1b[35m┃\x1b[0m Path: \"{s}\"\n", .{record.cpu.path});
|
||||||
|
std.debug.print("\x1b[35m┃\x1b[0m Size: {}x{}\n", .{ record.cpu.size[0], record.cpu.size[1] });
|
||||||
|
std.debug.print("\x1b[35m┗━\x1b[0m\n", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/resource/time.zig
Normal file
5
src/resource/time.zig
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
dt: f32 = 0.0,
|
||||||
|
now: u64 = 0,
|
||||||
|
last: u64 = 0,
|
||||||
|
frequency: u64 = 0,
|
||||||
|
target_dt: f32 = 1.0 / 60.0,
|
||||||
6
src/resource/window.zig
Normal file
6
src/resource/window.zig
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
const sdl = @import("sdl");
|
||||||
|
|
||||||
|
title: []const u8 = "Vampire Survivors",
|
||||||
|
width: u32 = 800,
|
||||||
|
height: u32 = 600,
|
||||||
|
raw: *sdl.SDL_Window,
|
||||||
107
src/system/core.zig
Normal file
107
src/system/core.zig
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const sdl = @import("sdl");
|
||||||
|
const App = @import("app");
|
||||||
|
|
||||||
|
const component = @import("../component/root.zig");
|
||||||
|
const resource = @import("../resource/root.zig");
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(ctx: App.scheduler.Context) !void {
|
||||||
|
// --- SDL3 INIT ---
|
||||||
|
if (!sdl.SDL_Init(sdl.SDL_INIT_VIDEO)) {
|
||||||
|
return error.SDL_Init;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- WINDOW ---
|
||||||
|
const window = try ctx.app.resources.getOrSet(resource.Window{
|
||||||
|
.raw = sdl.SDL_CreateWindow(
|
||||||
|
"Vampire Survivors",
|
||||||
|
800,
|
||||||
|
600,
|
||||||
|
sdl.SDL_WINDOW_RESIZABLE | sdl.SDL_WINDOW_HIGH_PIXEL_DENSITY,
|
||||||
|
) orelse return error.Window,
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- RENDERER ---
|
||||||
|
try ctx.app.resources.set(resource.Renderer{
|
||||||
|
.raw = sdl.SDL_CreateRenderer(
|
||||||
|
window.raw,
|
||||||
|
null,
|
||||||
|
) orelse return error.Renderer,
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- FRAME ---
|
||||||
|
try ctx.app.resources.set(resource.Time{
|
||||||
|
.now = sdl.SDL_GetPerformanceCounter(),
|
||||||
|
.last = sdl.SDL_GetPerformanceCounter(),
|
||||||
|
.frequency = sdl.SDL_GetPerformanceFrequency(),
|
||||||
|
});
|
||||||
|
try ctx.app.resources.set(resource.Frame{});
|
||||||
|
|
||||||
|
// --- ASSETS ---
|
||||||
|
const textures = try ctx.app.resources.getOrSet(resource.Textures.init(ctx.app.alloc));
|
||||||
|
|
||||||
|
// --- DEBUG ---
|
||||||
|
std.debug.print("\x1b[35m┏━ WINDOW\n", .{});
|
||||||
|
std.debug.print("\x1b[35m┃\x1b[0m Title: \"{s}\"\n", .{window.title});
|
||||||
|
std.debug.print("\x1b[35m┃\x1b[0m Size: {}x{}\n", .{ window.width, window.height });
|
||||||
|
std.debug.print("\x1b[35m┗━\x1b[0m\n", .{});
|
||||||
|
|
||||||
|
{ // ENTITIES
|
||||||
|
// --- CAMERA ---
|
||||||
|
try ctx.app.world.spawn(.{
|
||||||
|
component.Camera{},
|
||||||
|
component.Transform{},
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- PLAYER ---
|
||||||
|
try ctx.app.world.spawn(.{
|
||||||
|
component.Player{},
|
||||||
|
component.Caster{},
|
||||||
|
|
||||||
|
component.Friction{},
|
||||||
|
component.Velocity{},
|
||||||
|
component.Transform{ .size = .{ 200, 200 } },
|
||||||
|
component.Material{ .style = .{
|
||||||
|
.textured = try textures.load("Player", "src/assets/textures/player.png"),
|
||||||
|
} },
|
||||||
|
component.CameraTarget{},
|
||||||
|
});
|
||||||
|
|
||||||
|
try ctx.app.world.spawn(.{
|
||||||
|
component.Transform{ .pos = .{ 100, 100 } },
|
||||||
|
component.Material{ .style = .{
|
||||||
|
.solid = .{ 20, 200, 20, 255 },
|
||||||
|
} },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(ctx: App.scheduler.Context) !void {
|
||||||
|
const res = try ctx.app.resources.getMany(struct {
|
||||||
|
window: *resource.Window,
|
||||||
|
renderer: *resource.Renderer,
|
||||||
|
textures: *resource.Textures,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.textures.deinit();
|
||||||
|
sdl.SDL_DestroyRenderer(res.renderer.raw);
|
||||||
|
sdl.SDL_DestroyWindow(res.window.raw);
|
||||||
|
sdl.SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn build(ctx: App.scheduler.Context) !void {
|
||||||
|
const res = try ctx.app.resources.getMany(struct {
|
||||||
|
renderer: *resource.Renderer,
|
||||||
|
textures: *resource.Textures,
|
||||||
|
});
|
||||||
|
|
||||||
|
try res.textures.build(
|
||||||
|
res.renderer,
|
||||||
|
);
|
||||||
|
}
|
||||||
277
src/system/frame.zig
Normal file
277
src/system/frame.zig
Normal file
|
|
@ -0,0 +1,277 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const sdl = @import("sdl");
|
||||||
|
const App = @import("app");
|
||||||
|
|
||||||
|
const component = @import("../component/root.zig");
|
||||||
|
const resource = @import("../resource/root.zig");
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn inputs(ctx: App.scheduler.Context) !void {
|
||||||
|
const res = try ctx.app.resources.getMany(struct {
|
||||||
|
window: *resource.Window,
|
||||||
|
renderer: *resource.Renderer,
|
||||||
|
});
|
||||||
|
_ = res;
|
||||||
|
|
||||||
|
// --- POLL EVENTS ---
|
||||||
|
var raw_event: sdl.SDL_Event = undefined;
|
||||||
|
while (sdl.SDL_PollEvent(&raw_event)) {
|
||||||
|
switch (raw_event.type) {
|
||||||
|
sdl.SDL_EVENT_QUIT => try ctx.app.events.send(App.event.AppExit{}),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyboard: [*c]const bool = sdl.SDL_GetKeyboardState(null);
|
||||||
|
|
||||||
|
var query = ctx.app.world.query(struct {
|
||||||
|
player: *component.Player,
|
||||||
|
velocity: *component.Velocity,
|
||||||
|
}, .{});
|
||||||
|
while (query.next()) |e| {
|
||||||
|
const speed: f32 = 100.0;
|
||||||
|
if (keyboard[sdl.SDL_SCANCODE_W]) e.velocity.value[1] = -speed;
|
||||||
|
if (keyboard[sdl.SDL_SCANCODE_S]) e.velocity.value[1] = speed;
|
||||||
|
if (keyboard[sdl.SDL_SCANCODE_A]) e.velocity.value[0] = -speed;
|
||||||
|
if (keyboard[sdl.SDL_SCANCODE_D]) e.velocity.value[0] = speed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn beginFrame(ctx: App.scheduler.Context) !void {
|
||||||
|
const res = try ctx.app.resources.getMany(struct {
|
||||||
|
window: *resource.Window,
|
||||||
|
frame: *resource.Frame,
|
||||||
|
time: *resource.Time,
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// TIME
|
||||||
|
//
|
||||||
|
|
||||||
|
res.time.last = res.time.now;
|
||||||
|
res.time.now = sdl.SDL_GetPerformanceCounter();
|
||||||
|
|
||||||
|
res.time.dt =
|
||||||
|
@as(f32, @floatFromInt(res.time.now - res.time.last)) /
|
||||||
|
@as(f32, @floatFromInt(res.time.frequency));
|
||||||
|
|
||||||
|
//
|
||||||
|
// CAMERA
|
||||||
|
//
|
||||||
|
|
||||||
|
var target_transform: ?*const component.Transform = null;
|
||||||
|
|
||||||
|
{ // GATHER CAMERAS TARGET
|
||||||
|
var query = ctx.app.world.query(struct {
|
||||||
|
transform: *component.Transform,
|
||||||
|
camera_target: ?*component.CameraTarget,
|
||||||
|
}, .{});
|
||||||
|
while (query.next()) |e| {
|
||||||
|
if (e.camera_target) |_| {
|
||||||
|
target_transform = e.transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // GATHER CAMERAS
|
||||||
|
var query = ctx.app.world.query(struct {
|
||||||
|
camera: *component.Camera,
|
||||||
|
transform: *component.Transform,
|
||||||
|
}, .{});
|
||||||
|
while (query.next()) |e| {
|
||||||
|
const window_width: f32 = @floatFromInt(res.window.width);
|
||||||
|
const window_height: f32 = @floatFromInt(res.window.height);
|
||||||
|
const target = if (target_transform) |v| v else continue;
|
||||||
|
|
||||||
|
e.transform.pos[0] += ((window_width / 2.0) - (target.pos[0] + target.size[0] / 2.0) - e.transform.pos[0]) / 20.0;
|
||||||
|
e.transform.pos[1] += ((window_height / 2.0) - (target.pos[1] + target.size[1] / 2.0) - e.transform.pos[1]) / 20.0;
|
||||||
|
|
||||||
|
// --- SET CURRENT FRAME CAMERA ---
|
||||||
|
res.frame.camera = .{
|
||||||
|
.value = e.camera,
|
||||||
|
.transform = e.transform,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn endFrame(ctx: App.scheduler.Context) !void {
|
||||||
|
const res = try ctx.app.resources.getMany(struct {
|
||||||
|
frame: *resource.Frame,
|
||||||
|
time: *resource.Time,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.frame.camera = null;
|
||||||
|
|
||||||
|
const frame_time =
|
||||||
|
@as(f32, @floatFromInt(sdl.SDL_GetPerformanceCounter() - res.time.now)) /
|
||||||
|
@as(f32, @floatFromInt(res.time.frequency));
|
||||||
|
|
||||||
|
if (frame_time < res.time.target_dt) {
|
||||||
|
const delay_ms = (res.time.target_dt - frame_time) * 1000.0;
|
||||||
|
sdl.SDL_Delay(@intFromFloat(delay_ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn updateTransforms(ctx: App.scheduler.Context) !void {
|
||||||
|
const res = try ctx.app.resources.getMany(struct {
|
||||||
|
time: *resource.Time,
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- QUERY ---
|
||||||
|
var query = ctx.app.world.query(struct {
|
||||||
|
transform: *component.Transform,
|
||||||
|
velocity: *component.Velocity,
|
||||||
|
friction: ?*component.Friction,
|
||||||
|
}, .{});
|
||||||
|
while (query.next()) |e| {
|
||||||
|
// --- APPLY VELOCITY ---
|
||||||
|
e.transform.pos[0] += e.velocity.value[0] * res.time.dt;
|
||||||
|
e.transform.pos[1] += e.velocity.value[1] * res.time.dt;
|
||||||
|
|
||||||
|
// --- APPLY FRICTION ---
|
||||||
|
if (e.friction) |friction| {
|
||||||
|
const damping = std.math.exp(-friction.value * res.time.dt);
|
||||||
|
e.velocity.value[0] *= damping;
|
||||||
|
e.velocity.value[1] *= damping;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn updateTimers(ctx: App.scheduler.Context) !void {
|
||||||
|
const res = try ctx.app.resources.getMany(struct {
|
||||||
|
time: *resource.Time,
|
||||||
|
});
|
||||||
|
|
||||||
|
var query = ctx.app.world.query(struct {
|
||||||
|
timer: *component.Timer,
|
||||||
|
}, .{});
|
||||||
|
while (query.next()) |e| {
|
||||||
|
// --- # ---
|
||||||
|
if (e.timer.finished and !e.timer.repeat)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// --- PROGRESS ---
|
||||||
|
e.timer.remaining -=
|
||||||
|
res.time.dt;
|
||||||
|
|
||||||
|
if (e.timer.remaining <= 0.0) {
|
||||||
|
e.timer.finished = true;
|
||||||
|
|
||||||
|
if (e.timer.repeat) {
|
||||||
|
e.timer.remaining = e.timer.duration;
|
||||||
|
e.timer.finished = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn cast(ctx: App.scheduler.Context) !void {
|
||||||
|
const res = try ctx.app.resources.getMany(struct {
|
||||||
|
time: *resource.Time,
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- QUERY ---
|
||||||
|
var query = ctx.app.world.query(struct {
|
||||||
|
caster: *component.Caster,
|
||||||
|
transform: *component.Transform,
|
||||||
|
}, .{});
|
||||||
|
while (query.next()) |e| {
|
||||||
|
// --- SUBTRACT COOLDOWN ---
|
||||||
|
e.caster.current_cooldown -=
|
||||||
|
@max(0, res.time.dt);
|
||||||
|
|
||||||
|
// --- SKIP IF ON COOLDOWN ---
|
||||||
|
if (e.caster.current_cooldown >= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// --- SPAWN PROJECTILE ---
|
||||||
|
try ctx.app.world.spawn(.{
|
||||||
|
component.Transform{
|
||||||
|
.pos = e.transform.pos,
|
||||||
|
},
|
||||||
|
component.Velocity{
|
||||||
|
.value = .{ 400, 0 },
|
||||||
|
},
|
||||||
|
component.Material{
|
||||||
|
.style = .{ .solid = .{ 200, 20, 20, 255 } },
|
||||||
|
},
|
||||||
|
component.Projectile{},
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- SET COOLDOWN ---
|
||||||
|
e.caster.current_cooldown =
|
||||||
|
e.caster.cooldown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn draw(ctx: App.scheduler.Context) !void {
|
||||||
|
const res = try ctx.app.resources.getMany(struct {
|
||||||
|
window: *resource.Window,
|
||||||
|
renderer: *resource.Renderer,
|
||||||
|
textures: *resource.Textures,
|
||||||
|
frame: *resource.Frame,
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- SET CLEAR COLOR ---
|
||||||
|
_ = sdl.SDL_SetRenderDrawColor(
|
||||||
|
res.renderer.raw,
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
255,
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- DRAW ---
|
||||||
|
_ = sdl.SDL_RenderClear(res.renderer.raw);
|
||||||
|
defer _ = sdl.SDL_RenderPresent(res.renderer.raw);
|
||||||
|
|
||||||
|
{ // DRAW
|
||||||
|
var query = ctx.app.world.query(struct {
|
||||||
|
transform: *component.Transform,
|
||||||
|
material: *component.Material,
|
||||||
|
camera_target: ?*component.CameraTarget,
|
||||||
|
}, .{});
|
||||||
|
while (query.next()) |e| {
|
||||||
|
const camera = if (res.frame.camera) |*v| v else continue;
|
||||||
|
|
||||||
|
// --- SHAPE ---
|
||||||
|
var rect: sdl.SDL_FRect = .{
|
||||||
|
.x = e.transform.pos[0] + camera.transform.pos[0],
|
||||||
|
.y = e.transform.pos[1] + camera.transform.pos[1],
|
||||||
|
.w = e.transform.size[0],
|
||||||
|
.h = e.transform.size[1],
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- MATERIAL ---
|
||||||
|
switch (e.material.style) {
|
||||||
|
.solid => |albedo| {
|
||||||
|
_ = sdl.SDL_SetRenderDrawColor(res.renderer.raw, albedo[0], albedo[1], albedo[2], albedo[3]);
|
||||||
|
_ = sdl.SDL_RenderFillRect(res.renderer.raw, &rect);
|
||||||
|
},
|
||||||
|
.textured => |handle| {
|
||||||
|
const gpu = res.textures.get(handle) orelse continue;
|
||||||
|
_ = sdl.SDL_RenderTexture(
|
||||||
|
res.renderer.raw,
|
||||||
|
gpu.raw,
|
||||||
|
null,
|
||||||
|
&rect,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/system/root.zig
Normal file
2
src/system/root.zig
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub const core = @import("core.zig");
|
||||||
|
pub const frame = @import("frame.zig");
|
||||||
1
src/template/root.zig
Normal file
1
src/template/root.zig
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub const Storage = @import("storage.zig").Storage;
|
||||||
85
src/template/storage.zig
Normal file
85
src/template/storage.zig
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const App = @import("app");
|
||||||
|
const Handle = App.template.Handle;
|
||||||
|
|
||||||
|
pub fn Storage(
|
||||||
|
comptime CPU: type,
|
||||||
|
comptime GPU: type,
|
||||||
|
) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const Record = struct {
|
||||||
|
cpu: CPU,
|
||||||
|
gpu: ?GPU = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
data: std.ArrayList(Record),
|
||||||
|
lookup: std.AutoHashMap(Handle(CPU), []const u8),
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.data = .empty,
|
||||||
|
.lookup = .init(alloc),
|
||||||
|
.alloc = alloc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.data.deinit(self.alloc);
|
||||||
|
self.lookup.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn put(
|
||||||
|
self: *Self,
|
||||||
|
name: []const u8,
|
||||||
|
value: CPU,
|
||||||
|
) !Handle(CPU) {
|
||||||
|
// --- HANDLE ---
|
||||||
|
const handle: Handle(CPU) = .{
|
||||||
|
.idx = @intCast(self.data.items.len),
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- ADD DATA ---
|
||||||
|
try self.data.append(self.alloc, .{ .cpu = value });
|
||||||
|
errdefer _ = self.data.swapRemove(self.data.items.len - 1);
|
||||||
|
|
||||||
|
// --- ADD LOOKUP ---
|
||||||
|
try self.lookup.put(
|
||||||
|
handle,
|
||||||
|
name,
|
||||||
|
);
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn get(
|
||||||
|
self: *Self,
|
||||||
|
handle: Handle(CPU),
|
||||||
|
) ?*Record {
|
||||||
|
if (!self.lookup.contains(handle)) return null;
|
||||||
|
return &self.data.items[@intCast(handle.idx)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn items(self: *Self) []Record {
|
||||||
|
return self.data.items;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
3
src/vendors/sdl3/sdl.h
vendored
Normal file
3
src/vendors/sdl3/sdl.h
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3_image/SDL_image.h>
|
||||||
|
#include <SDL3_ttf/SDL_ttf.h>
|
||||||
Loading…
Add table
Reference in a new issue