Fixed bug where components of the same type would overlap because they were forced to be 8 bytes.
This commit is contained in:
103
ecs.odin
103
ecs.odin
@@ -1,5 +1,7 @@
|
|||||||
package ecs
|
package ecs
|
||||||
|
|
||||||
|
import "core:mem"
|
||||||
|
import "base:runtime"
|
||||||
import "core:container/queue"
|
import "core:container/queue"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:log"
|
import "core:log"
|
||||||
@@ -29,14 +31,21 @@ entity_old :: proc(world: ^World, entity: Entity) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component_Storage :: struct {
|
Component_Storage :: struct {
|
||||||
data: []typeid,
|
data: rawptr,
|
||||||
|
elem_size: u32,
|
||||||
alive: []bool,
|
alive: []bool,
|
||||||
}
|
}
|
||||||
Component_Registry :: map[typeid]Component_Storage
|
Component_Registry :: map[typeid]Component_Storage
|
||||||
|
|
||||||
|
get_addr_of_component_for_entity_id_from_storage :: proc(storage: ^Component_Storage, entity_id: Entity_ID) -> rawptr {
|
||||||
|
return rawptr(uintptr(storage.data) + uintptr((u32(entity_id) * storage.elem_size)))
|
||||||
|
}
|
||||||
|
|
||||||
// IDEA: SS - Add a 'World_State' variable that contains the 'tick' variable and entities? Idk. All the things that are "dynamic".
|
// IDEA: SS - Add a 'World_State' variable that contains the 'tick' variable and entities? Idk. All the things that are "dynamic".
|
||||||
World :: struct {
|
World :: struct {
|
||||||
name: string,
|
name: string,
|
||||||
|
max_entities: u32,
|
||||||
|
|
||||||
component_registry: Component_Registry,
|
component_registry: Component_Registry,
|
||||||
|
|
||||||
entity_id_queue: queue.Queue(Entity_ID),
|
entity_id_queue: queue.Queue(Entity_ID),
|
||||||
@@ -46,8 +55,10 @@ World :: struct {
|
|||||||
systems: []System,
|
systems: []System,
|
||||||
}
|
}
|
||||||
|
|
||||||
create_world :: proc(name: string, max_entities: u32, components_to_register: []typeid, systems: []System) -> (^World, bool) {
|
create_world :: proc(name: string, max_entities: u32, systems: []System) -> (^World, bool) {
|
||||||
world := new(World)
|
world := new(World)
|
||||||
|
world.name = name
|
||||||
|
world.max_entities = max_entities
|
||||||
|
|
||||||
registry, err := make(Component_Registry, max_entities)
|
registry, err := make(Component_Registry, max_entities)
|
||||||
if err != .None {
|
if err != .None {
|
||||||
@@ -57,19 +68,30 @@ create_world :: proc(name: string, max_entities: u32, components_to_register: []
|
|||||||
|
|
||||||
world.component_registry = registry
|
world.component_registry = registry
|
||||||
|
|
||||||
for c in components_to_register {
|
// for c in components_to_register {
|
||||||
// log.infof("Registered component '%v'.", c)
|
// log.infof("Registering component '%v' (size: %v).", c, size_of(c))
|
||||||
|
|
||||||
if c in world.component_registry {
|
// if c in world.component_registry {
|
||||||
// log.warnf("Component '%v' already registered.", c)
|
// log.warnf("Component '%v' already registered.", c)
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
|
|
||||||
world.component_registry[c] = Component_Storage {
|
// // register_component(world, c)
|
||||||
data = make([]typeid, max_entities),
|
// // id := typeid_of(type_of(c))
|
||||||
alive = make([]bool, max_entities)
|
// // info: ^runtime.Type_Info
|
||||||
}
|
// // info = type_info_of(id)
|
||||||
}
|
|
||||||
|
// // fmt.printfln("Runtime info about component: %v", c)
|
||||||
|
|
||||||
|
// // world.component_registry[c] = Component_Storage {
|
||||||
|
// // data = make([]type_of(c), max_entities),
|
||||||
|
// // alive = make([]bool, max_entities)
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // for i in 0..<10 {
|
||||||
|
// // fmt.printfln("ADDR %v: %v", i, &world.component_registry[c].data[i])
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
{ // Set up the world's queue of available entity-ids.
|
{ // Set up the world's queue of available entity-ids.
|
||||||
assert(u64(max_entities) <= u64(max(int)))
|
assert(u64(max_entities) <= u64(max(int)))
|
||||||
@@ -97,11 +119,30 @@ create_world :: proc(name: string, max_entities: u32, components_to_register: []
|
|||||||
return world, true
|
return world, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register_component :: proc(world: ^World, $A: typeid) -> bool {
|
||||||
|
log.infof("Registering component '%v' (size: %v).", typeid_of(A), size_of(A))
|
||||||
|
if A in world.component_registry {
|
||||||
|
log.warnf("Component '%v' already registered.", typeid_of(A))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
base := make([]A, world.max_entities)
|
||||||
|
|
||||||
|
world.component_registry[A] = Component_Storage {
|
||||||
|
data = &base[0],
|
||||||
|
elem_size = size_of(A),
|
||||||
|
alive = make([]bool, world.max_entities)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
destroy_world :: proc(world: ^World) {
|
destroy_world :: proc(world: ^World) {
|
||||||
assert(world != nil)
|
assert(world != nil)
|
||||||
|
|
||||||
for c, e in &world.component_registry {
|
for c, e in &world.component_registry {
|
||||||
delete(e.data)
|
// delete(e.data)
|
||||||
|
free(e.data)
|
||||||
delete(e.alive)
|
delete(e.alive)
|
||||||
|
|
||||||
delete_key(&world.component_registry, c)
|
delete_key(&world.component_registry, c)
|
||||||
@@ -153,10 +194,11 @@ destroy_entity :: proc(entity: ^Entity, world: ^World, loc := #caller_location)
|
|||||||
queue.push_back(&world.entity_id_queue, entity.id)
|
queue.push_back(&world.entity_id_queue, entity.id)
|
||||||
|
|
||||||
// Reset the entity's components.
|
// Reset the entity's components.
|
||||||
for c, v in &world.component_registry {
|
for c, &v in &world.component_registry {
|
||||||
if v.alive[entity.id] {
|
if v.alive[entity.id] {
|
||||||
v.alive[entity.id] = false
|
v.alive[entity.id] = false
|
||||||
v.data[entity.id] = {}
|
base_addr := get_addr_of_component_for_entity_id_from_storage(&v, entity.id)
|
||||||
|
mem.set(base_addr, 0, int(v.elem_size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +207,7 @@ destroy_entity :: proc(entity: ^Entity, world: ^World, loc := #caller_location)
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
add_component :: proc(entity: Entity, world: ^World, component: $T, loc := #caller_location) -> (^T, bool) {
|
add_component :: proc(entity: Entity, world: ^World, component: $T, loc := #caller_location) -> (^T, bool) { // MAYBE: SS - Remove 'world' here? Entity could cache the world they're a part of.. but that would make entities bigger.
|
||||||
if !entity_valid(entity) {
|
if !entity_valid(entity) {
|
||||||
log.warnf("Failed to add component %v - entity is invalid. Location: %v", typeid_of(T), entity, loc)
|
log.warnf("Failed to add component %v - entity is invalid. Location: %v", typeid_of(T), entity, loc)
|
||||||
return nil, false
|
return nil, false
|
||||||
@@ -187,7 +229,8 @@ add_component :: proc(entity: Entity, world: ^World, component: $T, loc := #call
|
|||||||
log.warnf("Failed to add component %v - component does not exist in the registry. Location: %v", typeid_of(T), loc)
|
log.warnf("Failed to add component %v - component does not exist in the registry. Location: %v", typeid_of(T), loc)
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
comp := transmute(^T)(&component_storage.data[entity.id])
|
|
||||||
|
comp := transmute(^T)(get_addr_of_component_for_entity_id_from_storage(component_storage, entity.id))
|
||||||
comp^ = component
|
comp^ = component
|
||||||
|
|
||||||
component_storage.alive[entity.id] = true
|
component_storage.alive[entity.id] = true
|
||||||
@@ -225,7 +268,9 @@ get_component :: proc($T: typeid, entity: Entity, world: ^World, loc := #caller_
|
|||||||
}
|
}
|
||||||
|
|
||||||
component_storage := &world.component_registry[T]
|
component_storage := &world.component_registry[T]
|
||||||
comp := transmute(^T)(&component_storage.data[entity.id])
|
comp := transmute(^T)(get_addr_of_component_for_entity_id_from_storage(component_storage, entity.id))
|
||||||
|
// fmt.printfln("Comp is: %#v (size of elem in storage: %v, size of comp: %v)", comp, component_storage.elem_size, size_of(comp))
|
||||||
|
// fmt.printfln("Address is: %v.", )
|
||||||
return comp, true
|
return comp, true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +303,7 @@ System :: struct {
|
|||||||
|
|
||||||
create_system :: proc(
|
create_system :: proc(
|
||||||
name: string,
|
name: string,
|
||||||
state: $T,
|
state: ^$T,
|
||||||
init, dispose: proc(world: ^World, state: rawptr) -> bool,
|
init, dispose: proc(world: ^World, state: rawptr) -> bool,
|
||||||
tick: proc(world: ^World, state: rawptr),
|
tick: proc(world: ^World, state: rawptr),
|
||||||
allocator := context.allocator,
|
allocator := context.allocator,
|
||||||
@@ -266,7 +311,7 @@ create_system :: proc(
|
|||||||
{
|
{
|
||||||
return System {
|
return System {
|
||||||
name = name,
|
name = name,
|
||||||
state = new_clone(state, allocator),
|
state = state,
|
||||||
init = init, dispose = dispose,
|
init = init, dispose = dispose,
|
||||||
tick = tick,
|
tick = tick,
|
||||||
}
|
}
|
||||||
@@ -290,7 +335,7 @@ create_system :: proc(
|
|||||||
s.dispose(world, s.state)
|
s.dispose(world, s.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
free(s.state)
|
// free(s.state)
|
||||||
s.state = nil
|
s.state = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,8 +357,8 @@ tick :: proc(world: ^World) {
|
|||||||
time.stopwatch_stop(&sw)
|
time.stopwatch_stop(&sw)
|
||||||
|
|
||||||
// TODO: SS - Reintroduce this at some point. Really nice for debugging! :)
|
// TODO: SS - Reintroduce this at some point. Really nice for debugging! :)
|
||||||
// system_tick_time := time.stopwatch_duration(sw)
|
// system_tick_duration := time.stopwatch_duration(sw)
|
||||||
// fmt.printfln("System '%v' took %v ms.", s.name, time.duration_milliseconds(system_tick_time))
|
// fmt.printfln("System '%v' took %v ms.", s.name, time.duration_milliseconds(system_tick_duration))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,7 +372,7 @@ Query :: struct {
|
|||||||
|
|
||||||
Query_Types :: []typeid
|
Query_Types :: []typeid
|
||||||
|
|
||||||
create_query :: proc(world: ^World, include: []typeid, exclude: []typeid = {}) -> Query {
|
create_query :: proc(include: []typeid, exclude: []typeid = {}) -> Query {
|
||||||
query: Query
|
query: Query
|
||||||
|
|
||||||
// TODO: SS - Verify that the same component/typeid doesn't exist in both the include AND the exclude list.
|
// TODO: SS - Verify that the same component/typeid doesn't exist in both the include AND the exclude list.
|
||||||
@@ -350,7 +395,13 @@ destroy_query :: proc(query: ^Query) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entities_alive_with_component :: proc(type: typeid, world: ^World) -> u32 {
|
entities_alive_with_component :: proc(type: typeid, world: ^World) -> u32 {
|
||||||
|
assert(world != nil)
|
||||||
|
|
||||||
component_storage := &world.component_registry[type]
|
component_storage := &world.component_registry[type]
|
||||||
|
if component_storage == nil {
|
||||||
|
fmt.eprintfln("No component storage for type '%v' in world '%v'.", type, world.name)
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
assert(component_storage != nil)
|
assert(component_storage != nil)
|
||||||
|
|
||||||
// Check how many entities have this component.
|
// Check how many entities have this component.
|
||||||
@@ -386,7 +437,9 @@ query_entities :: proc(world: ^World, query: ^Query) -> []Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(component_with_fewest_entities != nil)
|
if component_with_fewest_entities == nil {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
assert(amount_of_entities_with_component > 0)
|
assert(amount_of_entities_with_component > 0)
|
||||||
|
|
||||||
// fmt.printfln("component_with_fewest_entities: %v (%v)", component_with_fewest_entities, amount_of_entities_with_component)
|
// fmt.printfln("component_with_fewest_entities: %v (%v)", component_with_fewest_entities, amount_of_entities_with_component)
|
||||||
|
|||||||
Reference in New Issue
Block a user