The following implementation seems to pass all initial requirements. Which were as follows:
- Store all components in the root class allowing for any scope with a reference to the class the ability to find or lookup active components
- One reference to all components, all others would be weakref's
- Ability to create a new component via a factory method
- Unique component id's
- Grouping of all similar components for fast lookups
The goal was a fast and modular component lookup and a way to store the components that didn't have to be passed around. Again the code works in the current scope of the project however my init seems a bit verbose and potentially unnecessary. I'm wondering if there are any obvious flaws or bugs before I move forward and begin work on other portions of the ECS
from __future__ import annotations
from typing import Dict, List, Type
class Component(object):
""" Root Component type in an ECS system """
_type_table: Dict[str:Type] = {} # Hashable ref to all Components -> {class_name:class}
_active_comp_ref: Dict[str:List[Component]] = {} # Ref to all active components
_id = 0 # Count of every object every created
def __init__(self, *args, **kwargs):
self.__instance_id = None
c_type = self.__class__
c_name = c_type.__name__
if c_name not in Component._type_table: # First time creating this type
Component._type_table[c_name] = c_type # Store type
c_type._active_comp_ref = dict() # Create comp ref on subclass
Component._active_comp_ref[c_name] = c_type._active_comp_ref # Store ourselves in ref
self.__initialize_id()
c_type._active_comp_ref[self.id] = self
@staticmethod
def get_component(type_name: str) -> Type[Component]:
""" Return a Component type by name """
return Component._type_table[type_name]
@staticmethod
def get_component_table() -> Dict[str:Dict[int:Component]]:
""" Get the table used to store all Components """
return Component._active_comp_ref
@staticmethod
def component_types() -> Dict[str:Type[Component]]:
""" Get the Component Type Table """
return Component._type_table
@staticmethod
def __next_id():
""" Increment Core ID: Should not be called externally"""
Component._id += 1
return Component._id
@property
def id(self):
""" Unique Instance ID Property """
return self.__instance_id
def __initialize_id(self):
""" Get the next ID and remove this method """
# Done like this to ensure id is set once and only once
self.__instance_id = self.__next_id()
self.__set_id = None
class Transform(Component):
""" Basic Testing class"""
def __init__(self, pos, direction, vel):
super().__init__()
self.pos = pos
self.direction = direction
self.vel = vel
class Health(Component):
""" Basic Testing class"""
def __init__(self, cur_hp, max_hp):
super().__init__()
self.cur_hp = cur_hp
self.max_hp = max_hp
def c_factory(name: str, **kwargs) -> Component:
""" Create a new Component from a name and a dict of attributes """
c = type(name, (Component, ), kwargs)
return c()
# Basic Implementation Tests
t = Transform((100, 100), (1, 0), .1)
t2 = Transform((100, 50), (-1, 1), .2)
Health(10, 100)
f = c_factory("Renderer", surface=None, size=(100, 100))
f.surface = (500, 400, 800)
del f # Should only delete pointer to this?
print(Component.get_component_table()["Renderer"][4].surface) # Obviously not how I plan to do lookups but at this moment i'm not positive on the best way!
Component.get_component("Transform")((10, 10), (0, -1), .5)
print(Component.get_component_table())