Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question How to detect how many colliders colliding inside of a trigger in a child object?

Discussion in 'Scripting' started by LazyGhost15, Jul 29, 2023.

  1. LazyGhost15

    LazyGhost15

    Joined:
    Feb 12, 2022
    Posts:
    88
    I need to create a cube that can detect other nearby cubes. To achieve this, I created multiple empty objects and added colliders that were set to trigger. How can I verify if there is a game object within each of the triggered areas separately?
     
  2. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    4,393
    OverlapBox?
    I am not exactly sure what the end goal is, so can't give a definitive answer
     
  3. LazyGhost15

    LazyGhost15

    Joined:
    Feb 12, 2022
    Posts:
    88
    I tried to use OverlapSphere, but it did not function correctly.

    My goal is for the cube to detect the directions in which it is surrounded by other cubes.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    You can get the heading (bearing) to another object using Mathf.Atan2() and the delta in position.

    https://forum.unity.com/threads/yaxis-rotation-parent-object.796419/#post-5299722

    As for the aggregate "fully surrounded" part, that will be handled by some kind of count or checklist of directions.

    For instance, in a grid it is easy: if you are at x,y then you just add offsets going around all 4 directions (or 8 directions) to your x,y and ask if there is something present.
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,507
    With due respect, I'm sure it functioned as its supposed to but you used it wrong. I'd say don't give up on using queries that work perfectly fine, just try again and check you've passed the correct arguments etc. We see this all the time on the forums; devs asking how to do something because they tried and failed at the method that is directly designed to achieve what they want simply because of a simple misunderstanding, wrong arguments, interpreting results incorrectly etc :) Don't give up on it!
     
    Sluggy, bugfinders and orionsyndrome like this.
  6. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,589
    Maybe that's just me, but could you describe your actual usecase? The correct response will look wildly different depending on what you need this for, and the scale of this operation. If you only have one object that does this check, you get away with pretty much anything. If you attempt to, for example, build a grid (world, vehicle builder, ..) out of this mechanic, this may be the completely wrong approach. A bit of context would be appreciated.
     
  7. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Not sure if one of my saved snippets would help, but:

    Code (CSharp):
    1. // nonAlloc for no garbage collection
    2. void ExplosionDamage(Vector3 center, float radius)
    3.     {
    4.         int maxColliders = 100; // suggested to be smaller
    5.         Collider[] hitColliders = new Collider[maxColliders];
    6.         int numColliders = Physics.OverlapSphereNonAlloc(center, radius, hitColliders);
    7.         for (int i = 0; i < numColliders; i++)
    8.         {
    9.             int index = GetEnemyIndex(hitColliders[i]);
    10.             if (index == -1) continue; // skip iteration if true
    11.             enemies[index].TakeDamage(15);
    12.         }
    13.     }
    14.  
    15.     int GetEnemyIndex(Collider collision)
    16.     {
    17.         for (int i = 0; i < enemies.Count; i++)
    18.         {
    19.             if (collision.gameObject == enemies[i].gameObject)
    20.             {
    21.                 return i;
    22.             }
    23.         }
    24.         return -1;
    25.     }
    As I found OverlapSphere is more performant(faster) than OverlapBox, and in some cases AABB is faster than sphere. And in my snippet I'm using nonAlloc, which you probably don't need for your project(overkill).

    But OverlapSphere returns colliders, so can't speak much on if you're using triggers, and how it would be different in correlation. So once you get the Colliders of your other cubes, then all you need to do is get the transform or gameObject from that. Once you now have position and rotation from returned array(or list) of your other cubes, it's just a bit of math or coding from there.

    I may have shot way left field on your question, but that's what you are referring, not getting the other cubes as a result of being nearby, right?
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    ... and then you go on to use a linear search to match collider Gameobjects in order to look up the EnemyController on every item?! :)

    A more flat-responding approach would be to do a GetComponent<T>() for the EnemyController (or whatever you call it), and if it exists, call TakeDamage() on that.

    Replaces loop on line 7 above:

    Code (csharp):
    1. foreach ( Collider c in hitColliders)
    2. {
    3.   var enemy = c.GetComponent<EnemyController>();
    4.   if (enemy)
    5.   {
    6.     enemy.TakeDamage(15);
    7.   }
    8. }
     
    orionsyndrome likes this.
  9. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Yes, as I found it to be way more performant(less ms) than using GetComponent() during runtime. I wouldn't have saved that snippet if it wasn't the best choice, after running Profiler and System.Diagnostics.Stopwatch in all my testing, versus different methods to achieve the same result.

    Plus "enemy" described in my code is the parent class of all enemies, so a general call to any "Enemy.cs" would also hit "grunt/captain/warlord/turret/base" etc... Had I chose GetComponent() for readability, there would be many GetComponent<Grunt>/<Captain>/<Warlord>/etc... oof

    Edit: wait, I think you can call GetComponent<Enemy>() and it still work for the child classes.. It wasn't that, but there was another reason to why that wouldn't work.. can't remember now..
     
    Last edited: Jul 30, 2023
  10. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,589
    How did you test that? With how many enemies did you test that?

    It can be faster in some cases, especially small scenarios. But it should scale worse. I would argue that you dont need any kind of optimization in a scenario in which the amount of enemies if sufficiently small. You never want to trade readability for micro optimizations. I would further argue that GetComponent should outscale a m*O(n) check over all enemies at scale.
     
    Kurt-Dekker and orionsyndrome like this.
  11. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Why does everyone keep saying that it won't scale? Depending on what game I'm working on, it could be as little as 400 instances, and at most 10,000. It scales very well, unlike GetComponent() during runtime does, at large scale.

    Now I have found that it's because I have static cached lists of the classes/instances, and are iterating through them, that seems to be the performance part of it, and to why GetComponent() isn't performant.

    I am currently making a test scene, to test just this(I will make new post on it), and to get all the values proven with benchmarked ticks, and eventually profiler milliseconds. But I can't get GetComponents function to work:

    Code (CSharp):
    1. void ExplosionDamageComponent(Vector3 center, float radius)
    2.     {
    3.         int maxColliders = 100; // suggested to be smaller
    4.         Collider[] hitColliders = new Collider[maxColliders];
    5.         int numColliders = Physics.OverlapSphereNonAlloc(center, radius, hitColliders);
    6.         for (int i = 0; i < numColliders; i++)
    7.         {
    8.             //hitColliders[i].gameObject.GetComponent<Playables>().ModifyHealth(25); // error
    9.             GameObject obj = hitColliders[i].gameObject;
    10.             Playables objScript = obj.GetComponent<Playables>(); // error
    11.             objScript.ModifyHealth(25);
    12.         }
    13.     }
    I will assume, since hierarchy is [Playables > Enemy > (objA, objB)], that I can't call GetComponent on the parent class, to handle any children. So I'd literally have to write out each child's particular class with each GetComponent call. I'm already remembering why I made the structure I have now.. LOL
     
  12. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Ok, I tested it rigorously, and am shocked by the results @Yoreki @Kurt-Dekker ...

    At 100, and even jumping to 900 colliders, GetComponent() out performs my old method by over 10x factor. So I will admit where I'm wrong, but mostly am curious to why it's wrong now. That method was done 3 years ago with Unity 2019, and GetComponent() was absolutely dreadful with benchmarking it's milliseconds.

    And even to a point where I cached components like transform, as : https://docs.unity3d.com/2019.3/Doc...imizationPracticalScriptingOptimizations.html , states with :
    "Little known fact: all of the component accessors in MonoBehaviour, things like transform, renderer, and audio, are equivalent to their GetComponent(Transform) counterparts, and they are actually a bit slow."

    And it was most certainly the case. I have to now go back through all of my projects and test the comparisons. It will be a ton more work, as not inclusive of parents means I'll need a ton of "if" statements, and pages I got down to only 2k lines of code will now be 20k lines of code.. oof!

    But again, sorry for any misinformation. Code keeps changing, and so will I. Cheers!
     
    Yoreki likes this.
  13. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,589
    I'm reasonably sure you can just GetComponent<BaseClass> and it should work, depending on how you handled your inheritances. You usually want to keep it modular and have something like a HealthComponent on stuff with health, so when an explosion needs to damage stuff, you simply get the HealthComponent, and it doesnt matter whether it's a player or an enemy.

    Since, after clarifying the performance discussion, i dont think this benefits OPs question anymore, consider opening a new thread if maybe you need some other feedback on how to handle certain setups. Happy coding!
     
  14. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    840
    Very likely you were profiling in debug mode back then (the only mode the editor operated in at the time). These days the editor is by default NOT in debug mode until you switch it. This makes a huge difference as some functions fundamentally work differently in debug mode. Can you guess which famously infamous function that might be in this case? GetComponent. It allocates memory that is used within the editor to provide extra information which can of course be quite costly and often contributes to the semi-myth that you should never use it because it's slow. The only way to truly profile accurately however is to make a full build and profile that.
     
    Yoreki likes this.
  15. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Now that you mention it, I do remember Garbage Collection also being a thing. As my method handled both, no GC, and was faster as far as ticks/ms. I'll have to re-do that test and benchmark after the garbage collection, as that stopwatch I used, stopped right on the same frame as the function completed.

    Very nice heads up, thank you! I was wondering why GetComponent() was becoming infamous, and assumed it ran through the list of all Unity.gameObjects, then upon finding the correct one, then ran again through it's components, whether a string parse or not, then returned said component.

    I'm thinking now, Unity has these components as reference within the gameObject class. I just hope to find a better way, if in fact GetComponent() still has garbage to collect. :)
     
  16. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    840
    As far as I can tell it certainly does not run through every object in the scene as I've had scenes with nearly a hundred thousand objects and the search time remains constant for the same object. However, it probably *does* iterate through a list of components attached to the GameObject, checking the types along the way and more than likely those pointers in that list are scattered all through memory causing all kinds of cache-thrashing. If you are using the variant that searches children then things can potentially grow exponentially there depending on how complex your object hierarchies are. The other variant that searches backwards through parents could also be bad in certain situations.

    Also just to clarify, GetComponent only generates garbage in the editor. This is not present in the final build. And perhaps also when not in debug mode, though I've never bothered to verify because I don't do profiles in anything but a release build.

    My takeaway has always been this. If you really can't help calling GetComponent without an extraordinary amount of effort then just call GetComponent and be done with it. If it is simple to avoid then perhaps caching the component if it is accessed very frequently is worthwhile. I say 'perhaps' because when you start to scale things up (like to thousands or tens of thousands of instances) that extra 8 bytes of memory pointing to a component can have it's own kind of impact. In those cases if you really see a problem then profile both ways in a release and verify which way is performing better. While I have no doubt that in some cases you can improve performance by avoiding it, there are times where it is just not practical and I've personally used it in cases where it gets called a few thousand times per frame and it still never even registered in the profiler when I was looking for optimizations.

    For reference, in this video at 17:58 I'm spawning 1024 (IIRC) GameObjects for the bullets from a pool. Each time it comes from the pool I have to call GetComponent on each bullet. I also call GetComponent on the objects they strike and then again on the particles they spawn after striking because those are also in a pool. It's never once caused me any significant issue.

     
    wideeyenow_unity likes this.
  17. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,507
    I'm a bit confused over this misknowledge here too. Calling GetComponent on a GameObject would have no reason to iterate all GameObjects; it's a method on that specific instance. It turns the type into the internal type-id and does a linear search in an array that each GameObject has so it's very quick. This internal type is set-up such that it's also super quick to detect derived types.

    It's along these lines:
    Code (CSharp):
    1. Unity::Component* GameObject::QueryComponentByType(const Unity::Type* type) const
    2. {
    3.     // Find a component with the requested ID
    4.     Container::const_iterator i;
    5.     Container::const_iterator end = m_Component.end();
    6.     for (i = m_Component.begin(); i != end; ++i)
    7.     {
    8.         if (type->IsBaseOf(i->GetTypeIndex()))
    9.             return i->GetComponentPtr();
    10.     }
    11.  
    12.     return NULL;
    13. }
    It also shouldn't allocate anything. What does happen in the Editor, specificially the marshalling layer is that when you access any Unity object the very first time, it'll create a wrapper around it which needs allocating but it'll only happen once. If you've already referenced that object, it'll not create another one. This is called out for lots of random Unity functions but it's nothing to do with the call-site but the wrapper. I see this reported to this day for collision callbacks or when a dev accesses a collider object in a physics query and report a GC bug but haven't checked if it happens on every call (it doesn't) or if it happens in builds (it doesn't). Maybe there's a nicer way to label that in the profiler, not sure, not my area.

    This sort of thing (only allocated if it doesn't exist):
    Code (CSharp):
    1. return Scripting::ScriptingWrapperFor(GetGameObjectPtr());
    Still seen today in lots of projects are devs using GetComponent<T> several times in a method rather than caching it. Even using the "transform" property uses a fast-path of GetComponent but it's faster because Transform (and RectTransform) is always enforced to be the first component. Still it doesn't stop devs from doing horror code like this:
    Code (CSharp):
    1. var vec = new Vector3(transform.position.x + 1, transform.position.y, transform.position.z);
    DRY and all that. :)
     
    Sluggy and Yoreki like this.
  18. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    729

    Genuine question on the subject of micro optimisation.

    I thought that the garbage created by Physics.OverlapSphere was in the creation of a new array of results. If you create a new array every time you call Physics.OverlapSphereNonAlloc, doesn't this essentially amount to the same thing?
     
  19. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,507
    Yes, if anything returns a new array, not limited to physics, it clearly has to be allocated so unless you hold onto that as a reference somewhere, it'll be left to the GC. This is indeed why that "non-alloc" exists so you can provide (and reuse) your own. Allocation is still there but you reuse it.

    Recycle not Replace! :)

    The above code doesn't reuse the array so yes, it's no better than using the "All" suffix!!
     
  20. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    729
    Thanks for confirming!
     
  21. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    True, I was just assuming the gameObject class had no cached variables within it, so getting any of it's components required a search like what I'm used to. Which would be nice if it did cache it's variables(user scripts) wink wink :D

    And I wasn't sure on if it did allocate or not, I just remember GetComponent being dreadful when testing in the Editor, years ago. So good to know.

    But you must heartedly agree,
    Transform trans = transform;
    , meaning caching the component within said class, is faster than using "transform". right? To the best of my knowledge, a cached value is the fastest and cleanest way to go.

    And to the nonAlloc, that was me playing with benchmarks, I know any time you use a temp variable it eventually has to be deleted. So sorry for any misunderstandings with that one, my bad.
     
    MelvMay and flashframe like this.
  22. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,507
    Caching anything locally (if the compiler doesn't do the same) is alway faster yes, but also, faster has to be weighed against easier i.e. code quality, maintainability, consistency etc. I think where possible, grabbing a reference to a component that's always going to be there makes sense and can be easier.

    Specifically, anything that is allocated on the heap rather than the stack.

    Absolutely no need for an apology of any kind; it's a black box after all! :)
     
    wideeyenow_unity likes this.