Tags & components; look up for faster iteration

Hi everyone,

there’s been some discussion of late concerning the idea of a “tag” system, which identifies objects has having certain functionality. Whilst there are advantages and disadvantages to this design paradigm, I’ve chosen to include an example for reference

Tagging example***

from collections import defaultdict



class CollectionView:
    
    def __init__(self, data):
        self.data = data
    
    def __getitem__(self, key):
        return self.data[key]
    
    def __iter__(self):
        return iter(self.data)
    
    def __len__(self):
        return len(self.data)


    def __repr__(self):
        return repr(self.data)




class TagManager:
    
    def __init__(self):
        self._tags = defaultdict(set)
    
    def associate(self, obj, tag):
        self._tags[tag].add(obj)
        
    def disassociate(self, obj, tag):
        self._tags[tag].discard(obj)
        
    def with_tag(self, tag):
        tags = self._tags.get(tag, set())
        return CollectionView(tags)
    
Components example *** Whilst I think that tags can be useful for simple queries, if you want to use them for updating the tagged objects in question, rather than using them to find the bosses in the scene, it might make more sense to utilise a system-component design:

In this example, components are defined using linked sensors which support configuration through properties.

Each component must be linked with a sensor (no true pulse), and a name following the convention of
“TAG_” followed by the name of the component, e.g “Input”, written as “TAG_Input”.

The sensor must be created on a separate object - I use empties, and that object is better left unparented from the entity object to avoid wasting calculations on transforms.

The properties set on the component object can be used from within the systems to configure the components.

To create a system, copy the example system in systems.py. Any System subclass in the systems.py file will be automatically added to the system manager).

The “required_components” attribute of the system is a set, which looks like dictionary literal notation, except it is defined in the same manner as a list - curly braces, followed by a comma delimited sequence of names. These are the component names.

Because some systems might require more than one component, when updating the entities, the components associated with the entity corresponds to a dictionary, of the component name followed by the property data. If you have only one component, you still must access it by name within the update loop.

Some of the benefits of ECS designs (avoiding cache misses) are nullified when using Python due to the inability to dictate where memory is allocated, but they can still be a useful abstraction.

Attachments

components.blend (483 KB)

Cool, I’m going to go and sleep for a bit and then read that again. :slight_smile:
Too late at night here, but I think I get what you’re talking about (I remember the thread about tags). It could be very useful.

I use a method like this to maintain a list of objects,
However I use a property, and search through scene.objects on frame 0,
And build a base list, then if a item is added or removed I manage the list with Pop and append.

Same functionality in the end or did I miss something?

That’s the basis for tagging. Here i’m using quite efficient methods to avoid any additional overhead where possible.

The components concept is quite distinct, as its own paradigm

yes with big list there a huge boost using ListWiew, rather than make a copy list.

great tip

anyway if the addiction is pretty simple, what about the remotion?
this seem a bit more convulted


class TagManager:
    
    def __init__(self):
        self._tags = defaultdict(list)
    
    def associate(self, obj, tag):
        self._tags[tag].append(obj)
        
    def with_tag(self, tag):
        tags = self._tags.get(tag, [])
        return ListView(tags)
    
    def remove_obj_tag(self, obj, tag):
        self._tags[tag].remove(obj) # potentially slow
        if not self._tags[tag]:
            self._tags.pop(tag)

but, weel , is not a operation so frequent…

PS: there are some good reason to give two arguments (obj, tag) , rather than just just one (obj) ?

agoose, you think is doable , really , in bge , this tag system(research fast of objects) or the code is so hugly that also add one thing like this is impossible ? :smiley:

alternatively also make readable the list of “actors” (you put the flag in the tab physic of UI) will be not bad at all , this should be already present somewhere (where?)
and the advantage will be that the syntax can be exactly the same as other CList .(just much more short than scene.objects…more or less 10x more short)

ie:
for i in scene.actors:
or
for i in scene.objects.actors:

It makes sense to leave this in Python -> Maintaining the data isn’t that slow, and you’d need to iterate over the result in Python anyway.

If you wanted to speed this up further, and didn’t have any requirement for the order of objects to be preserved, using a set instead of a list would be a lot faster for removing objects as the size of the set grows. I’ve updated the example to include this.

Personally speaking, I’d probably make use of an ECS system more frequently than a “tag” ssytem, but I can’t imagine it wouldn’t have some practical uses.