const gfx = @import("gfx"); const hid = @import("hid"); const ona = @import("ona"); const std = @import("std"); const Spawned = struct { visual: struct { color: gfx.Color, position: gfx.Vector2, rotation: f32, }, lifetime_seconds: f32, }; const Visuals = struct { spawned: ona.stack.Parallel(Spawned) = .{}, random: std.Random.Xoroshiro128, mouse_position: @Vector(2, f32) = @splat(0), }; fn cleanup(visuals: ona.Write(Visuals)) !void { visuals.state.spawned.deinit(); } fn load(visuals: ona.Write(Visuals)) !void { const initial_spawn_capacity = 1024; try visuals.state.spawned.grow(initial_spawn_capacity); } pub const main = ona.App.game .with_module(gfx) .with_module(hid) .with_state(Visuals{.random = std.Random.Xoroshiro128.init(47342563891212)}) .with_system(.load, ona.system_fn(load), .{.label = "load visuals"}) .with_system(.update, ona.system_fn(update), .{.label = "spawn visuals"}) .with_system(.render, ona.system_fn(render), .{.label = "render visuals"}) .with_system(.exit, ona.system_fn(cleanup), .{.label = "clean up visuals"}).build(); fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display: ona.Read(gfx.Display)) !void { update_spawned(&visuals.state.spawned); const random = visuals.state.random.random(); const width: f32 = @floatFromInt(display.state.width); const height: f32 = @floatFromInt(display.state.height); for (events.messages()) |event| { switch (event) { .key_down => { try visuals.state.spawned.push_grow(.{ .lifetime_seconds = 2.5 + (5 * random.float(f32)), .visual = .{ .color = .{random.float(f32), random.float(f32), random.float(f32), random.float(f32)}, .position = .{width * random.float(f32), height}, .rotation = std.math.pi * random.float(f32), }, }); }, .mouse_down => { try visuals.state.spawned.push_grow(.{ .lifetime_seconds = 2.5 + (5 * random.float(f32)), .visual = .{ .color = .{random.float(f32), random.float(f32), random.float(f32), random.float(f32)}, .position = visuals.state.mouse_position, .rotation = std.math.pi * random.float(f32), }, }); }, .mouse_motion => |motion| { visuals.state.mouse_position = motion.absolute_position; }, else => {} } } } fn update_spawned(spawned: *ona.stack.Parallel(Spawned)) void { const float_speed = 6; for (spawned.values.slice(.visual)) |*visual| { visual.position -= .{0, float_speed}; } { var range = spawned.len(); var index: usize = 0; while (index < range) : (index += 1) { const lifetime_seconds = spawned.values.get(.lifetime_seconds, index).?; lifetime_seconds.* -= 1.0 / 60.0; if (lifetime_seconds.* <= 0) { range -= 1; std.mem.swap(f32, lifetime_seconds, spawned.values.get(.lifetime_seconds, range).?); } } std.debug.assert(spawned.pop_many(spawned.len() - range)); } } fn render(visuals: ona.Write(Visuals), commands: gfx.Commands(.foreground)) !void { for (visuals.state.spawned.values.slice(.visual)) |visual| { try commands.draw_texture(.{ .anchor = @splat(0.5), .position = visual.position, .tint = visual.color, .size = @as(gfx.Vector2, @splat(64)), }); } }