just a bunch of cleanup to entities

stable
Ivory 2024-08-23 17:00:59 -04:00
parent a6d543ced5
commit 2ebfdb8db0
3 changed files with 126 additions and 25 deletions

View File

@ -2,35 +2,77 @@ const assets = @import("assets.zig");
const Layer = @import("Layer.zig"); const Layer = @import("Layer.zig");
const Color = @import("Color.zig"); const Color = @import("Color.zig");
const Recti = @import("geometry/Recti.zig"); const Recti = @import("geometry/Recti.zig");
const std = @import("std");
const Entity = @This(); 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, 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(MutablePointer);
const type_info = @typeInfo(@TypeOf(ptr));
// ensure the passed type is a mutable pointer
if(type_info != .Pointer) @compileError("Entity implementation must be a 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"); 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 { 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 { 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); type_info.Pointer.child.draw(self);
// @call(.{ .modifier = .always_inline }, ptr_info.Pointer.child.draw, .{ self });
// draw(self);
} }
}; };
return .{ return .{
.ptr = ptr, .ptr = ptr,
.options = options,
.destroyFn = gen.destroyImpl,
.updateFn = gen.updateImpl,
.drawFn = gen.drawImpl, .drawFn = gen.drawImpl,
}; };
} }
pub fn update(self: *const Entity, dt: f32) void {
self.updateFn(self.ptr, dt);
}
pub fn draw(self: *const Entity) void { pub fn draw(self: *const Entity) void {
self.drawFn(self.ptr); self.drawFn(self.ptr);
} }
pub fn destroy(self: *const Entity) void {
self.destroyFn(self.ptr);
}

View File

@ -1,3 +1,5 @@
const Scene = @This();
const assets = @import("assets.zig"); const assets = @import("assets.zig");
const Recti = @import("geometry/Recti.zig"); const Recti = @import("geometry/Recti.zig");
const Layer = @import("Layer.zig"); const Layer = @import("Layer.zig");
@ -7,13 +9,6 @@ const Terrain = @import("Terrain.zig");
const std = @import("std"); const std = @import("std");
const ArrayList = std.ArrayList; const ArrayList = std.ArrayList;
const Scene = @This();
// const DrawFunction = fn() void;
// const UpdateFunction = fn(f32) void;
// draw: DrawFunction,
// update:
entities: ArrayList(Entity), entities: ArrayList(Entity),
pub fn new() Scene { pub fn new() Scene {
@ -24,14 +19,46 @@ pub fn new() Scene {
} }
pub fn destroy(self: *Scene) void { pub fn destroy(self: *Scene) void {
for (self.entities.items) |entity| {
entity.destroy();
}
self.entities.deinit(); self.entities.deinit();
} }
pub fn start(self: *Scene) !void { pub fn start(self: *Scene) !void {
const terrain = Terrain.new(); const terrain = try Terrain.create();
// terrain.entity(); 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 { 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); 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);
}

View File

@ -5,21 +5,41 @@ const Layer = @import("Layer.zig");
const Color = @import("Color.zig"); const Color = @import("Color.zig");
const Recti = @import("geometry/Recti.zig"); const Recti = @import("geometry/Recti.zig");
const Entity = @import("Entity.zig"); const Entity = @import("Entity.zig");
const Scene = @import("Scene.zig");
const std = @import("std"); const std = @import("std");
var prng = std.rand.DefaultPrng.init(0); var prng = std.rand.DefaultPrng.init(0);
size: u32 = 24, size: u16 = 24,
draw: u8 = 4,
pub fn new() Terrain { pub fn entity(self: *Terrain) Entity {
return .{}; return Entity.init(self, .{});
} }
pub fn draw(_: *const Terrain) void { pub fn create() !*Terrain {
const idx: usize = @intFromFloat(@floor(prng.random().float(f32) * 4)); return try Scene.allocate(Terrain {
assets.terrain[idx].draw(&Recti.from_xywh(200, 100, 64, 64), Layer.FLOOR, Color.WHITE); .size = 24,
});
} }
pub fn entity(self: *const Terrain) Entity { pub fn destroy(self: *const Terrain) void {
return Entity.init(self); 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));
}
}
} }