From 807f5e0c6d556ed2838bafed9e2eee460618bf29 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Fri, 25 Feb 2022 19:16:18 -0600 Subject: [PATCH] converted entity code to new component system --- src/entity/editor.onyx | 18 +-- src/entity/items.onyx | 36 ++--- src/entity/manager.onyx | 42 +++++- src/entity/player.onyx | 277 ++++++++++++++++----------------------- src/game.onyx | 23 +++- src/utils/any_utils.onyx | 5 + 6 files changed, 202 insertions(+), 199 deletions(-) diff --git a/src/entity/editor.onyx b/src/entity/editor.onyx index 88226b4..24473ba 100644 --- a/src/entity/editor.onyx +++ b/src/entity/editor.onyx @@ -55,13 +55,12 @@ editor_update :: (dt: f32) { mouse_pos := mouse_get_position_vector(); if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) && mouse_pos.x < ~~window_width - sidebar_width && mouse_pos.y > menubar_height { - /* - entity_type := ^scene.entity_types.entries[active_index].value; - - entity := entity_type.create_default(^scene); + entity_create := scene.schematics.entries[active_index].value; + entity := entity_create(^scene); scene->add(entity); + entity.pos = mouse_pos; - */ + entity.size = .{editor_grid_size, editor_grid_size}; } } @@ -224,20 +223,17 @@ editor_draw :: () { #local render_create_sidebar :: (x, y, w, h: f32) { i := 0; - /* - for scene.entity_types.entries { + for^ scene.schematics.entries { defer i += 1; - type := it.key; - info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(type); + name := it.key; y += 40.0f; theme := Button_Theme.{active = active_index == i}; - if draw_button(.{x, y - 18, w, 36.0f}, info.name, ^theme, increment=i) { + if draw_button(.{x, y - 18, w, 36.0f}, name, ^theme, increment=i) { active_index = i; } } - */ #persist test : [..] u8; if test.data == null { diff --git a/src/entity/items.onyx b/src/entity/items.onyx index c2a648f..c53bb90 100644 --- a/src/entity/items.onyx +++ b/src/entity/items.onyx @@ -87,27 +87,22 @@ item_store_get_item :: (use this: ^Item_Store, id: str) -> ^Item { return items[id]; } -Item_Entity :: struct { - use entity: Entity; - - [Editor_Custom_Field.{render_item_picker}] - item: str; - - init_data :: struct { - pos := Vector2.{0, 0}; - } - - init :: (use this: ^Item_Entity, data: init_data) { - this.pos = data.pos; - this.size = .{16, 16}; +#local Item_Entity :: struct [Entity_Schematic.{create}] { + create :: (scene) => { + this := scene->make(); + this.pos = .{0, 0}; + this.size = .{0, 0}; this.flags |= .Carryable; - } - get_rect :: Entity.get_rect + scene->create_and_add(this, RenderComponent) { comp.func = render; } + scene->create_and_add(this, ItemComponent) {} + return this; + } - draw :: (use this: ^Item_Entity) { + render :: (use this: ^Entity) { r := this->get_rect(); - item_data := item_store->get_item(item); + item_comp := this->get(ItemComponent); + item_data := item_store->get_item(item_comp.item); if item_data == null { immediate_set_color(.{1,0,0}); immediate_rectangle(r.x, r.y, r.w, r.h); @@ -125,6 +120,13 @@ Item_Entity :: struct { } } +ItemComponent :: struct { + use base: Component; + + [Editor_Custom_Field.{render_item_picker}] + item: str; +} + #local { render_item_picker :: () { diff --git a/src/entity/manager.onyx b/src/entity/manager.onyx index e54cfda..cad011e 100644 --- a/src/entity/manager.onyx +++ b/src/entity/manager.onyx @@ -33,6 +33,16 @@ RenderComponent :: struct { func : (e: ^Entity) -> void; } +SizeComponent :: struct { + use base: Component; + func : (e: ^Entity) -> Rect; +} + +InteractableComponent :: struct { + use base: Component; + interact: (e: ^Entity, interactor: ^Entity) -> void; +} + Entity :: struct { id: Entity_ID; flags: Entity_Flags; @@ -41,7 +51,11 @@ Entity :: struct { // Does every entity need to have a size? size: Vector2; - get_rect :: (use e: ^Entity) => Rect.{ pos.x - size.x / 2, pos.y - size.y / 2, size.x, size.y }; + get_rect :: (use e: ^Entity) => { + if e->has(SizeComponent) do return (e->get(SizeComponent)).func(e); + + return Rect.{ pos.x - size.x / 2, pos.y - size.y / 2, size.x, size.y }; + } components: Map(type_expr, ^Component); has :: (use this: ^Entity, component_type: type_expr) => components->has(component_type); @@ -59,6 +73,10 @@ Entity_Flags :: enum #flags { Carryable :: 0x04; } +Entity_Schematic :: struct { + create: (^Entity_Manager) -> ^Entity; +} + Entity_Manager :: struct { entity_allocator: Allocator; @@ -66,7 +84,7 @@ Entity_Manager :: struct { entities: [..] ^Entity; entity_map: Map(Entity_ID, ^Entity); - // entity_types: Map(type_expr, Entity_Info); + schematics: Map(str, (^Entity_Manager) -> ^Entity); next_entity_id: Entity_ID; @@ -96,9 +114,29 @@ entity_manager_create :: () -> Entity_Manager { // Entity ID 0 is reserved as a "empty / null" entity em.next_entity_id = 1; + entity_manager_load_schematics(^em); return em; } +#local entity_manager_load_schematics :: (use this: ^Entity_Manager) { + use type_info; + + for type_table { + if it.kind != .Struct do continue; + + s_info := cast(^Type_Info_Struct) it; + for^ s_info.tags { + if it.type == Entity_Schematic { + schematic := cast(^Entity_Schematic) it.data; + // This is safe to use as the key, as a struct's name exists in static memory. + schematics[s_info.name] = schematic.create; + + debug_log(.Debug, "Discovered schematic: '{}'", s_info.name); + } + } + } +} + entity_manager_add :: (use this: ^Entity_Manager, entity: ^Entity) -> Entity_ID { // Automatically assign an id if the entity does not have one. // This will not be used when loading the entity from a file. diff --git a/src/entity/player.onyx b/src/entity/player.onyx index 19e71b2..308f761 100644 --- a/src/entity/player.onyx +++ b/src/entity/player.onyx @@ -45,7 +45,11 @@ facing_to_direction_vector :: macro (f: Facing) -> Vector2 { } } -player_create :: (scene: ^Entity_Manager, pos := Vector2.{400, 200}, controls := player_1_controls) -> ^Entity { +#local Player :: struct [Entity_Schematic.{create}] { + create :: (scene: ^Entity_Manager) => player_create(scene, .{0,0}); +} + +player_create :: (scene: ^Entity_Manager, pos: Vector2, controls := player_1_controls) -> ^Entity { this := scene->make(); this.pos = pos; this.size = .{32, 32}; @@ -76,29 +80,6 @@ PlayerComponent :: struct { controls : Player_Controls; facing := Facing.Up; - create :: (scene: ^Entity_Manager, pos := Vector2.{400, 200}, controls := player_1_controls) -> ^Entity { - this := scene->make(); - this.pos = pos; - this.size = .{32, 32}; - this.flags |= .Solid; - - player_comp := scene->create_component(PlayerComponent); - player_comp.holding = Entity_Nothing; - player_comp.controls = controls; - player_comp.color = .{1,1,1}; - this->add(player_comp); - - update_comp := scene->create_component(UpdateComponent); - update_comp.func = update; - this->add(update_comp); - - render_comp := scene->create_component(RenderComponent); - render_comp.func = render; - this->add(render_comp); - - return this; - } - update :: (use this: ^Entity, dt: f32) { speed :: 128.0f; @@ -116,96 +97,35 @@ PlayerComponent :: struct { try_move(this, .{delta.x, 0}, walls); try_move(this, .{0, delta.y}, walls); - } - try_move :: (use this: ^Entity, delta: Vector2, obsticles: [] ^Entity) { - pos += delta; - ent_rect := Entity.get_rect(this); - - holding: Entity_ID; - if player := this->get(PlayerComponent); player != null { - holding = player.holding; - } + // Highlight the object that you are going to interact with - for obsticles { - // Don't check collision on the object that you are carrying or yourself because it probably won't make sense. - if it == this do continue; - if it.id == holding do continue; + // + // Try to interact with nearby objects + // + if is_key_just_up(player.controls.interact) { + area := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2}; + objects := scene->query_by_component(area, InteractableComponent); + defer memory.free_slice(^objects); - if Rect.intersects(ent_rect, Entity.get_rect(it)) { - pos -= delta; - break; + for objects { + interact_comp := it->get(InteractableComponent); + interact_comp.interact(it, this); } } - } - - render :: (use this: ^Entity) { - player := this->get(PlayerComponent); - immediate_set_color(player.color); - - rect := Entity.get_rect(this); - immediate_image(^player_assets.texture, rect.x, rect.y, rect.w, rect.h); - } -} - -wall_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity { - this := scene->make(); - this.pos = pos; - this.size = size; - this.flags |= .Solid; - - scene->create_and_add(this, RenderComponent) { - comp.func = wall_draw; - } - - return this; -} - -wall_draw :: (use this: ^Entity) { - immediate_set_color(.{1,1,1}); - - r := Entity.get_rect(this); - immediate_rectangle(r.x, r.y, r.w, r.h); -} - -/* - get_rect :: Entity.get_rect - - update :: (use this: ^Player, dt: f32) { - speed :: 128.0f; - - delta: Vector2; - if is_key_down(controls.left) { delta.x -= speed * dt; facing = .Left; } - if is_key_down(controls.right) { delta.x += speed * dt; facing = .Right; } - if is_key_down(controls.up) { delta.y -= speed * dt; facing = .Up; } - if is_key_down(controls.down) { delta.y += speed * dt; facing = .Down; } - - dist := math.max(size.x, size.y) * 2; - walls := scene->query_by_flags(.{pos.x - dist, pos.y - dist, dist * 2, dist * 2}, .Solid); - defer memory.free_slice(^walls); - - try_move(this, .{delta.x, 0}, walls); - try_move(this, .{0, delta.y}, walls); - - // Highlight the object that you are going to interact with // // Try to pick up nearby objects // - if is_key_just_up(controls.pick_up) { - if holding != Entity_Nothing { - holding_object := scene->get(holding); + if is_key_just_up(player.controls.pick_up) { + if player.holding != Entity_Nothing { + holding_object := scene->get(player.holding); holding_object.flags |= .Carryable; - /* :ENT_INFO - obj_get_rect := ENT_INFO(holding_object).get_rect; - if obj_get_rect != null_proc { - r := get_rect(holding_object); + r := Entity.get_rect(holding_object); - d := Vector2.{(size.x + r.w) / 2 + 4, (size.y + r.h) / 2 + 4}; - holding_object.pos = pos + facing_to_direction_vector(facing) * d; - } - */ - holding = Entity_Nothing; + d := Vector2.{(size.x + r.w) / 2 + 4, (size.y + r.h) / 2 + 4}; + holding_object.pos = pos + facing_to_direction_vector(player.facing) * d; + player.holding = Entity_Nothing; } else { area := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2}; @@ -219,7 +139,7 @@ wall_draw :: (use this: ^Entity) { target_object := objects[i]; target_object.flags ^= .Carryable; // This only works because we assume that it is Carryable, otherwise this would make it carryable. - this.holding = target_object.id; + player.holding = target_object.id; break; } @@ -227,111 +147,134 @@ wall_draw :: (use this: ^Entity) { } } - // - // Try to interact with nearby objects - // - if is_key_just_up(controls.interact) { - area := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2}; - objects := scene->query_by_flags(area, .Interactable); - defer memory.free_slice(^objects); - - for objects { - /* :ENT_INFO - if interact := ENT_INFO(it).interact; interact != null_proc { - interact(it, this); - } - */ - } - } - // // Adjust the position of the entity you are carrying // to be above the players head. // - if holding != Entity_Nothing { - holding_object := scene->get(holding); - /* :ENT_INFO - obj_get_rect := ENT_INFO(holding_object).get_rect; - if obj_get_rect != null_proc { - r := obj_get_rect(holding_object); - holding_object.pos = pos - .{0, (size.y + r.h) / 2}; - } - */ + if player.holding != Entity_Nothing { + holding_object := scene->get(player.holding); + r := Entity.get_rect(holding_object); + holding_object.pos = pos - .{0, (size.y + r.h) / 2}; } } - // This should be made generic and work for all kinds of entities - try_move :: (use this: ^Player, delta: Vector2, obsticles: [] ^Entity) { + try_move :: (use this: ^Entity, delta: Vector2, obsticles: [] ^Entity) { pos += delta; - ent_rect := get_rect(this); + ent_rect := Entity.get_rect(this); + + holding: Entity_ID; + if player := this->get(PlayerComponent); player != null { + holding = player.holding; + } for obsticles { // Don't check collision on the object that you are carrying or yourself because it probably won't make sense. if it == this do continue; - if it.id == this.holding do continue; + if it.id == holding do continue; - /* :ENT_INFO - vtable := ENT_INFO(it); - if Rect.intersects(ent_rect, vtable.get_rect(it)) { + if Rect.intersects(ent_rect, Entity.get_rect(it)) { pos -= delta; break; } - */ } } + render :: (use this: ^Entity) { + player := this->get(PlayerComponent); + immediate_set_color(player.color); + + rect := Entity.get_rect(this); + immediate_image(^player_assets.texture, rect.x, rect.y, rect.w, rect.h); + } +} + +#local Wall :: struct [Entity_Schematic.{create}] { + create :: (scene) => wall_create(scene, .{0,0}, .{0,0}); + + draw :: (use this: ^Entity) { + immediate_set_color(.{1,1,1}); + + r := Entity.get_rect(this); + immediate_rectangle(r.x, r.y, r.w, r.h); + } +} + +wall_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity { + this := scene->make(); + this.pos = pos; + this.size = size; + this.flags |= .Solid; + + scene->create_and_add(this, RenderComponent) { + comp.func = Wall.draw; + } + + return this; } -*/ player_assets: struct { ["assets/images/player.png"] texture: Texture; } -Door :: struct { - use entity: Entity; - - [Entity_Store.Skip] openness := 0.0f; - target_openness := 0.0f; +#local Door :: struct [Entity_Schematic.{create}] { + create :: (scene) => door_create(scene, .{0, 0}, .{0, 0}); - init_data :: struct { - pos := Vector2.{0, 0}; - size := Vector2.{10, 10}; + update :: (use this: ^Entity, dt: f32) { + door := this->get(DoorComponent); + if door.openness != door.target_openness { + move_towards(^door.openness, door.target_openness, dt * 2); + } } - init :: (use this: ^Door, data: init_data) { - this.pos = data.pos; - this.size = data.size; + render :: (use this: ^Entity) { + immediate_set_color(.{0.7, 0.7, 0.1}); - this.flags |= .Interactable; - this.flags |= .Solid; + r := Entity.get_rect(this); + immediate_rectangle(r.x, r.y, r.w, r.h); } - get_rect :: (use this: ^Door) => { - size_x := size.x * (1 - openness); + get_rect :: (use this: ^Entity) => { + door := this->get(DoorComponent); + size_x := size.x * (1 - door.openness); return Rect.{ pos.x - size_x / 2, pos.y - size.y / 2, size_x, size.y }; } - update :: (use this: ^Door, dt: f32) { - if openness != target_openness { - move_towards(^openness, target_openness, dt * 2); + interact :: (use this: ^Entity, interactor: ^Entity) { + // Doors only open if interacted with by a player + if interactor->has(PlayerComponent) { + door := this->get(DoorComponent); + door->toggle_open(); } } +} - interact :: (use this: ^Door, interactor: ^Entity) { - // Doors only open if interacted with by a player - //if_entity_is(interactor, Player) { - target_openness = 0.8f - target_openness; - //} - } +DoorComponent :: struct { + use base: Component; - draw :: (use this: ^Door) { - immediate_set_color(.{0.7, 0.7, 0.1}); + target_openness := 0.0f; + openness := 0.0f; - r := this->get_rect(); - immediate_rectangle(r.x, r.y, r.w, r.h); + toggle_open :: (use this: ^DoorComponent) { + target_openness = 0.8f - target_openness; } } +door_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity { + this := scene->make(); + this.pos = pos; + this.size = size; + + this.flags |= .Interactable; + this.flags |= .Solid; + + scene->create_and_add(this, DoorComponent) {} + scene->create_and_add(this, UpdateComponent) { comp.func = Door.update; } + scene->create_and_add(this, RenderComponent) { comp.func = Door.render; } + scene->create_and_add(this, SizeComponent) { comp.func = Door.get_rect; } + scene->create_and_add(this, InteractableComponent) { comp.interact = Door.interact; } + + return this; +} diff --git a/src/game.onyx b/src/game.onyx index de36b34..83c1d95 100644 --- a/src/game.onyx +++ b/src/game.onyx @@ -18,12 +18,31 @@ game_init :: () { scene = entity_manager_create(); // scene->load_from_file("scenes/quick_save.scene"); - player := player_create(^scene); + // player := player_create(^scene); + + player := scene.schematics["Player"](^scene); scene->add(player); - wall := wall_create(^scene, .{0, 100}, .{300, 40}); + // wall := wall_create(^scene, .{0, 100}, .{300, 40}); + wall := scene.schematics["Wall"](^scene); + wall.pos = .{0, 100}; + wall.size = .{300, 40}; scene->add(wall); + door := scene.schematics["Door"](^scene); + door.pos = .{500, 300}; + door.size = .{100, 20}; + scene->add(door); + + item := scene.schematics["Item_Entity"](^scene); + item.pos = .{100, 200}; + item.size = .{32, 32}; + with (item->get(ItemComponent)) { + it.item = "beer"; + } + + scene->add(item); + item_store = item_store_make(); item_store->load_items_from_file("scenes/default.items"); } diff --git a/src/utils/any_utils.onyx b/src/utils/any_utils.onyx index 46d1b17..ab06c1d 100644 --- a/src/utils/any_utils.onyx +++ b/src/utils/any_utils.onyx @@ -40,3 +40,8 @@ get_any_for_member :: (base: any, member_name: str) -> any { return .{null, void}; } + +with :: macro (v: $T, body: Code) { + it := v; + #insert body; +} -- 2.25.1