From 2ebfdb8db05f1ed34036ef1a2398f52e337364b2 Mon Sep 17 00:00:00 2001 From: Ivory Date: Fri, 23 Aug 2024 17:00:59 -0400 Subject: [PATCH] just a bunch of cleanup to entities --- src/Entity.zig | 54 +++++++++++++++++++++++++++++++++++++++----- src/Scene.zig | 59 ++++++++++++++++++++++++++++++++++++++++--------- src/Terrain.zig | 38 +++++++++++++++++++++++-------- 3 files changed, 126 insertions(+), 25 deletions(-) diff --git a/src/Entity.zig b/src/Entity.zig index f73f10b..12ba754 100644 --- a/src/Entity.zig +++ b/src/Entity.zig @@ -2,35 +2,77 @@ const assets = @import("assets.zig"); const Layer = @import("Layer.zig"); const Color = @import("Color.zig"); const Recti = @import("geometry/Recti.zig"); +const std = @import("std"); const Entity = @This(); +const EntityOptions = struct { + renders: bool = true, + updates: bool = true, + accepts_keyboard: bool = false, + accepts_mouse: bool = false, +}; -ptr: *const anyopaque, +ptr: *anyopaque, +options: EntityOptions, +destroyFn: *const fn(*const anyopaque) void, +updateFn: *const fn(*anyopaque, f32) void, drawFn: *const fn(*const anyopaque) void, +pub fn init(ptr: anytype, options: EntityOptions) Entity { + const MutablePointer = @TypeOf(ptr); + // @compileLog("Compiling Entity type for " ++ @typeName(MutablePointer)); -pub fn init(ptr: anytype) Entity { - const type_info = @typeInfo(@TypeOf(ptr)); + const type_info = @typeInfo(MutablePointer); + // ensure the passed type is a mutable pointer if(type_info != .Pointer) @compileError("Entity implementation must be a pointer"); + // to a single thing if(type_info.Pointer.size != .One) @compileError("Entity pointer must be a single item pointer"); + // where that thing is a struct or opaque + switch (@typeInfo(type_info.Pointer.child)) { + .Struct => {}, + else => @compileError("Entity pointer must point to a struct or opaque type"), + } + + // also take the const pointer type, as we will use both mutable an immutable pointers. + // some entities mutate and some do not + const ConstPointer = *const @typeInfo(MutablePointer).Pointer.child; + // @compileLog("const pointer type: " ++ @typeName(ConstPointer)); const gen = struct { + pub fn destroyImpl(pointer: *const anyopaque) void { + const self: ConstPointer = @ptrCast(@alignCast(pointer)); + type_info.Pointer.child.destroy(self); + } + pub fn updateImpl(pointer: *anyopaque, dt: f32) void { + const self: MutablePointer = @ptrCast(@alignCast(pointer)); + type_info.Pointer.child.update(self, dt); + } pub fn drawImpl(pointer: *const anyopaque) void { - const self: @TypeOf(ptr) = @ptrCast(@alignCast(pointer)); + const self: ConstPointer = @ptrCast(@alignCast(pointer)); type_info.Pointer.child.draw(self); - // @call(.{ .modifier = .always_inline }, ptr_info.Pointer.child.draw, .{ self }); - // draw(self); } }; return .{ .ptr = ptr, + .options = options, + .destroyFn = gen.destroyImpl, + + .updateFn = gen.updateImpl, .drawFn = gen.drawImpl, }; } +pub fn update(self: *const Entity, dt: f32) void { + self.updateFn(self.ptr, dt); +} + pub fn draw(self: *const Entity) void { self.drawFn(self.ptr); } + +pub fn destroy(self: *const Entity) void { + self.destroyFn(self.ptr); +} diff --git a/src/Scene.zig b/src/Scene.zig index e0520de..dd869bc 100644 --- a/src/Scene.zig +++ b/src/Scene.zig @@ -1,3 +1,5 @@ +const Scene = @This(); + const assets = @import("assets.zig"); const Recti = @import("geometry/Recti.zig"); const Layer = @import("Layer.zig"); @@ -7,13 +9,6 @@ const Terrain = @import("Terrain.zig"); const std = @import("std"); const ArrayList = std.ArrayList; -const Scene = @This(); -// const DrawFunction = fn() void; -// const UpdateFunction = fn(f32) void; - -// draw: DrawFunction, -// update: - entities: ArrayList(Entity), pub fn new() Scene { @@ -24,14 +19,46 @@ pub fn new() Scene { } pub fn destroy(self: *Scene) void { + for (self.entities.items) |entity| { + entity.destroy(); + } self.entities.deinit(); } pub fn start(self: *Scene) !void { - const terrain = Terrain.new(); - // terrain.entity(); + const terrain = try Terrain.create(); + try self.add(terrain); +} - try self.entities.append(terrain.entity()); +// --- + +pub fn add(self: *Scene, instance_ptr: anytype) !void { + const instance_ptr_type = @TypeOf(instance_ptr); + const instance_ptr_type_info = @typeInfo(instance_ptr_type); + + if (instance_ptr_type_info != .Pointer) + @compileError( + "Cannot add to scene type " ++ + @typeName(instance_ptr_type) ++ + "Must be a mutable pointer\n" + ); + if (instance_ptr_type_info.Pointer.size != .One) + @compileError("Pointer size must be One"); + if (instance_ptr_type_info.Pointer.is_const) + @compileError("Pointer must be mutable"); + + const instance_type = instance_ptr_type_info.Pointer.child; + + if (!@hasDecl(instance_type, "entity")) + @compileError("Pointer must be to a struct with fn entity() Entity"); + + try self.entities.append(instance_ptr.entity()); +} + +pub fn update(self: *Scene, dt: f32) void { + for (self.entities.items) |entity| { + entity.update(dt); + } } pub fn draw(self: *Scene) void { @@ -45,4 +72,16 @@ pub fn draw(self: *Scene) void { assets.heart.draw(&Recti.from_xywh(80, 100, 32, 64), Layer.ENTITIES, Color.INDIGO); } +// --- helpfer functions for entities themselves +// an entity can choose to allocate itself however it pleases, but these +// are good default functions that just throw things on the heap. +pub fn allocate(instance: anytype) !*@TypeOf(instance) { + const ptr = try std.heap.page_allocator.create(@TypeOf(instance)); + ptr.* = instance; + return ptr; +} + +pub fn deallocate(instance: anytype) void { + std.heap.page_allocator.destroy(instance); +} diff --git a/src/Terrain.zig b/src/Terrain.zig index 6836222..ad97431 100644 --- a/src/Terrain.zig +++ b/src/Terrain.zig @@ -5,21 +5,41 @@ const Layer = @import("Layer.zig"); const Color = @import("Color.zig"); const Recti = @import("geometry/Recti.zig"); const Entity = @import("Entity.zig"); +const Scene = @import("Scene.zig"); const std = @import("std"); var prng = std.rand.DefaultPrng.init(0); -size: u32 = 24, -draw: u8 = 4, +size: u16 = 24, -pub fn new() Terrain { - return .{}; +pub fn entity(self: *Terrain) Entity { + return Entity.init(self, .{}); } -pub fn draw(_: *const Terrain) void { - const idx: usize = @intFromFloat(@floor(prng.random().float(f32) * 4)); - assets.terrain[idx].draw(&Recti.from_xywh(200, 100, 64, 64), Layer.FLOOR, Color.WHITE); +pub fn create() !*Terrain { + return try Scene.allocate(Terrain { + .size = 24, + }); } -pub fn entity(self: *const Terrain) Entity { - return Entity.init(self); +pub fn destroy(self: *const Terrain) void { + Scene.deallocate(self); +} + +pub fn update(self: *Terrain, _: f32) void { + if (prng.random().int(u8) == 34) { + self.size = self.size + 1; + } +} + +pub fn draw(self: *const Terrain) void { + for(0..self.size) |y| { + for(0..self.size) |x| { + const idx: usize = @intFromFloat(@floor(prng.random().float(f32) * 4)); + var grey = prng.random().float(f32); + grey /= 4.0; + grey += 3.0 / 4.0; + const rect = Recti.from_xywh(@intCast(100 + 16 * x), @intCast(100 + 16 * y), 16, 16); + assets.terrain[idx].draw(&rect, Layer.FLOOR, Color.rgba(grey, grey, grey, 1)); + } + } }