From: Brendan Hansen Date: Mon, 28 Feb 2022 17:50:56 +0000 (-0600) Subject: flushing out component system X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=bbb66ba6ab014c48f1c140e550e04fd1d9a2daa6;p=bar-game.git flushing out component system --- diff --git a/run_tree/lib/onyx_openal.so b/run_tree/lib/onyx_openal.so new file mode 100755 index 0000000..77bd206 Binary files /dev/null and b/run_tree/lib/onyx_openal.so differ diff --git a/run_tree/scenes/default.items b/run_tree/scenes/default.items index 5b797fe..d0ee5ee 100644 --- a/run_tree/scenes/default.items +++ b/run_tree/scenes/default.items @@ -1,5 +1,7 @@ :beer name="Beer" texture_path="./assets/images/beer-1.png" +texture_pos = 0 0 +texture_size = 16 16 diff --git a/run_tree/scenes/quick_save_new.scene b/run_tree/scenes/quick_save_new.scene index 8d39eec..19205dd 100644 --- a/run_tree/scenes/quick_save_new.scene +++ b/run_tree/scenes/quick_save_new.scene @@ -13,7 +13,7 @@ color.a = 1.0000 [Wall] id = 11 -flags = 2 +flags = 3 pos.x = 440.0000 pos.y = 160.0000 size.x = 16.0000 @@ -27,8 +27,8 @@ color.a = 1.0000 [Player] id = 12 flags = 2 -pos.x = 452.6227 -pos.y = 356.0881 +pos.x = 544.3174 +pos.y = 236.6171 size.x = 32.0000 size.y = 32.0000 :MovementComponent @@ -38,7 +38,7 @@ controls.left = 65 controls.right = 68 controls.interact = 70 controls.pick_up = 71 -facing = 1 +facing = 4 :PlayerComponent holding = 0 :RenderComponent @@ -79,3 +79,34 @@ color.a = 1.0000 :ItemComponent item = "beer" +[Item_Entity] +id = 15 +flags = 4 +pos.x = 555.2645 +pos.y = 298.4719 +size.x = 16.0000 +size.y = 16.0000 +:RenderComponent +color.r = 1.0000 +color.g = 1.0000 +color.b = 1.0000 +color.a = 1.0000 +:ItemComponent +item = "beer" + +[Tap] +id = 16 +flags = 3 +pos.x = 576.0000 +pos.y = 160.0000 +size.x = 32.0000 +size.y = 32.0000 +:RenderComponent +color.r = 0.1000 +color.g = 0.1000 +color.b = 1.0000 +color.a = 1.0000 +:DispenserComponent +item = "beer" +max_timeout = 5.0000 + diff --git a/src/build.onyx b/src/build.onyx index b949ecf..4adc9a1 100644 --- a/src/build.onyx +++ b/src/build.onyx @@ -13,29 +13,13 @@ DEBUG :: false #load "core/std" -#load "config" -#load "game" -#load "main" - -#load "entity/editor" -#load "entity/items" -#load "entity/manager" -#load "entity/player" -#load "entity/store" - -#load "gfx/canvas" -#load "gfx/font" -#load "gfx/immediate" -#load "gfx/mesh" -#load "gfx/shader" -#load "gfx/texture" -#load "gfx/ui" - -#load "utils/any_utils" -#load "utils/asset_loader" -#load "utils/input" -#load "utils/logger" -#load "utils/vecmath" +#load_all "./." + +#load_all "./entity" +#load_all "./entity/components" +#load_all "./entity/schematics" +#load_all "./gfx" +#load_all "./utils" #load "stb_image" #load "stb_truetype" diff --git a/src/entity/components/dispenser.onyx b/src/entity/components/dispenser.onyx new file mode 100644 index 0000000..748f6d8 --- /dev/null +++ b/src/entity/components/dispenser.onyx @@ -0,0 +1,70 @@ + +use package core + +// +// Currently, DispenserComponents only dispense Item_Entity's, and no +// other entity type. I can't think of a reason at the moment that +// you would need to dispense something else, but that is a limitation. + +DispenserComponent :: struct { + use base: Component; + + item: str; + max_timeout := 2.0f; + + #tag Entity_Store.Skip + timeout := 0.0f; + + added :: (use this: ^DispenserComponent, entity: ^Entity) { + if entity->has(InteractableComponent) { + debug_log(.Error, "DispenserComponent expected entity to not have an InteractableComponent"); + } + + scene->create_and_add(entity, InteractableComponent) { + comp.interact = interact; + } + } + + update :: (use this: ^DispenserComponent, entity: ^Entity, dt: f32) { + timeout -= dt; + if timeout < 0 do timeout = 0; + } + + interact :: (this: ^Entity, entity: ^Entity) { + if entity->has(PlayerComponent) { + player_comp := entity->get(PlayerComponent); + if player_comp.holding != Entity_Nothing do return; + + dispenser_comp := this->get(DispenserComponent); + if dispenser_comp.timeout != 0 do return; + + item := scene->create_from_schematic("Item_Entity"); + if item == null { + debug_log(.Error, "Bad schematic type."); + return; + } + + // This should dynamic... + item.size = .{16, 16}; + (item->get(ItemComponent)).item = dispenser_comp.item; + + scene->add(item); + player_comp.holding = item.id; + dispenser_comp.timeout = dispenser_comp.max_timeout; + } + } + + post_render :: (use this: ^DispenserComponent, entity: ^Entity) { + if timeout != 0 { + rect := entity->get_rect(); + + percent := (max_timeout - timeout) / max_timeout; + rect.y += (1 - percent) * rect.h; + rect.h *= percent; + + immediate_set_color(.{0.4, 1, 0.4, 0.8}); + immediate_rectangle(rect.x, rect.y, rect.w, rect.h); + } + } +} + diff --git a/src/entity/items.onyx b/src/entity/items.onyx index 5229cca..a96a1da 100644 --- a/src/entity/items.onyx +++ b/src/entity/items.onyx @@ -7,6 +7,8 @@ Item :: struct { color := Color.{ 1, 1, 1 }; texture_path : str; + texture_pos := Vector2.{0, 0}; + texture_size := Vector2.{1, 1}; } Item_Store :: struct { @@ -116,7 +118,11 @@ item_store_get_item :: (use this: ^Item_Store, id: str) -> ^Item { if !loaded { immediate_rectangle(r.x, r.y, r.w, r.h); } else { - immediate_image(^texture, r.x, r.y, r.w, r.h); + tp := item_data.texture_pos; + ts := item_data.texture_size; + // immediate_image(^texture, r.x, r.y, r.w, r.h); + immediate_subimage(^texture, r.x, r.y, r.w, r.h, + tp.x, tp.y, ts.x, ts.y); } } } diff --git a/src/entity/manager.onyx b/src/entity/manager.onyx index 5b18c1f..bf4d5fc 100644 --- a/src/entity/manager.onyx +++ b/src/entity/manager.onyx @@ -16,8 +16,30 @@ Entity_ID :: #distinct u32 } #local Component_Vtable :: struct { - init : (rawptr) -> void = null_proc; - update: (rawptr, ^Entity, f32) -> void = null_proc; + // + // 'init' is called when the component is first created, before it + // has been added to any entity. + init : (rawptr) -> void = null_proc; + + // + // 'added' is called when the component is added to an entity. + // In theory, it could be called multiple times, if the component + // is shared between multiple entities. + added : (rawptr, ^Entity) -> void = null_proc; + + // + // 'update' is called every update cycle. Currently, there is no + // specified as to the order in which components get updated. + // I think by circumstance, it is the order that you add them + // to the entity, but could break in the future. + update : (rawptr, ^Entity, f32) -> void = null_proc; + + // + // 'post_render' is called after the entity has been rendered + // normally. Entities must have a 'RenderComponent' in order + // to have 'post_render' called on their components. This + // can be used to add overlays or popups to an entity. + post_render: (rawptr, ^Entity) -> void = null_proc; } Component :: struct { @@ -38,7 +60,8 @@ RenderComponent :: struct { #tag Editor_Hidden, Entity_Store.Skip func : (e: ^Entity) -> void; - color := Color.{1,1,1}; + layer := 0; + color := Color.{1, 1, 1}; } SizeComponent :: struct { @@ -80,6 +103,9 @@ Entity :: struct { add :: (use this: ^Entity, component: ^Component) => { if components->has(component.type) do return; components[component.type] = component; + if component.added != null_proc { + component->added(this); + } } } @@ -253,11 +279,20 @@ entity_manager_update :: (use this: ^Entity_Manager, dt: f32) { entity_manager_draw :: (use this: ^Entity_Manager) { // Entities should be sorted by z-order. + + for entities { render_comp := it->get(RenderComponent); if render_comp != null { immediate_set_color(render_comp.color); render_comp.func(it); + + for^ entry: it.components.entries { + comp := entry.value; + if comp.post_render != null_proc { + comp->post_render(it); + } + } } } } diff --git a/src/entity/player.onyx b/src/entity/player.onyx index cf207d8..fdb2185 100644 --- a/src/entity/player.onyx +++ b/src/entity/player.onyx @@ -216,6 +216,7 @@ wall_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity { player_assets: struct { #tag "assets/images/player.png" + #tag Texture_Wrap.Clamp texture: Texture; } diff --git a/src/entity/schematics/tap.onyx b/src/entity/schematics/tap.onyx new file mode 100644 index 0000000..98a9907 --- /dev/null +++ b/src/entity/schematics/tap.onyx @@ -0,0 +1,35 @@ + +Tap :: struct { + #struct_tag Entity_Schematic.{ + (scene) => Tap.create(scene, .{0,0}, .{0,0}) + } + + create :: (scene: ^Entity_Manager, pos: Vector2, size: Vector2) -> ^Entity { + this := scene->make(); + this.pos = pos; + this.size = size; + this.flags |= .Solid; + this.flags |= .Interactable; + + scene->create_and_add(this, RenderComponent) { + comp.func = render; + } + + scene->create_and_add(this, DispenserComponent) { + comp.item = "beer"; + } + + return this; + } + + render :: (use this: ^Entity) { + rect := this->get_rect(); + + immediate_rectangle(rect.x, rect.y, rect.w, rect.h); + } +} + +tap_assets: struct { + #tag "./assets/" + texture: Texture; +} \ No newline at end of file diff --git a/src/game.onyx b/src/game.onyx index 8a218bd..5f55149 100644 --- a/src/game.onyx +++ b/src/game.onyx @@ -58,6 +58,7 @@ game_draw :: () { canvas_use(null); immediate_clear(.{1, 1, 1}); + immediate_set_color(.{1, 1, 1}); texture := canvas_to_texture(^scene_canvas); view_rect: Rect; diff --git a/src/gfx/texture.onyx b/src/gfx/texture.onyx index 2ceb980..0df03b8 100644 --- a/src/gfx/texture.onyx +++ b/src/gfx/texture.onyx @@ -46,7 +46,7 @@ texture_lookup :: #match {} glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); @@ -65,3 +65,16 @@ texture_use :: (use tex: ^Texture, texture_binding := 0) { glActiveTexture(GL_TEXTURE0 + texture_binding); glBindTexture(GL_TEXTURE_2D, texture); } + + +Texture_Wrap :: enum { + Clamp :: GL_CLAMP_TO_EDGE | 0; + Repeat :: GL_REPEAT | 0; + Mirrored_Repeat :: GL_MIRRORED_REPEAT | 0; +} + +texture_wrap :: (use tex: ^Texture, wrap: Texture_Wrap) { + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ~~ wrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ~~ wrap); +} \ No newline at end of file diff --git a/src/main.onyx b/src/main.onyx index 42138c5..fa1abb9 100644 --- a/src/main.onyx +++ b/src/main.onyx @@ -141,7 +141,7 @@ create_window :: () => { } main :: (args) => { - debug_set_level(.Info); + debug_set_level(.Debug); if !glfwInit() { debug_log(.Critical, "Failed to initialize GLFW!"); os.exit(1); diff --git a/src/utils/any_utils.onyx b/src/utils/any_utils.onyx index ab06c1d..803615d 100644 --- a/src/utils/any_utils.onyx +++ b/src/utils/any_utils.onyx @@ -30,7 +30,7 @@ get_any_for_member :: (base: any, member_name: str) -> any { for info.members { if it.name == part_name { member_info := get_type_info(it.type); - if member_info.kind == .Struct { + if member_info.kind == .Struct && name != "" { return get_any_for_member(any.{cast(^u8) base.data + it.offset, it.type}, name); } else { return any.{cast(^u8) base.data + it.offset, it.type}; diff --git a/src/utils/asset_loader.onyx b/src/utils/asset_loader.onyx index c4ecea6..ccf5f73 100644 --- a/src/utils/asset_loader.onyx +++ b/src/utils/asset_loader.onyx @@ -44,6 +44,11 @@ load_assets :: () { if path_tag != null { path := *cast(^str) path_tag.data; if texture, success := texture_lookup(path); success { + if wrap_tag := array.first(tags, (x) => x.type == Texture_Wrap); wrap_tag != null { + wrap := *cast(^Texture_Wrap) wrap_tag.data; + texture_wrap(^texture, wrap); + } + *out_texture = texture; } else { debug_log(.Error, "Failed to load texture '{}' for asset queued here: {}:{},{}.\n", path, location.file, location.line, location.column); diff --git a/src/utils/vecmath.onyx b/src/utils/vecmath.onyx index 9dbf7c1..3bda373 100644 --- a/src/utils/vecmath.onyx +++ b/src/utils/vecmath.onyx @@ -5,6 +5,7 @@ Vector2 :: struct { Zero :: Vector2.{0, 0} #struct_tag conv.Custom_Format.{format_vector2} + #struct_tag conv.Custom_Parse.{parse_vector2} } Vector3i :: struct { @@ -79,6 +80,21 @@ Vector3 :: struct { format_vector3i :: (output: ^conv.Format_Output, format: ^conv.Format, v: ^Vector3i) { conv.format(output, "({}, {}, {})", v.x, v.y, v.z); } + + parse_vector2 :: (output: ^Vector2, line_: str, string_allocator: Allocator) -> bool { + string :: package core.string + + line := line_; + xs := string.read_until(^line, #char " "); + string.advance(^line, 1); + ys := string.read_until(^line, #char " "); + + if xs == "" || ys == "" do return false; + + output.x = ~~ conv.str_to_f64(xs); + output.y = ~~ conv.str_to_f64(ys); + return true; + } }