From d3b03901fb7ae185e8c971318669e84ba414adaa Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Mon, 4 Jul 2022 22:19:35 -0700 Subject: [PATCH] examples: add initial ecs-app This is a temporary application to begin iterating on high-level ECS applications. Eventually, this will be removed - for now it's just here so we can see how this API looks today and improve it. Signed-off-by: Stephen Gutekanst --- build.zig | 4 ++- examples/ecs-app/main.zig | 54 ++++++++++++++++++++++++++++++++++ examples/ecs-app/physics2d.zig | 26 ++++++++++++++++ examples/ecs-app/renderer.zig | 13 ++++++++ 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 examples/ecs-app/main.zig create mode 100644 examples/ecs-app/physics2d.zig create mode 100644 examples/ecs-app/renderer.zig diff --git a/build.zig b/build.zig index cab5d275..52e9cbd5 100644 --- a/build.zig +++ b/build.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); pub const gpu = @import("gpu/build.zig"); const gpu_dawn = @import("gpu-dawn/build.zig"); pub const glfw = @import("glfw/build.zig"); +pub const ecs = @import("ecs/build.zig"); const freetype = @import("freetype/build.zig"); const Pkg = std.build.Pkg; @@ -42,6 +43,7 @@ pub fn build(b: *std.build.Builder) void { .{ .name = "fractal-cube", .packages = &[_]Pkg{Packages.zmath} }, .{ .name = "gkurve", .packages = &[_]Pkg{ Packages.zmath, Packages.zigimg, freetype.pkg }, .std_platform_only = true }, .{ .name = "textured-cube", .packages = &[_]Pkg{ Packages.zmath, Packages.zigimg } }, + .{ .name = "ecs-app", .packages = &[_]Pkg{} }, }) |example| { // FIXME: this is workaround for a problem that some examples (having the std_platform_only=true field) as // well as zigimg uses IO which is not supported in freestanding environments. So break out of this loop @@ -285,7 +287,7 @@ pub const App = struct { pub const pkg = std.build.Pkg{ .name = "mach", .source = .{ .path = thisDir() ++ "/src/main.zig" }, - .dependencies = &.{gpu.pkg}, + .dependencies = &.{ gpu.pkg, ecs.pkg }, }; fn thisDir() []const u8 { diff --git a/examples/ecs-app/main.zig b/examples/ecs-app/main.zig new file mode 100644 index 00000000..a87dff06 --- /dev/null +++ b/examples/ecs-app/main.zig @@ -0,0 +1,54 @@ +// Experimental ECS app example. Not yet ready for actual use. + +const std = @import("std"); +const mach = @import("mach"); +const gpu = mach.gpu; +const ecs = mach.ecs; + +// TODO: rename *ecs.World to *engine.Engine or something + +const renderer = @import("renderer.zig"); +const physics2d = @import("physics2d.zig"); + +// Define all the modules in our application. Modules can have components, systems, state, +// and/or global values in them. They can also send and receive messages to coordinate +// with each-other. +// +// Single-word module names (`.mach`, `.renderer`, etc.) are reserved for the application itself. +// +// Modules that come from libraries must be prefixed (e.g. `.bullet_physics`, `.ziglibs_box2d`) +// similar to GitHub repositories, to avoid conflicts with one another. Note that modules themselves +// will interact with the ECS using e.g. `.getComponent(.bullet_physics, .location)` internally and +// so cannot be renamed here. +// TODO: just make this a list so one cannot even think renaming is possible here +const modules = ecs.Modules(.{ + .mach = mach.module, + .renderer = renderer.module, + .physics2d = physics2d.module, +}); + +// Our Mach app, which tells Mach where to find our modules and init entry point. +pub const App = mach.App(modules, init); + +pub fn init(engine: *ecs.World(modules)) !void { + // The Mach .core is where we set window options, etc. + const core = engine.get(.mach, .core); + try core.setOptions(.{ .title = "Hello, ECS!" }); + + // We can get the GPU device: + const device = engine.get(.mach, .device); + _ = device; // TODO: actually show off using the GPU device + + // We can create entities, and set components on them. Note that components live in a module + // namespace, so we set the `.renderer, .location` component which is different than the + // `.physics2d, .location` component. + + // TODO: cut out the `.entities.` in this API to make it more brief + const player = try engine.entities.new(); + try engine.entities.setComponent(player, .renderer, .location, .{ .x = 0, .y = 0, .z = 0 }); + try engine.entities.setComponent(player, .physics2d, .location, .{ .x = 0, .y = 0 }); + _ = player; + + // TODO: there could be an entities wrapper to interact with a single namespace so you don't + // have to pass it in as a parameter always? +} diff --git a/examples/ecs-app/physics2d.zig b/examples/ecs-app/physics2d.zig new file mode 100644 index 00000000..948a1713 --- /dev/null +++ b/examples/ecs-app/physics2d.zig @@ -0,0 +1,26 @@ +const mach = @import("mach"); +const ecs = mach.ecs; +const std = @import("std"); + +pub const Message = ecs.Messages(.{ + .tick = void, +}); + +pub const module = ecs.Module(.{ + .components = .{ + .location = Vec2, + .rotation = Vec2, + .velocity = Vec2, + }, + .messages = Message, + .update = update, +}); + +pub const Vec2 = struct { x: f32, y: f32 }; + +fn update(msg: Message) void { + switch (msg) { + // TODO: implement queries, ability to set components, etc. + .tick => std.debug.print("\nphysics tick!\n", .{}), + } +} diff --git a/examples/ecs-app/renderer.zig b/examples/ecs-app/renderer.zig new file mode 100644 index 00000000..b14bd4c5 --- /dev/null +++ b/examples/ecs-app/renderer.zig @@ -0,0 +1,13 @@ +const mach = @import("mach"); +const ecs = mach.ecs; + +pub const module = ecs.Module(.{ + .components = .{ + .location = Vec3, + .rotation = Vec3, + }, + // TODO: there would be systems that we register here. Functions that iterate over entities + // with renderer components like `.geometry` and render them for example! +}); + +pub const Vec3 = struct { x: f32, y: f32, z: f32 };