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 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);
}

View File

@ -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);
}

View File

@ -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 {
pub fn create() !*Terrain {
return try Scene.allocate(Terrain {
.size = 24,
});
}
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));
assets.terrain[idx].draw(&Recti.from_xywh(200, 100, 64, 64), Layer.FLOOR, Color.WHITE);
}
pub fn entity(self: *const Terrain) Entity {
return Entity.init(self);
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));
}
}
}