Search Unity

[Released] BTagged - GUIDs per GameObject

Discussion in 'Assets and Asset Store' started by Timboc, Sep 2, 2020.

  1. Timboc

    Timboc

    Joined:
    Jun 22, 2015
    Posts:
    238
    BTagged - easily query GameObjects & Entities via code.
    Having a unique global ID associated with a GameObject makes it easy to find what you're looking for without a mess of inter-dependencies.

    Asset Store / Docs / Website / Discord



    Update 26/10/2020:
    - Version 1.2 is up on the store with support for 2019.4+
    - Huge improvements to API for finding tagged objects
    - Faster project searching
    - Many quality of life improvements

    Conveniently create & manage Tags & Groups. Create a new tag and select it with just a couple of clicks.
    1.gif

    See everywhere a Tag's used:
    2.gif

    Works fast & gc-free with DOTS/ECS
    3.png

    Extensibility
    A lot of the power of BTagged is packed into the PropertyDrawer making it easy to use for managing your own ScriptableObjects with all the built-in management & reference finding.

    Any issues or features you'd like to see added? Let me know below. Cheers!
     
    Last edited: Oct 26, 2020
    NikoBay likes this.
  2. Timboc

    Timboc

    Joined:
    Jun 22, 2015
    Posts:
    238
    Tech-post - in the name of Performance!
    So as a follow-up to this thread (https://forum.unity.com/threads/share-your-ui-toolkit-projects.980061/#post-6507240), I thought I'd go into a bit of technical detail wrt how BTagged works and some ballpark performance figures for those interested.
    Note: All comments are wrt BTagged v1.3 which at the time of writing has yet to be fully tested & released but is coming soon.

    GameObjects
    First, a word on GameObject.FindObjectByTag() - it's fast!
    A naïve implementation would see a search through the hierarchy comparing tags - this would be slow and grow horribly with project complexity. Clearly Unity are smarter than that!

    I believe* much like BTagged, Unity keep a hash table of GameObjects by string.
    Unlike this very flat structure that can be maintained in C++, BTagged allows for complex, iterative searches in C# with mucho power and expressivity.

    Nevertheless, I think it's certainly reasonable that in the most simple case (equivalent to FindObjectByTag()), BTagged should be at least as fast.

    After a recent heavy round of optimisation I finally got numbers I'm happy with.
    On my desktop machine:
    - 1000 calls to
    FindObjectByTag("aTag")
    ~0.45ms
    - 1000 calls to
    BTagged.Find(aTag).GetFirstGameObject()
    ** ~0.15ms

    This is in C#, beating out a (presumably) flat hash table in C++. This is likely in part due to the comparison of hashes (uint4s) that back every BTag instead of strings.

    Allocations are also the enemy to this kind of performance so beyond maintaining the hash table and construction of the queries (which can be done separately to execution e.g. in Awake) there is very little-to-no gc generated at runtime.

    Of course, the more complex the query, the slower the query will take to execute and queries that search through children are comparatively expensive. If you do find yourself doing 1000's of queries every frame I would recommend caching the results where possible or considering a change of approach.

    TLDR: BTagged should be fast enough that you never have to think about it.
    Caching a found tagged object is always good practice.


    DOTS / Entities
    Unlike GameObjects, there is no Hash table to maintain and upon conversion, a tagged GameObject becomes an entity with a `TagICD` - an IComponentData with a reference to a Blob which is a NativeArray of hashes. Phew!
    This is great for several reasons:
    1) Tagged Entities can be loaded straight into RAM from disc with no deserialization
    2) If the entity is e.g. a tagged Prefab, every instantiated version of that prefab will have a reference to the same Blob - saving on memory
    3) Generally hierarchies tend to be flatter with ECS - this combined with a Burst search can be fast.
    At the moment, the equivalent of above (i.e. 1000 x
    BTagged.FindEntities(aTag).GetFirstEntity()
    ) takes around 0.19ms for entities. Again, 1000 queries in a frame taking less than 0.2ms should hopefully rarely be an issue, yet I believe this can be faster. I think the current bottleneck is partly due to the overhead of SystemBase itself and my understanding is Unity are currently working on unmanaged Systems to make them burstable. It is my hope this will improve these ecs numbers further.

    So what if my complex queries are too expensive?
    In my experience it's very rare that a very expressive query would be performed e.g. hundreds of times so I see this being an unlikely occurance. Having said that, the great news is that should a query (or set of queries) ever become so complex that they are causing you performance issues, there is always the option of giving your target objects a more specific tag. Meaning no matter where in the hierarchy your GameObject is, you can get the fastest performance by simply calling BTagged.Find(myUniqueTag).

    There is still much testing to be done and one cost of these optimisations has been harder to maintain code. Once v1.3 drops, do let me know if you hit any issues and I'll do my best to resolve ASAP.

    Many thanks for your continued support of this labour of love!

    *Without access to Unity source this is educated supposition
    **Caching the query
    var query = BTagged.Find(aTag);
    then executing it by calling
    query.GetFirstGameObject();

    ***It's now possible to reuse a query for entities by making use of
    query.AutoDispose = false;
    then manually calling
    query.Dispose();
    when you're done.
     
    Last edited: Nov 11, 2020
    Lukas_Kastern likes this.
  3. RealityDotStop

    RealityDotStop

    Joined:
    Mar 4, 2013
    Posts:
    42
    My understanding is tags are using a uint already, which is why deleted tags are not removed immediately from the TagManager, but it is interesting that you're able to get significant speed boosts if that is true.

    I'm just beginning to dig into BTagged, and I'm liking what I'm finding. I do have a request to toss into your pile of things to think about, but obviously only if you think it is a good fit.

    For reference, my preferred workflow is using a hybrid GameObject-based ECS system (not the official Unity one) embodied by https://github.com/RealityStop/gocs . You can think of this as creating interfaces, dropping an implementation of that interface on your gameobjects, and then writing systems that operate on all the instances of those interfaces in the scene, which is similar to the workflow BTagged brings to the table. But all of this still operates in the old Mono world, and isn't trying to access the memory layout improvements. It's just structural.

    For me, GoCS has been a pleasant experience, and provides a lot of structure and maintainability. It isn't hard ECS, and allows me to hybridize, using traditional OOP Monos when I need them as well as third party assets, but nicely preventing God scripts and well, it works with my brain better.

    I'm looking to teach some new users using a Visual Scripting solution (kids, I don't have time to teach them C#), which makes my go to GoCS a no-go, as it is C# interface-based, and relies on inheritance or manually-injected calls in Mono events, but I'd still prefer to use this mindset as I still feel it is a better long term management solution and sets the user on the path to success.

    So I'm eyeing BTagged as a solution that should allow me to have relatively simple data GameObjects with game systems operating over them. But one thing it appears to lack is "live" queries for multiple tags. I can see you have extension methods for IEnumerable<Tag> (thanks) for "find" style operations, but the live queries appear to be only supported for single tags (that being the OnGameObjectEnabled et al.)

    I can get around this, by observing the tags independently, checking if they have all the requisite tags and components, then adding them to a hashset, which prevents double additions as each tag reports in and which handles delay-added tags (not sure why that would happen... but it's nice to be safe I guess), so it isn't without workaround. But it is a possible workflow you might consider.

    Thanks!


    ***Edit*** to anyone who visits - this discussion was resumed on the discord, and the author immediately resolved the issue in a few hours.
     
    Last edited: Dec 11, 2020
    firstuser and Timboc like this.
  4. wechat_os_Qy05LqjxeDAXECbmkeN1DGnwo

    wechat_os_Qy05LqjxeDAXECbmkeN1DGnwo

    Joined:
    Apr 11, 2023
    Posts:
    2
    Its so slowed in 2021.3.31f, are u still work on it?
     
  5. wechat_os_Qy05LqjxeDAXECbmkeN1DGnwo

    wechat_os_Qy05LqjxeDAXECbmkeN1DGnwo

    Joined:
    Apr 11, 2023
    Posts:
    2
    it can not serch reference in the project